Kotlin으로 서버 개발을 하다 보면 생각보다 많은 ORM 선택지와 Query 빌더들을 접할 수 있다.
ORM으로는 JPA, Ktorm, Exposed, JOOQ 등이 있다. 오늘 글의 주제는 바로 Query 빌더에 대한 내용이다.
Query 빌더 중 가장 많이 접한 것은 바로 kotlin-jdsl과 Querydsl이다.
먼저 kotlin-jdsl이란?
Kotlin JDSL | Kotlin JDSL
Latest stable version: 3.5.4
kotlin-jdsl.gitbook.io
Kotlin JDSL은 메타모델 없이 쿼리를 쉽게 만들 수 있게 도와주는 Kotlin 라이브러리이다.
Kotlin JDSL의 패러다임과 추구하는 방향은 아래와 같다.
- APT를 이용하면 클래스명이나 필드명이 변경되었을 때마다 다시 컴파일해야 하는 불편함 해소
- 쉽게 쿼리를 만들 수 있도록 KClass와 KProperty 기반의 Domain-Specific Language
- DB 라이브러리가 아닌 DB 라이브러리를 사용할 때 도움을 주기 위해서 만들어진 라이브러리이기 때문에 직접 쿼리를 실행하거나 DB 라이브러리를 래핑 하는 등의 작업은 하지 않는다
결과적으로 jdsl은 ORM이 아닌 DB 라이브러리를 사용할 때 도움을 주는 갖춘 코틀린 진영의 Query 빌더이다.
Querydsl 이란?
Querydsl - Unified Queries for Java
Querydsl - Unified Queries for Java
Unified Queries for Java. Querydsl is compact, safe and easy to learn. <!-- Querydsl Unified Queries for Java Querydsl provides a unified querying layer for multiple backends in Java. Compared to the alternatives Querydsl is more compact, safer and easier
querydsl.com
QueryDsl은 하이버네이트 쿼리 언어(HQL: Hibernate Query Language)의 쿼리를 타입에 안전하게 생성 및 관리해주는 프레임워크이다.
주로 Spring Data와 함께 사용하며 복잡한 쿼리, 동적 쿼리를 해결할 수 있는 것이 QueryDSL이다.
Querydsl은 다음과 같은 장점들이 있다.
- 자바 코드로 SQL문을 작성할 수 있어 컴파일 시에 오류를 발생하여 잘못된 쿼리가 실행되는 것을 방지
- 복잡한 쿼리나 동적 쿼리 작성이 편리하다.
- 쿼리 작성 시 제약 조건 등을 메서드 추출을 통해 재사용할 수 있다.
- JPQL 문법과 유사한 형태로 작성할 수 있어 쉽게 적응할 수 있다.
특이한 점은 QClass이다.
- 컴파일 단계에서 엔티티를 기반으로 QClass를 생성하는데 JPAAnnotationProcessor가 컴파일 시점에 작동해서 @Entity 등등의 어노테이션을 찾아 해당 파일들을 분석해서 QClass를 만든다.
- QClass는 Entity와 형태가 똑같은 Static Class이다. 또한, 쿼리를 작성할 때 QClass 를 기반으로 쿼리를 실행한다.
결과적으로 Querydsl의 내부 동작 또한 Qclass 기반 코드로 만들어진 것을 JPQL로 변환하여 EntityManager를 통해 DB에 질의하는 것이다.
Kotlin-jdsl과 Querydsl의 비교
사실 따지고 비교하자면, 코틀린을 사용하는 곳에서는 Querydsl의 단점들이 Kotlin-jdsl에서 대부분 해결이 가능하다.
Kotlin JDSL은 QueryDSL과 달리 메타 모델(Metadata Model)을 요구하지 않는다.
메타 모델은 생성할 때 드는 단점은 다음과 같다.
- 프로젝트 초기 설정의 복잡성이 증가한다.
- 일반적인 빌드 프로세스에 메타데이터 생성 단계가 추가된다.
위 같은 단점이 있어도 Querydsl을 사용하는 것은 타입 안정성을 바탕으로 컴파일 시점에 쿼리 오류를 발견할 수 있게 해준다.
하지만 중요한 점은 Kotlin JDSL은 메타 모델을 요구하지 않으면서도 이러한 타입 안전성을 제공한다는 것이다.
Kotlin JDSL은 Kotlin의 KProperty를 활용하여 타입 기반의 쿼리를 작성할 수 있도록 지원.
KProperty를 사용함에도 리플렉션으로 인한 성능 저하를 걱정할 필요가 없는데, 이는 Kotlin JDSL이
KProperty의 이름만을 사용하며, Kotlin이 Java로 컴파일할 때 이 이름을 상수로 등록하기 때문.
KProperty는 IDE를 통해 필드 변경을 자동으로 감지할 수 있어, 객체의 필드명이 변경되더라도 즉시 반영.
어떤 게 더 나은지가 아니라, 어떤 게 더 적합한지를 고민해라
개발에 있어 기술 선택은 당연히 개발자가 속한 그룹의 선택이며, 해당 선택은 이유와 명분이 따르기 때문이다.
더 뛰어나고 좋은 기술을 선택하는 것도 좋지만, 개발은 혼자 하는 것이 아니기 때문에 도입하려는 이유, 쓰려는 이유, 팀원들의 러닝커브 등을 고려한다. 그리고 도입하려는 기술이 정말 합당한가에 대해 체크해야 한다.
예를 들어 Querydsl은 2018년에는 2번, 2019년에는 1번, 2020년에는 2번, 2021년에는 두 번, 그리고 2024년에는 한 번의 릴리스가 있다. 이렇듯 이슈는 꾸준히 제기되고 있지만, 실제로 적극적인 패치나 새로운 기능의 개발은 이루어지고 있지 않은 상황이다.
반면 kotlin-jdsl을 꾸준한 업데이트를 가지고 가며 비교적 새로운 기능이나 적극적인 패치에 굉장히 열려있는 기술이다. 하지만 신생기술이고 잠식된 문제점 혹은 사용감에 있어서는 아직 래퍼런스가 많이 없다는 것이 단점이다.
팀에서 이미 Querydsl을 잘 사용하고 있다면 큰 문제없이 쭉 이어나갈 가능성이 크다. 하지만 코틀린을 사용할 때 조금 주의해야 하는 점은 Kotlin에서 Querydsl을 사용하려면 kapt가 필수적으로 필요하다.
kapt는 Kotlin의 Annotation Processor를 활용하기 위한 도구로, 주로 Kotlin에서 작성된 코드의 Annotation을 처리하는 데 사용된다. 그러나 공식 문서에 따르면 kapt의 새로운 기능 지원이 중단되고 유지보수만 진행될 예정이라고 한다. kapt가 향후 Kotlin에서 지원되지 않을 가능성이 높다는 것을 의미한다. KSP라는 대안이 있긴 하지만 Querydsl에서 이를 지원할지는 아직 미지수이다.
둘 다 써본 입장에서는?
나는 Querydsl, kotlin-jdsl 둘 다 써본 입장이다.
사실 기술 동향을 살펴보면 일반적으로 JPA + Querydsl을 많이 사용한다. 나 또한 이 둘의 조합을 굉장히 많이 사용했고 응용했다. 특히 동적 쿼리나 JPA로 만들기 힘든 쿼리들은 보통 Querydsl을 통해 작성했다.
물론 Querydsl이 완벽한 해답은 아니었다. 매우 복잡한 Query에 대한 질의, DTO를 사용한 프로젝션, transform 등 여러 가지 헤쳐나가야 하는 과정이 있었다.
이는 jdsl도 비슷하다. 다만 더 코틀린 친화적이라는 것이다. 코틀린의 여러 함수를 가져다 쓰며 Query를 조작하고 코틀린의 문법이랑 유사하기 때문에 이를 적극적으로 활용할 수 있다.
다만, jdsl에서 지원하는 기능을 코틀린과 적절히 조합해서 편하게 사용하는 것은 사실 추가적인 코드 작업이 필요하다. 예를 들어 헬퍼 클래스를 만들어서 Nullable 컬렉션을 처리하는 함수를 제작하거나 단일 조회 함수 같은 것들을 만들어야 했다.
그래서 결론은?
내가 코틀린을 사용하여 새롭게 프로젝트를 시작할 것이라면 다시 한번 JDSL과 Querydsl 중 선택을 고민할 것이다.
JDSL의 경우 Custom으로 DSL 형태로 만들어서 사용할 수 있으며 코틀린 친화적인 문법과 메타 모델 생성 없이 간편하게 사용할 수 있기 때문이다.
하지만 모두가 알다시피 우리는 Querydsl에 익숙하다. 그리고 Querydsl은 jdsl에 비해 래퍼런스와 연식이 있기 때문에 문제 상황을 겪었을 때 해결할 수 있는 시간이 빠르다. 그리고 어느 정도 성숙해졌다.
위에서 언급된 내용들과 직접 써보면서 느낀 경험은 Querydsl과 충분히 겨룰 수 있기 때문이다. 혹여나 더 편리하다고 느낄 때도 많았다.
나는 코틀린을 꾸준히 사용한다면 kotlin-jdsl를 선택하고 싶다.
그리고 jetbrains에서 만든 Kotlin 경량 ORM인 Exposed도 사용해보고 싶다. 이는 직관적이고 간결한 API를 제공하기 때문이다. 이 또한 추후 블로그 글에서 다룰 것이다.
'서버 개발(생각과 구현)' 카테고리의 다른 글
외부 API 호출을 위한 OpenFeign, RestClient, WebClient들의 차이점 (0) | 2024.12.20 |
---|---|
Kotest를 활용한 유닛 테스트 구성 (0) | 2024.12.19 |
코틀린을 활용한 JPA 엔티티 전략 고민 과정 (0) | 2024.12.19 |
Spring Eureka를 사용해서 MSA 체험기 (2) (0) | 2024.12.19 |
Spring Eureka를 사용해서 MSA 체험기 (1) (0) | 2024.12.19 |