Node.js/TypeORM

[TypeORM] 복합키 (다중컬럼 PK) 지정하기

후뿡이 2024. 5. 8. 01:39

🐳 문제

계층형 데이터 구조를 저장하기 위해 Custom Closure Table을 구현하는데

PK값이 없다는 아래와 같은 에러가 발생했다.

MissingPrimaryColumnError

MissingPrimaryColumnError: Entity "CategoryClosure" does not have a primary column. Primary column is required to have in all your entities. Use @PrimaryColumn decorator to add a primary column to your entity.

 

 

📋 Entity 코드

작성한 Entity 코드는 아래와 같다.

@Entity()
@Unique(['ancestorId', 'descendantId'])
export class CategoryClosure {
  @Column({ comment: '조상 카테고리 PK', type: 'integer' })
  @ApiProperty({ description: '조상 카테고리 PK', type: 'number' })
  ancestorId: number;

  @Column({ comment: '자손 카테고리 PK', type: 'integer' })
  @ApiProperty({ description: '자손 카테고리 PK', type: 'number' })
  descendantId: number;

  @Column({ comment: '카테고리 뎁스', type: 'integer', default: 0 })
  @ApiProperty({ description: '카테고리 뎁스', type: 'number', example: 0 })
  depth: number;

  @ManyToOne(() => Category)
  @JoinColumn({ name: 'ancestorId' })
  ancestor: Category;

  @ManyToOne(() => Category)
  @JoinColumn({ name: 'descendantId' })
  descendant: Category;
}

 

Primary Column으로 지정한 Column은 없지만 Unique 키를 지정했기 때문에 괜찮을 것이라고 생각했다.

하지만 처음에 봤던 것과 같은 MissingPrimaryColumnError 에러가 발생했다.

 

🐳 해결방안

Unique 키 값을 지정해주는 것이 아니라 PrimaryColumn을 지정해 주어야 한다.

그리고 이때 PrimaryColumn 데코레이터를 여러개 사용하는 경우 복수 Column을 묶어서 하나의 PK로 사용한다.

 

코드를 살펴보자

📋 변경된 Entity 코드

@Entity()
// Unique 키 값이 아닌 PrimaryColumn 값을 지정해 주어야 하므로 삭제한다.
// @Unique(['ancestorId', 'descendantId'])
export class CategoryClosure {
  // 기존에 Column 데코레이터 -> PrimaryColumn 으로 수정
  @PrimaryColumn({ comment: '조상 카테고리 PK', type: 'integer' })
  @ApiProperty({ description: '조상 카테고리 PK', type: 'number' })
  ancestorId: number;

  // 기존에 Column 데코레이터 -> PrimaryColumn 으로 수정
  @PrimaryColumn({ comment: '자손 카테고리 PK', type: 'integer' })
  @ApiProperty({ description: '자손 카테고리 PK', type: 'number' })
  descendantId: number;

  @Column({ comment: '카테고리 뎁스', type: 'integer', default: 0 })
  @ApiProperty({ description: '카테고리 뎁스', type: 'number', example: 0 })
  depth: number;

  @ManyToOne(() => Category)
  @JoinColumn({ name: 'ancestorId' })
  ancestor: Category;

  @ManyToOne(() => Category)
  @JoinColumn({ name: 'descendantId' })
  descendant: Category;
}

 

 

📋 결과 확인

DBeaver를 통해 확인한 CategoryClosure Table의 PK

 

우리가 원하는 대로 ancestorId + descendantId를 PK 값으로 사용하고 있는 것을 확인할 수 있다 !!

 

문제 해결 !!!