본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성하였습니다.
커리어 성장을 위한 최고의 실무교육 아카데미 | 패스트캠퍼스
성인 교육 서비스 기업, 패스트캠퍼스는 개인과 조직의 실질적인 '업(業)'의 성장을 돕고자 모든 종류의 교육 콘텐츠 서비스를 제공하는 대한민국 No. 1 교육 서비스 회사입니다.
fastcampus.co.kr
0318 화
학습 내용 정리
QueryBuilder
QueryBuilder 사용하는 방식으로 로직 변경하기
두개의 셀이 같은 기능이다.
그리고 더욱 간편하고, 사람이 읽기 쉽다.
하지만
create endpoint에서는 manytomany 같은 기능도 따로 구현해줘야한다.
즉, 코드가 엄청 길어진다.
위의 코드가 아래 처럼 길어진다.
학습후기: QueryBuilder 사용 방식의 전환과 적용 경험
이번 학습에서는 기존 Repository API 방식에서 QueryBuilder를 활용하는 방식으로 로직을 변경하는 과정에 대해 깊이 있게 다루었다. QueryBuilder는 복잡한 쿼리를 더욱 직관적이고 유연하게 작성할 수 있도록 돕는 도구로, 기존의 find 및 save 메서드와 비교하여 몇 가지 장점과 단점이 있었다.
1. QueryBuilder 사용 방식 전환의 필요성
기존 Repository API를 사용할 경우, findAndCount() 등의 메서드를 활용하여 데이터를 가져오는 로직이 단순하면서도 직관적이었다. 그러나 이 방식은 복잡한 조건을 가진 쿼리를 작성할 때 제약이 많았으며, 성능 최적화가 어렵다는 한계가 있었다. 예를 들어, title 검색을 수행하는 기존 코드에서는 Like 조건을 활용하여 간단한 쿼리를 만들 수 있었지만, 더 정교한 필터링이나 조인을 추가할 경우 가독성이 떨어지고 유지보수가 어려워지는 문제가 있었다.
이러한 문제를 해결하기 위해 QueryBuilder를 활용하면, where, join, orderBy 등의 다양한 SQL 기능을 직관적으로 조합할 수 있었다. 특히 where('movie.title LIKE :title', { title: %${title}% }) 형태의 코드가 Repository API의 find 메서드보다 가독성이 높았고, join을 활용하여 복잡한 데이터 구조를 한 번의 쿼리로 조회할 수 있었다. 결과적으로, 쿼리 성능 최적화 및 유지보수 측면에서 QueryBuilder 방식이 더 효율적이라는 점을 확인할 수 있었다.
2. QueryBuilder 적용 후 개선점
QueryBuilder를 적용하면서 얻은 주요 개선점은 다음과 같았다.
- 가독성 향상
- 기존 Repository API 방식에서는 find()와 findAndCount() 등의 메서드 안에서 where 조건을 작성하는 방식이었는데, QueryBuilder를 사용하면 SQL과 유사한 형태로 작성할 수 있어 직관적이었다.
- 특히, 조건문(if(title))을 활용하여 쿼리를 동적으로 조정하는 것이 쉬워졌고, andWhere, orWhere 등을 조합하여 더욱 세밀한 쿼리 작성을 할 수 있었다.
- 유연한 조인 및 관계 처리
- 기존 방식에서는 relations 옵션을 통해 director, genres 등을 가져왔지만, QueryBuilder를 사용하면 leftJoinAndSelect() 등을 활용하여 더 정교한 관계 설정이 가능했다.
- 예를 들어, 특정 감독이 연출한 영화 목록을 조회하면서, 해당 영화의 장르 정보를 함께 가져오는 복합적인 쿼리를 더욱 쉽게 작성할 수 있었다.
- 쿼리 최적화 및 성능 개선
- QueryBuilder는 limit, offset, groupBy 등의 기능을 더욱 직관적으로 사용할 수 있어, 성능 최적화가 용이했다.
- 필요에 따라 getQuery()를 활용하여 실제 생성된 SQL을 확인할 수도 있어, 성능 테스트 및 디버깅이 용이했다.
3. QueryBuilder의 단점 및 해결 방안
QueryBuilder가 가진 강점에도 불구하고 몇 가지 단점도 존재했다.
- 코드가 길어짐
- 기존 save() 방식에서는 cascade 옵션을 활용하여 MovieDetail을 자동으로 저장할 수 있었지만, QueryBuilder 방식에서는 insert()와 execute()를 사용하여 각각 처리해야 했다.
- 특히, ManyToMany 관계에서는 relation().of().add() 등의 추가 작업이 필요하여 코드가 상당히 길어지는 단점이 있었다.
- 해결 방안으로는 반복되는 QueryBuilder 로직을 service 계층에서 별도의 메서드로 분리하여 코드의 재사용성을 높이는 방법을 고려할 수 있었다.
- 트랜잭션 처리의 필요성 증가
- QueryBuilder를 사용할 경우 여러 개의 insert 문이 실행되므로, 데이터 정합성을 유지하기 위해 트랜잭션 처리가 필요했다.
- NestJS의 Transactional 데코레이터를 활용하여 여러 쿼리를 하나의 트랜잭션으로 묶는 방식을 적용하는 것이 필요했다.
4. 학습 후 얻은 인사이트
이번 학습을 통해 QueryBuilder를 활용한 데이터 조회 및 삽입 방식에 대해 깊이 이해할 수 있었으며, 다음과 같은 중요한 인사이트를 얻을 수 있었다.
- 단순한 CRUD 작업에는 Repository API가 더 적합
- 기본적인 데이터 조회, 생성, 수정, 삭제 작업의 경우 Repository API가 더 직관적이며 코드가 간결하다.
- 따라서 QueryBuilder를 적용할 필요가 없는 경우에는 기존 방식을 유지하는 것이 유지보수성 측면에서 더 좋다.
- 복잡한 관계 및 동적 필터링이 필요한 경우 QueryBuilder가 유리
- QueryBuilder를 활용하면 join을 효율적으로 사용할 수 있고, 조건을 동적으로 추가할 수 있어 유연성이 높다.
- 특히, 데이터가 많아지고 복잡한 비즈니스 로직이 추가될 경우, 성능 최적화 측면에서도 유리하다.
- ManyToMany 관계 처리 시 QueryBuilder의 추가 작업 필요
- ManyToMany 관계에서는 relation().of().add()를 활용하여 관계 데이터를 별도로 추가해야 한다는 점을 유념해야 한다.
- 이를 위해 Service 계층에서 helper function을 만들어 반복되는 작업을 줄이는 것이 효과적이다.
5. 향후 적용 계획
이번 학습을 바탕으로 향후 프로젝트에서 다음과 같은 방식으로 적용할 계획이다.
- 단순 조회 및 저장은 Repository API 유지
- 불필요하게 QueryBuilder를 남용하지 않고, 단순한 CRUD 작업에는 기존 방식을 유지한다.
- 복잡한 조건이 필요한 경우 QueryBuilder 적극 활용
- 여러 조건이 조합된 검색 기능을 구현할 때 QueryBuilder를 적극 활용하여 가독성과 성능을 개선한다.
- ManyToMany 및 관계 삽입 처리 로직 모듈화
- ManyToMany 관계를 처리하는 별도의 헬퍼 메서드를 만들어 반복되는 코드를 줄인다.
- 트랜잭션을 활용한 데이터 정합성 유지
- QueryBuilder로 여러 개의 insert 및 update를 수행할 때, NestJS의 트랜잭션 기능을 활용하여 데이터 정합성을 유지한다.
사진



