관리 메뉴

Mini

[Nest] Relation 생성, 1대다, 다대1 연관관계 구현 본문

JS/Nest.js

[Nest] Relation 생성, 1대다, 다대1 연관관계 구현

Mini_96 2024. 9. 18. 02:38
  • 엔티티 수정
/**
 * 게시글은 1명의 사용자를 가진다.
 * 사용자는 게시글 여러개를 가진다.
 * users 모델에서 posts모델을 표현하는 속성이 posts다.
 */
@ManyToOne(()=>UsersModel, (user)=> user.posts, {
  nullable: false,
})
author: UsersModel;
/**
 * 사용자는 여러개의 post를 가진다.
 * (연동할모델, 반환 모델)
 * 포스트모델의 어떤 속성하고 연동인지? post.author
 */
@OneToMany(()=> PostsModel, (post) => post.author)
posts : PostsModel[];

 

* 오류

  • 원인 : author는 더이상 string이 아니라, user 객체이기 때문.
  • 해결 : authorId로 인자를 바꿈
  • 예시 : authorId가 1 -> 1번 유저와 연동됨.

 

* 오류2

QueryFailedError: column "authorId" of relation "posts_model" contains null values

  • 원인 : author는 이제 user 타입인데, 기존의 varchar로 되있음. 기존 data가 어디로 들어갈지 모르기때문임
  • 해결1 : migration 로직 작성
  • 해결2 : post-data 폴더 삭제후, docker-compose up으로 재실행
  • 결과 : 다 쪽에 FK가 하나 생기고, user의 id를 가르키고 있음.
  • 1쪽에는 아무 변화가 없음.

 

* 문제

  • post를 가져올때, user(author)가 안보임

  • 원인 : 서비스는 PostModel만 신경씀 (default)
export class PostsService {
  constructor(
    @InjectRepository(PostsModel)
    private readonly postsRepository: Repository<PostsModel>) {
  }
  • 해결 : typeorm의 relation도 가져오라고 수정.
async getAllPosts(){
  return await this.postsRepository.find({
    relations:['author'],
  });
}

결과

  • 작동원리 : post는 author_id=1만 가지고 있음 -> 이걸 가지고 select from user where id =1로 user정보를 가져오는 것임.

실제로는 JOIN으로 가져오는군.

 

  • gpt

네, 이 코드는 TypeORM에서 엔티티 간의 관계를 정의하는 부분입니다. 구체적으로 Many-to-One 관계를 설정하고 있습니다. 각 부분을 상세히 설명해 드리겠습니다.

/**
 * 사용자는 게시글 여러개를 가진다.
 */
@ManyToOne(() => UsersModel, (user) => user.posts)
author: UsersModel;
  1. 주석 설명:
    • 이 주석은 관계의 본질을 설명합니다: "사용자는 게시글 여러 개를 가진다."
    • 이는 One-to-Many 관계의 반대 측면을 나타냅니다.
  2. @ManyToOne 데코레이터:
    • 이 데코레이터는 "많은" 쪽(현재 엔티티, 아마도 PostsModel)에서 "하나"의 쪽(UsersModel)으로의 관계를 정의합니다.
    • 즉, 여러 게시글이 하나의 사용자에 속할 수 있음을 의미합니다.
  3. 데코레이터 인자:
    • 첫 번째 인자 () => UsersModel:
      • 관계의 대상 엔티티를 지정합니다. 여기서는 UsersModel입니다.
      • 함수 형태로 제공되어 순환 의존성 문제를 방지합니다.
    • 두 번째 인자 (user) => user.posts:
      • 이는 역 관계를 정의합니다.
      • UsersModel에서 이 관계에 접근할 때 사용할 속성 이름을 지정합니다.
      • 즉, UsersModel에는 posts 속성이 있어야 하며, 이를 통해 사용자의 모든 게시글에 접근할 수 있습니다.
  4. author: string;:
    • 이 부분은 TypeScript 타입 정의입니다.
    • 하지만 실제로는 string 타입이 아닐 가능성이 높습니다. UsersModel의 인스턴스나 사용자 ID(보통 number 타입)가 될 가능성이 더 높습니다.

이 관계 설정을 시각화한 다이어그램을 제공하겠습니다:

erDiagram
    POSTS ||--o{ USERS : "belongs to"
    POSTS {
        int id
        string title
        string content
        int authorId
    }
    USERS {
        int id
        string name
        string email
    }
    note on POSTS {
        authorId is a foreign key
        referencing USERS.id
    }

이 다이어그램은 다음을 보여줍니다:

  1. 하나의 USER는 여러 개의 POST를 가질 수 있습니다(One-to-Many).
  2. 각 POST는 하나의 USER에 속합니다(Many-to-One).
  3. POSTS 테이블의 authorId는 USERS 테이블의 id를 참조하는 외래 키입니다.

실제 데이터베이스에서 이 관계는 다음과 같은 SQL 쿼리로 표현될 수 있습니다:

CREATE TABLE "users" (
    "id" SERIAL PRIMARY KEY,
    "name" VARCHAR NOT NULL,
    "email" VARCHAR NOT NULL
);

CREATE TABLE "posts" (
    "id" SERIAL PRIMARY KEY,
    "title" VARCHAR NOT NULL,
    "content" TEXT,
    "authorId" INTEGER,
    FOREIGN KEY ("authorId") REFERENCES "users" ("id")
);

이 설정의 주요 이점은 다음과 같습니다:

  1. 데이터 무결성: 외래 키 제약 조건으로 인해 존재하지 않는 사용자에 게시글을 할당할 수 없습니다.
  2. 효율적인 쿼리: 사용자의 모든 게시글이나 게시글의 작성자를 쉽게 조회할 수 있습니다.
  3. 관계의 명확성: 코드 레벨에서 엔티티 간의 관계가 명확히 표현됩니다.

마지막으로, author: string; 부분은 아마도 다음과 같이 수정되어야 할 것 같습니다:

@ManyToOne(() => UsersModel, (user) => user.posts)
author: UsersModel;

이렇게 하면 author 속성의 타입이 UsersModel이 되어, TypeORM이 올바르게 관계를 처리할 수 있습니다.