NestJS에서는
nest g resource <이름>
명령어를 사용하는 것이 가장 표준적이고 효율적인 방법이다.
이 명령어는 users 같은 기능(리소스)에 필요한 모든 구성 요소의 뼈대를 한 번에 자동으로 생성해준다.
module: 기능의 중심 허브 (users.module.ts)controller: API 엔드포인트 (users.controller.ts)service: 비즈니스 로직 (users.service.ts)entity: DB 테이블 매핑 (entities/user.entity.ts)DTO: 데이터 전송 규격 (dto/create-user.dto.ts)이 명령어로 뼈대를 생성한 뒤, 비어있는 entity 파일의 내용을 DDL(SQL) 설계도에 맞게 수동으로 채워 넣으면 된다.
엔티티 파일은 DDL의 테이블 정의를 TypeORM 데코레이터로 1:1 변환하는 작업이다.
@Entity('테이블명'): 클래스와 DB 테이블을 매핑한다.@PrimaryGeneratedColumn(): AUTO_INCREMENT가 설정된 기본 키(PK) 컬럼에 사용한다.@Column({...}): 일반 데이터 컬럼에 사용한다.@Column({ type: 'varchar', length: 5, nullable: false })@CreateDateColumn() / @UpdateDateColumn(): CREATED_AT, UPDATED_AT처럼 자동으로 시간을 기록하는 컬럼에 사용한다.DDL의 외래 키(FK) 관계를 TypeORM 엔티티 간의 객체 관계로 설정한다.

@OneToMany(() => Review, (review) => review.user)
reviews: Review[];
@관계데코레이션(() => 관계의 대상 엔티티, Review 엔티티의 인스턴스를 의미하는 매개변수 => 저 Review 엔티티 클래스에 가보면, user라는 속성이 있을 거야. 그 user 속성이 바로 이 관계의 반대편이야. 라는 뜻)
@ManyToOne(() => User, (user) => user.reviews)
@JoinColumn({ name: 'USER_ID' }) //실제 DB의 FK 컬럼명
user: User;
LikedList와 Reserved는 단순 연결 테이블이 아니라 STATUS, CHECKIN 등 자체 데이터를 가진 중요한 엔티티이다.
따라서 이 테이블들도 nest g resource 명령어로 독립적인 리소스로 생성해야 한다.
Reserved, LikedList처럼 AUTO_INCREMENT ID가 없고, 두 개 이상의 컬럼을 조합해 기본 키로 삼는 테이블의 경우, @PrimaryGeneratedColumn 대신 @PrimaryColumn 데코레이터를 해당 컬럼들에 모두 붙여준다.
//reserved.entity.ts
@Entity('Reserved')
export class Reserved {
@PrimaryColumn()
USER_ID: number;
@PrimaryColumn()
HOTEL_ID: number;
...
@ManyToOne(() => User, user => user.reserveds)
@JoinColumn({ name: 'USER_ID' })
user: User;
@ManyToOne(() => Hotel, hotel => hotel.reserveds)
@JoinColumn({ name: 'HOTEL_ID' })
hotel: Hotel;
}
모든 엔티티(User, Hotel, Review, Reserved, LikedList 등)를 생성한 후, TypeORM이 인식할 수 있도록 app.module.ts의 entities 배열에 모두 추가해야 한다.
// app.module.ts
...
entities: [User, Hotel, Review, Reserved, LikedList],
...