ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • TypeOrm을 사용할때 식별관계 테이블에서 find 메서드에 skip과 take 사용시 주의 해야할점
    Nest.js/TypeOrm 2024. 4. 10. 22:23
    728x90
    // repository
     await this.repository.find({
        select: {
            id: true,
            notificationTitle: true,
            notificationFeedId: true,
            createdAt: true,
        },
        where: {
            recipientId,
        },
        relations: {
            sender: true,
        },
        skip,
        take,
    });
    해당 쿼리가 대충 아래처럼 실행 될것이라고 예상 했지만 그렇지 않았다. FK를 가르키는 senderId를 찾을 수 없다는 에러가 나왔다.
    select 
        fn."senderId", 
        fn."id",
        fn."notificationTitle",
        fn."createdAt",
        fn."senderId", 
        fn."notificationFeedId"
    from fam_notification fn left join fam_member fm  on fn."senderId" = fm.id 
    where fn."senderId" ='410b7202-660a-4423-a6c3-6377857241cc'
    LIMIT 10 OFFSET 1

     

    실제로 동작하는 쿼리를 살펴보니 아래와 같았다.

     

    skip과 take를 사용한 쿼리

    SELECT 
    DISTINCT "distinctAlias"."NotificationEntity_id" AS "ids_NotificationEntity_id",
    "distinctAlias"."NotificationEntity_recipientId" AS "ids_NotificationEntity_recipientId",
    "distinctAlias"."NotificationEntity_senderId" AS "ids_NotificationEntity_senderId"
    FROM (
    SELECT 
     .. 조회될 필드들
    FROM "fam_notification" "NotificationEntity" 
    LEFT JOIN "fam_member" "NotificationEntity__NotificationEntity_sender" ON "NotificationEntity__NotificationEntity_sender"."id"="NotificationEntity"."senderId" 
    WHERE ("NotificationEntity"."recipientId" = $1)) "distinctAlias" 
    ORDER BY "NotificationEntity_id" ASC, "NotificationEntity_recipientId" ASC, "NotificationEntity_senderId" ASC LIMIT 10 OFFSET 1
    흥미로웠다. ids_NotificationEntity_recipientId 와 ids_NotificationEntity_senderId 와 NotificationEntity_id 3개는 
    테이블의 PK이다.

     

    @PrimaryColumn('uuid')
    id!: string;
    
    @PrimaryColumn('uuid')
    public readonly recipientId!: string;
    
    @PrimaryColumn('uuid')
    public readonly senderId!: string;

     

    필요에 의해서 recipientId와 senderId는 FK이지만 해당 테이블의 식별자 관계로 형성되어 있다. 그 상태에서 skip과 take 옵션을 넣어주게 되면 위와 같이 중복 제거를 위한 DISTINCT가 추가 되면서 내가 select 하지 않을 예정인 recipientId와 senderId를 찾을 수 없다는 에러가 발생한다. 해결 방법은 당연히 해당 PK들을 전부 select절에 추가 시켜주어야 한다. 그럼 만약 똑같은 find 메서드의 옵션에서 skip과 take를 빼고 요청하는 쿼리는 어떨까

     

    skip과 take를 제외한 쿼리

    SELECT 
    ... 조회될 필드들
    FROM "fam_notification" "NotificationEntity" 
    LEFT JOIN "fam_member" "NotificationEntity__NotificationEntity_sender" 
    ON "NotificationEntity__NotificationEntity_sender"."id"="NotificationEntity"."senderId" 
    WHERE ("NotificationEntity"."recipientId" = $1)

     

    내가 제일 처음 예상한 쿼리 그 잡채다.

     

     

     

    결론

    • find 메서드를 사용 할때에 paginate를 위해 skip과 take를 사용할때 식별관계인 테이블에서 사용시 주의해야 된다.
    • 보통은 쿼리가 좀 더 복잡해지면 typeorm에서 쿼리빌더를 사용해버리는게 나을것 같다.

     

    ps) 빨리 해당 프로젝트를 완성 한 후에 prisma를 해보고 싶다. prisma에서는 여러 관계들을 식별관계로 사용하는 칼럼에대해 어떻게 쿼리를 처리 할까 궁금하다

    728x90
Designed by Tistory.