본문 바로가기
개발 기초 다지기

distinctAlias 문제 (쿼리 빌더)

by 너의고래 2024. 7. 22.

알림기능을 구현하던중 알 수 없는 오류에 빠졌다. 쿼리가 'distinctAlias.Cards_card_id'라는 칼럼을 찾지 못한다는 것이다.

알 수 없는 별칭을 찾으려는 TypeORM

정확히 말하면, 이 부분은 알림 기능을 위해 카드의 이름과, 카드에 속한 worker들을 불러올 때 생긴 문제이다. cardRepository에서 직접 불러오는데 왜 저 별칭이 생기는 것일까? 의문인 점은 동일한 코드로 card service 파일에서는 잘 불러와졌다는 것이다.

    //카드 제목 조회
    const card = await this.cardsRepository.findOne({
      where: { cardId },
      select: ['title'],
      relations: ['workers', 'workers.user']
    })

위와 같은 코드인데, 엔티티부터 모듈까지 모두 확인해보았다. 그런데도 계속 적으로 저 오류가 떴고 찾아보던 중, find나 findOne 메서드 사용시 TypeORM이 예상치 못한 별칭을 사용할 수 있다는 점과 이 경우 이러한 오류가 발생할 수 있다는 것이다. 그래서 해결 방법을 찾아보던 중 쿼리 빌더를 알게되었다.

- 쿼리빌더(Query Builder)

쿼리빌더는 무엇일까?

쿼리 빌더(QueryBuilder)는 TypeORM에서 복잡한 쿼리를 작성하고 실행할 수 있도록 하는 도구로, SQL 쿼리를 더 세밀하게 제어할 수 있으며, 다양한 조건과 조인을 포함한 복잡한 쿼리를 쉽게 작성할 수 있다.

 

이러한 쿼리빌더를 통해 해당 부분을 새로 만들어주었다.

  // 카드와 관련된 workers 및 users를 로드
  const card = await this.cardsRepository.createQueryBuilder('card')
    .leftJoinAndSelect('card.workers', 'worker')
    .leftJoinAndSelect('worker.user', 'user')
    .where('card.cardId = :cardId', { cardId })
    .getOne();

 

쿼리빌더를 통해 새롭게 만들어준 결과는 성공이었다.

 

그렇다면 쿼리빌더의 어떤점이 사용했을 때, 정상적으로 작동하게 만들어준 것일까?

1. 정확한 제어

  • 쿼리빌더는 생성되는 SQL 쿼리에 대한 더 높은 수준의 제어를 통해 원하는 방식으로 조인 및 선택 작업을 세밀하게 조정할 수 있다고 한다. 그래서 의도하지 않은 쿼리 문제를 방지할 수 있다.

2. 명시적인 조인

  • 조인할 테이블과 조건을 명시적으로 설정해 조인 조건 및 선택 필드를 명확하게 지정함으로써 Unknown column과 같은 오류를 피할 수 있다.

3. 필드 선택의 유연성

  • .addSelect를 사용하여 추가적으로 필요한 필드(예: card.title)를 명시적으로 선택할 수 있다.
  • TypeORM의 findOne 메서드는 복잡한 조인 및 선택 작업에서 때로 예상치 못한 결과를 가져올 수 있지만, 쿼리빌더는 이를 더 명확하게 처리한다.

 

그렇다면 언제 쿼리 빌더를 사용해야할까?

우선 내 오류같은 경우, findOne 메서드에서 select 옵션과 relations 옵션을 함께 사용할 , 특정 필드만 선택하면서도 관계 데이터를 로드하려고 발생할 있었던 문제라고 추측하고있다. 

 

- findOne 사용 상황:

  • 단순한 조건으로 특정 레코드를 조회할 때.
  • 간단한 관계 로딩이 필요한 경우.
  • 코드가 간결하고 읽기 쉬워야 하는 경우.

- 쿼리 빌더(QueryBuilder) 사용 상황:

  • 복잡한 조인이나 조건이 필요한 경우.
  • 특정 필드만 선택하여 데이터 전송을 최적화해야 하는 경우.
  • 서브쿼리가 필요한 복잡한 쿼리를 작성해야 하는 경우.
  • SQL 쿼리를 세밀하게 제어해야 하는 경우.

 

이를 통해 여러테이블을 join해야한다던지, 특정 필드 지정과 관계 데이터를 로드를 동시에 진행하거나하는 상황에서는 쿼리빌더를 사용해야한다는점을 배울 수 있었다. 아직 멀고 먼 typescript의 세계. 이것저것 시도해보며 더욱 많은 것들을 배워나가고 싶다.

댓글