낙서장이자 오답 노트이자 컨닝 페이퍼

패스트캠퍼스 환급챌린지 15일차 : 코드팩토리의 백엔드 아카데미 : 한 번에 끝내는 NestJS 패키지 - 기초부터 MSA까지 강의 후기 본문

NestJS

패스트캠퍼스 환급챌린지 15일차 : 코드팩토리의 백엔드 아카데미 : 한 번에 끝내는 NestJS 패키지 - 기초부터 MSA까지 강의 후기

NangIn 2025. 4. 15. 21:30

본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성하였습니다.

 

강의 내용 정리   

 

Relationship 이론

 

개념 및 용어 정리

  • SQL에서는 "JOIN", ORM에서는 "Relationship"
    • Entity 간 관계를 맺는 개념
    • 대부분의 ORM (TypeORM 포함)에서는 4가지 관계 유형만 존재

 

4가지 Relationship 유형  

관계 유형 설명  예시
1:1 (One to One) 한 엔티티의 하나의 row가 다른 엔티티의 하나의 row와만 연결 사용자 ↔ 프로필
N:1 (Many to One) 여러 개의 row가 하나의 row를 참조 여러 글 → 하나의 사용자
1:N (One to Many) 하나의 row가 여러 개의 row를 참조 하나의 사용자 → 여러 글
N:N (Many to Many) 여러 row ↔ 여러 row 질문 ↔ 카테고리

 

관계 매핑 방식 요약

  • ManyToOne / OneToMany
    • 예시: Photo ↔ User (여러 사진은 하나의 사용자에게 귀속)
    • // Photo.ts (ManyToOne)
      @Entity()
      export class Photo {
      	@PrimaryGeneratedColumn()
      	id: number
      	
      	@Column()
      	url: string
      	
      	@ManyToOne(() => User, (user) => user.photos)
      	user: User;
      }
      
      // User.ts (OneToMany)
      @Entity()
      export class Photo {
      	@PrimaryGeneratedColumn()
      	id: number
      	
      	@Column()
      	name: string
      	
      	@OneToMany(() => Photo, (photo) => photo.user)
      	photos: Photo[];
      }
      
    • 특징
      • 첫번째 파라미터에는 타입을 반환하는 함수를 입력
        • Class Transformer의 Type과 같은 개념
      • 두번째 파라미터에는 첫번째 파라미터에 입력한 클래스의 칼럼중 하나를 입력
        • 이 칼럼은 서로 관련지을 프로퍼티여야 함
        • user.photos, photo.user로 접근 가능
      • foreign key (user_id)는 항상 Many 쪽 테이블(Photo)에 생김
        • photo 테이블에는 user_id 칼럼이 자동으로 생김
        • 네이밍 패턴은 {상태 테이블 이름}_id
        • user_id는 user 테이블의 id 칼럼을 Foreign Key로 레퍼런스
        • user 테이블은 추가로 칼럼이 생성되지 않음
  • OneToOne
    • 예시: User ↔ Profile (1:1 관계)
    • // Profile.ts
      @Entity()
      export class Photo {
      	@PrimaryGeneratedColumn()
      	id: number
      	
      	@Column()
      	gender: string
      	
      	@Column()
      	photo: string
      		
      	@OneToOne(() => User, (user) => user.profile)
      	user: User;
      
      }
      
      // User.ts
      @Entity()
      export class User{
      	@PrimaryGeneratedColumn()
      	id: number
      	
      	@Column()
      	name: string
      
      	@OneToOne(() => Profile, (profile) => profile.user)
      	@JoinColumn() // FK를 이쪽에 생성
      	profile: Profile;
      }
      
    • 특징
      • OneToOne Relationship도 마찬가지로 Annotation을 원하는 프로퍼티에 정의
      • FK를 어느 쪽에 둘지 명시적으로 지정 필요 → @JoinColumn()
      • 한쪽에만 @JoinColumn()을 설정해야 함 → 둘 다 FK를 들고 있는건 불가능하고 의미도 없음
  • ManyToMany
    • 예시: Question ↔ Category (N:N 관계)
    • // Category.ts
      @Entity()
      export class Category{
      	@PrimaryGeneratedColumn()
      	id: number
      	
      	@Column()
      	name: string
      
      	@ManyToMany(() => Question, (question) => question.categories)
      	questions: Question[];
      }
      
      // Question.ts
      @Entity()
      export class Question{
      	@PrimaryGeneratedColumn()
      	id: number
      	
      	@Column()
      	title: string
      	
      	@Column()
      	text: string
      	
      	@ManyToMany(() => Category, (category) => category.questions)
      	@JoinTable() // 이 테이블이 중간 테이블 생성 주도
      	categories: Category[];
      }
      
    • 특징
      • @JoinTable()은 한쪽에만 작성
      • 중간 테이블이 자동 생성
        • 중간 테이블이 생성될 때 @JoinTable이 적용된 테이블 이름이 먼저 위치하게 됨 (question_category_relation_table)
      • FK를 두 테이블 모두에서 들고 있는 제3의 테이블
      • 양방향 접근 가능 (question.categories, category.questions)

 

실습할 내용들

  • ManyToOne: Director → 감독은 여러개의 영화를 만들 수 있음
  • OneToOne: MovieDetail → 영화는 하나의 상세 내용을 갖을 수 있음
  • ManyToMany: Genre → 영화는 여러개의 장르를 갖을 수 있고 장르는 여러개의 영화에 속할 수 있음

 

One to One Relationship 객체 만들어보기

 

관계: Movie ↔ MovieDetail (1:1)

 

구현 목표 및 개념 

항목 설명
관계 종류 One-to-One
예시 구조 하나의 Movie는 하나의 MovieDetail을 가진다
도입 이유 영화에 대한 부가적인 메타 정보(예: 상세 설명)를 별도 테이블로 관리
테이블 구성 Movie, MovieDetail 두 개의 테이블 구성

 

기본 구성 

파일 역할
movie.entity.ts 영화 기본 정보 엔티티
movie-detail.entity.ts 영화 상세 설명 엔티티
base-table.entity.ts 공통 컬럼 관리 (createdAt, updatedAt, version 등)

 

관계 매핑 구현

  • MovieDetail 엔티티
    • @Entity()
      export class MovieDetail extends BaseTable {
        @PrimaryGeneratedColumn()
        id: number;
      
        @Column()
        detail: string;
      
        @OneToOne(() => Movie, (movie) => movie.id)
        movie: Movie;
      }
      
  • Movie 엔티티
    • @Entity()
      export class Movie extends BaseTable {
        @PrimaryGeneratedColumn()
        id: number;
      
        @Column()
        title: string;
      
        @Column()
        genre: string;
      
        @OneToOne(() => MovieDetail, (movieDetail) => movieDetail.id)  
        @JoinColumn() // Movie 테이블에 FK로 detailId 추가
        detail: MovieDetail;
      }
      
  • 중요한 점
    • @JoinColumn()은 1:1 관계에서 FK 소유자를 지정
      • 여기선 Movie가 MovieDetail을 참조하는 쪽으로 선택
    • BaseTable 상속으로 createdAt, updatedAt, version 등 공통 속성 재사용

 

연결 후 필수 작업

  • AppModule 혹은 관련 모듈의 TypeOrmModule.forFeature()에 두 엔티티 모두 등록 필요
  • TypeOrmModule.forFeature([Movie, MovieDetail])
  • 등록 누락 시, EntityMetadataNotFound 오류 발생

 

생성된 DB 구조 (예시)

  • Movie 테이블  
    컬럼명 설명
    id PK
    title 제목
    genre 장르
    detailId FK → MovieDetail.id
    createdAt, updatedAt, version BaseTable 상속
  • MovieDetail 테이블 
    컬럼명 설명
    id PK
    detail 상세 설명
  • Constraints 정보 
    이름 설명
    FK_detailId Movie → MovieDetail 외래키
    UQ_detailId 하나의 Movie만 특정 MovieDetail을 참조할 수 있음 (Unique Constraint)

 

학습 후기         

 

이번 강의에서는 TypeORM의 핵심 기능인 엔티티 간 관계(Relationship)에 대해 집중적으로 학습했습니다. 단순한 CRUD를 넘어서 도메인 모델 간의 구조적인 연결을 직접 설계하며, 데이터베이스의 복잡도를 효율적으로 관리하는 방법을 익힐 수 있었습니다.

 

특히 OneToOne 관계를 Movie와 MovieDetail 사이에 적용하며, 설계 시 어떤 테이블이 외래 키(FK)를 소유할지 명확히 정해줘야 한다는 점이 인상 깊었습니다. @JoinColumn()을 통해 관계 주체를 지정하는 방식은 단순히 코드 레벨의 연결이 아닌, 실제 DB 설계의 주도권을 결정짓는 중요한 포인트임을 깨달았습니다. 실무에서는 이러한 선택이 API 응답 구조나 성능, 유지보수에도 영향을 준다는 사실도 이해하게 되었습니다.

 

또한, 관계 설정 시 @ManyToOne, @OneToMany, @ManyToMany 등의 구조를 코드에서 어떻게 매핑하고, 각각의 관계가 어떤 방향으로 foreign key를 생성하는지 파악하는 과정이 매우 유익했습니다. 특히 ManyToMany는 중간 테이블이 자동으로 생성되며, 그 테이블의 소유권이 @JoinTable()이 선언된 쪽에 있음을 실습을 통해 명확히 체감할 수 있었습니다.

 

이전까지는 단일 테이블 중심의 CRUD 작업에만 익숙했다면, 이번 강의는 객체 간의 연결성과 그에 따른 데이터 구조 설계가 얼마나 중요한지를 처음으로 깊이 체험하게 해주었습니다. 실무에서 사용자와 주문, 상품과 카테고리처럼 서로 연결되는 관계를 어떻게 모델링할지에 대한 실질적인 감각을 익히기에 매우 좋은 학습이었습니다. 추상적인 관계 개념이 실제 코드와 DB 테이블로 구현될 때 어떤 구조로 나타나는지도 명확히 정리되어 남았습니다.

 

학습 인증샷       

                       

수강 인증 사진
학습 인증샷
공부 시작 시간

 

공부 종료 시간

https://abit.ly/lisbva