Node.js 개발자의 코-프링 적응기(2) | 인프런 워밍업 클럽 2기 - 백엔드

Node.js 개발자의 코-프링 적응기(2) | 인프런 워밍업 클럽 2기 - 백엔드

인프런 워밍업 클럽 2기

입문자를 위한 Spring Boot with Kotlin(https://inf.run/bXQQQ) 강의를 듣고 작성하였습니다.

 

h2

자바로 구현된 인 메모리 DB 입니다. 애플리케이션이 종료되면 모든 메모리가 삭제됩니다.(휘발성) 프로젝트 내부에 임베드 해서 브라우저를 통해 매니징을 할 수 있다는 점이 특이했습니다.

 

겪은 오류 - user 테이블이 생기지 않음

예전에 회사에서 user 테이블에 대해 데이터베이스를 명시하지 않아 오류가 발생한 적이 있었는데, 비슷한 상황인거 같아, 테이블 이름을 명시하였습니다.

https://velog.io/@readnthink/DataJpaTest사용시-user-table-예약어-에러

image


엔티티 정의 어노테이션

@Table 와 @Entity

테이블은 물리적인 정보, 엔티티는 논리적인 정보를 정의하는 걸까? 싶었는데, 아니었던 것 같습니다. 아예 목적이 좀 다른 느낌..?

@Entity is useful with model classes to denote that this is the entity or table
@Table is used to provide any specific name to your table if you want to provide any different name

https://stackoverflow.com/questions/18732646/name-attribute-in-entity-and-table

https://www.inflearn.com/community/questions/75556

 imageimage

엔티티랑 테이블 어노테이션 내부에 들어가도 자세하게 테이블 옵션을 정의한다기보다는 느낌보다는 정말 bean임을 명시하기 위한 식별자라고 생각이 들었습니다.

 

// Sequelize entity 정의 예시
@Table({
  tableName: 'charge',
  timestamps: true,
  paranoid: true,
  createdAt: 'created_at',
  deletedAt: 'deleted_at',
})
export class ChargeEntity
    extends Model<ChargeAttributes, ChargeCreationAttributes>
    implements ChargeAttributes
{..}

/* 
* node.js 의 Sequelize 라는 ORM에서는 @Table 에 이렇게 다양한 옵션이 존재해서 이런 차이가 있구나~~ 했었습니다.
* 개인적으로는 이렇게 테이블에 옵션 때려박아두는 것 보다는 어노테이션(=데코레이터)을 여러개 사용하는 것이 낫다고 생각했습니다
*/

 

 

@Id 와 @GeneratedValue

https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/id
https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/generatedvalue

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // mysql auto_increment
@Column(name = "report_id") /* column mapping */
var id: Long? = null /* spring에서 null로 보내면, DB에서 auto_increment */

둘 다 엔티티 정의에서 기본키를 정의할 때 사용합니다. @Id는 말 그대로 기본키라는 것을 식별하기 위해 사용합니다. 함께 사용하는 GeneratedValue는, 기본키에 대한 생성 전략을 정의합니다. (전략 타입은 아래 표)

imagehttps://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/generationtype

이번 수업에서는 db 에 기본키 생성을 위임하는 GenerationType.IDENTITY 를 사용하였습니다.

 

// Sequelize PK 정의 예시
@Column({
  field: 'charge_id',
  primaryKey: true,
  autoIncrement: true,
  type: DataType.INTEGER,
})
id: number;

/* 
* 여긴 없지만 @PrimaryKey 라는 데코레이터가 존재합니다.  @Id와 같은 역할을 할 듯,,
* autoIncrement: true, 외에 다른 옵션이 없었는데, @GeneratedValue 는 컨트롤할수 있는 범위가 넓은 것 같아 좋았습니다. 
*/

 

@Column

https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/column

@Column(name = "phone", nullable = false) /* column mapping */
var phone: String = phone

name 은 말 그대로 물리 컬럼명을 이어주는 역할을 합니다. 사실 모든 경우에 대해, @Column을 해줘야 하는 줄 알았는데요, (DB에서 스네이크케이스, spring에서는 카멜케이스 )

강의에서 "알아서 바꿔준다" 하고 슥 넘어간 거 같아 궁금해서 찾아보았습니다.

 

By default, Spring Boot configures the physical naming strategy with CamelCaseToUnderscoresNamingStrategy
https://docs.spring.io/spring-boot/how-to/data-access.html

이렇게 스프링 부트에 내장되어 있는 전략에 영향을 받는 것으로? 우선 이해를 해 보았습니다.

스프링이 참 딥해서,, 문서같은걸 뒤적거려도 확신이 잘 안서네요 ;ㅁ;

 

nullable 은 default 가 true 이기 때문에 필요한 경우에만 명시해주면 됩니다!

맨 위에 링크한 문서에, 각종 옵션들에 대한 default 가 나와있으니 개발할때 참고하면 좋을 것 같습니다.

@Enumerated

https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/enumerated

@Enumerated(value = EnumType.STRING)
var category: ReportType = ReportType.valueOf(category)

말 그대로, enum 타입이어야 함을 명시합니다. (옵션은 아래 표)

 

imagehttps://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/enumtype

연관관계

@ManyToMany

https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/manytomany

다대다(n-m) 관계일 때 사용합니다.

n-m 관계의 경우 아래 사진처럼 1-n / n-1 이렇게 분리하는게 약간 >국룰< 입니다.

image

문서에서도 @Jointable 을 사용해 중간 테이블을 지정하라고 되어 있습니다.

https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/jointable

many-to-many는 정말 은근,, 사용할 일이 없었던 전적이 있어서 슥 넘어가겠습니다.

 

@ManyToOne @OneToMany

https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/onetomany
https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/manytoone

@OneToMany(
    targetEntity = Post::class,
    fetch = FetchType.LAZY,
    cascade = [CascadeType.ALL]
)
@JoinColumn(name = "user_id")
var post: MutableList<Post> = mutableListOf()

일대다 관계를 명시하는 어노테이션입니다. 관계를 맺고 있는 양쪽 테이블에 항상 모두 쓸 필요는 없고, 데이터가 필요한 부분에만 사용합니다.
이런 어노테이션의 경우 @OneToMany 앞(One)에 있는게 나고 타겟이 뒤(Many) 에 합니다.
위 같은 코드가 User Entity 클래스 내부에 작성된 내용이라면, User(1) - UserTime(N) 입니다. 개인적으로 ORM의 관계는 요걸 헷갈리지 않는거부터 시작이라고 생각합니다..

fetch 는 엔티티에서 항상 연관된 테이블의 정보를 가져올지 아닐지 선택합니다. (N+1 관련해서는 나중에 정리하겠습니다)

cascade는 참조 무결성을 위한 키워드입니다. CascadeType.ALL 은 아래 표의 나머지들을 모두 적용하겠다는 뜻입니다.

The value cascade=ALL is equivalent to cascade={PERSIST, MERGE, REMOVE, REFRESH, DETACH}.

imagehttps://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/cascadetype

 

// Sequelize 관계 정의 예시 
// user 테이블
@HasMany(() => PostEntity)
posts: PostEntity[];

// post 테이블
@BelongsTo(() => UserEntity)
user: UserEntity;

 

@OneToOne

https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/onetoone

@OneToOne(
    targetEntity = UserTime::class,
    fetch = FetchType.LAZY,
    cascade = [CascadeType.ALL]
)
@JoinColumn(name = "user_id")
lateinit var timeInfo: UserTime

일대일 관계를 명시할 때 사용합니다.
user와 user가 사용한 시간인 userTime이 1-1 관계를 맺고 있는 경우의 예시입니다.

 

// Sequelize 관계 정의 예시 
// user 테이블
@HasOne(() => UserTimeEntity)
userTime: UserTime;

// userTime 테이블
@BelongsTo(() => UserEntity)
user: UserEntity;

 

@JoinColumn

https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/joincolumn

조인의 기준? 되는 컬럼을 지정합니다.  


프로젝트

  • 데이터베이스 관련해서 정의를 추가하였습니다. 근데 이번에 블로그 쓰면서 잘못 작성하고 그런걸 좀 찾아서 다시 해야할거같습니다. ....

     

댓글을 작성해보세요.

채널톡 아이콘