QA 게시판 프로젝트 진행 중에, 검색 기능
을 추가하고 싶었다.
질문의 제목, 내용 그리고 작성자 중에 검색한 keyword가 포함
되어 있으면 해당되는 질문 글들을 찾아주는 기능이다.
처음에는, 간단한 쿼리 작업이기 때문에 JPA가 제공하는 Specification 인터페이스
를 사용할까 싶었다. 하지만 나중에는 복잡한 쿼리도 다뤄야 할 때가 생길거고, 또 성능 최적화를 위해 나중에 배우고 싶었던 QueryDsl
을 사용하기로 했다.
queryDsl 설정에는 여러가지 방법이 존재하는 것처럼 보였다.
검색하고 찾아본 결과, queryDsl plugin
을 이용하는 방법도 있었고 또 dependencies
에 추가할 implementation
, annotationProcessor
를 설정하는 방법도 있었다.
하지만 plugin
의 경우에는 2018년 7월 이후로 업데이트가 안 된 프로젝트라서 사용하기 좀 찝찝했고, gradle 버전은 고도화 되는데 이 변화에 맞추어 업데이트 되지 못해 생기는 오류들이 생길까 걱정됐다. 따라서 후자의 방법을 택했다.
설정은 간단했다.
dependencies {
implementation 'com.querydsl:querydsl-core'
implementation 'com.querydsl:querydsl-jpa'
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa"
annotationProcessor 'jakarta.persistence:jakarta.persistence-api'
annotationProcessor 'jakarta.annotation:jakarta.annotation-api'
}
궁합이 잘 맞는 버전은 스프링이 알아서 가지고 오도록, 이렇게만 명시해 주면 된다.
또한 QueryDsl 설정을 하기 위해 QueryDslConfig
java 파일을 하나 생성해준다.
@Configuration
public class QueryDslConfig {
@PersistenceContext
public EntityManager em;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(em);
}
}
이것이 끝이었다. 이제 domain @Entity
로 선언된 객체들은 Q
가 붙은 클래스 파일들이 build 폴더 안에 생길 것이다. 그러면 Repository에서 사용할 QAnswer, QUser 등을 임포트해서 그대로 사용하면 된다.
위 설정과 같이 쉽게 끝나면 좋았겠지만.. 항상 개발은 한 번에 잘 되는 경우는 드문 것 같다. 하지만 또 생긴 문제를 이렇게 해결하고 Trouble-shooting
글을 작성하는 것도 재밌는 일이긴 하다.
gradle 파일을 위와 같이 추가해주고 나서 QuerydslConfig 파일을 작성했더니, JPAQueryFactory
의 인자에 들어갈 Entitymanager
가 타입이 맞지 않는다는 compileError
가 발생했다.
package com.qa.board.config;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.persistence.PersistenceContext;
@Configuration
public class QueryDslConfig {
@PersistenceContext
public EntityManager em;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(em); // Error!
}
}
IDE가 발생시킨 이유를 읽어보니 jakarta
에서 임포트된 EntityManager를 인수를 받지 않는다고 한다. 그럼 어떤 걸 인수로 받아야 하는지 보니 javax
에서 임포트 해야 한다고 한다.
스프링 부트 버전이 3.xx 로 업그레이드 되면서 javax -> jakarta
라이브러리로 이름이 변했다고 알고 있었는데, 이렇게 dependency 까지 신경써야 할 줄은 몰랐었다.
그래서 시키는 대로 javax 를 임포트해 넣어주었더니, 이제는 아까 gradle 파일에 넣은 implementation
, annotationProcessor
에서 문제가 발생해 build 가 진행되질 않았다. 아무래도 jakarta 를 사용하는 라이브러리와 javax 를 사용하는 라이브러리 간의 충돌
이 있는 것처럼 보였다.
해결법을 오랜 시간동안 찾아 해맸다. 그 결과, 크게 두 가지 정도
시도해 볼 만한 방법이 있었다.
첫번째 방법은 build 시, jakarta와 javax 간의 충돌이 일어나지 않는 라이브러리 버전
을 찾아 명시해 주는 것이다. 지금은 따로 버전을 명시해주지 않아 스프링이 최적의 라이브러리 궁합을 찾아 가져오는 방법이었다면, 직접 명시해주는 방법인 것이다.
예를 들면,
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
5.0.0 과 같이 라이브러리의 버전을 직접 써주는 방법이다.
두번째 방법은 javax, jakarta 두 개의 라이브러리를 모두 컴파일할 수 있도록
dependencies 에 모두 추가하는 방법이다.
예를 들면,
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
implementation 'jakarta.persistence:jakarta.persistence-api'
implementation 'javax.persistence:javax.persistence-api:2.2'
이런 식으로 javax, jakarta 모두에게 관련한 depedency를 추가해주었다.
그 결과, gradle 도 빌드가 잘 진행되었고 QuerydslConfig 파일도 제대로 컴파일 되었다.
그래서 도대체
jakarta
와javax
는 뭐가 다른건가?
ChatGPT 가 자세하게 설명해준다.
요약하자면,
javax
라는 패키지 네임스페이스로 관리되어 왔다.jakarta
라는 새로운 패키지 네임스페이스로 바꾸어 transfer 과정이 진행됐다.rebranding
또는 Java EE 기술을 이어받았다
고 이해할 수 있겠다.패키지 이름
과 dependency 만 업데이트
해주면 된다.그렇다면 이번에 생긴 문제는 두 패키지 간의 QueryDsl 라이브러리에 대한 dependency 를 맞춰줘야 했던 문제
라고 볼 수 있겠다.
QueryDsl의 설정이 그렇게 까다롭지 않다고 알고 있었어서 그런지, 이번 문제는 조금 당황스러웠다.
앞으로 QueryDsl을 스프링 버전 3.xx 로 사용할 일이 많을 것 같으니 해결 방법을 글로 남겨 둔다.
참고 사이트: https://wikidocs.net/162814
https://stackoverflow.com/questions/74756871/spring-boot-3-with-querydsl