최근 Java 생태계는 Jakarta EE 11 및 Jakarta Persistence(JPA) 4.0으로의 전환기에 있습니다. 저는 첫 오픈소스 기여로 Spring Data JPA 프로젝트에 참여하여 차세대 표준 사양을 선제적으로 반영하는 유의미한 경험을 했습니다.
JPA 4.0 규격의 핵심은 관행적 코드를 표준 API로 대체하여 생산성과 성능을 개선하는 것입니다. Spring Data JPA는 이에 대응하기 위해 관련 이슈들을 생성중이었습니다.
@StaticQuery) 인프라 확장getResultCount())를 활용한 페이징 최적화첫 오픈소스 기여다보니, 먼저 가장 부담이 적은 #4150 부터 진행해보기로 했습니다.
이전 JPQL 사양에서 CASE 식은 반드시 ELSE 절을 포함해야 했으나, JPA 4.0부터는 ELSE 절이 선택 사항(Optional)으로 변경되었습니다. 생략 시 암묵적으로 NULL을 반환하도록 규격이 완화된 것입니다.
Spring Data JPA는 쿼리 해석을 위해 ANTLR4 파서 생성기를 활용합니다. 사용자가 작성한 쿼리가 신규 사양을 준수하는지 판단하도록 Jpql.g4 및 Eql.g4 정의 파일을 수정했습니다.
[Grammar Refactoring]
// 수정 후: (ELSE scalar_expression)? 구문을 통해 ELSE 절을 Optional하게 변경
general_case_expression
: CASE when_clause (when_clause)* (ELSE scalar_expression)? END ;
simple_case_expression
: CASE case_operand simple_when_clause (simple_when_clause)* (ELSE scalar_expression)? END ;
ELSE 구문을 ()? 기호로 그룹화하여 처리함으로써, 파싱 단계에서의 Syntax Error를 제거하고 표준 규격을 수용했습니다.
해당 Pull Request는 리뷰를 거쳐 메인 저장소에 병합(Merge)되었습니다.

그리고 메인테이너에 의해 백포트(Backport) 되었습니다.

"Jakarta Persistence 4.0은 아직 정식 릴리즈 전인데, 이전 버전(Backport)에 들어가도 정말 괜찮은 걸까?"
자바 데이터 접근 계층은 표준, 구현체, 추상화 라이브러리의 상호작용을 통해 발전합니다.
| 구분 | 프로젝트 | 역할 |
|---|---|---|
| 표준 (Spec) | Jakarta Persistence 4.0 | 공통 명세 및 문법 규칙 정의 |
| 구현 (Impl) | Hibernate 6.x / 7.0 | 명세에 따른 실제 쿼리 실행 엔진 |
| 추상화 (Lib) | Spring Data JPA | 고수준 인터페이스 및 가이드 제공 |
구현체 선행 (Running Code First): 표준 문서 확정 전이라도 Hibernate이 기능을 지원한다면, 프레임워크는 이를 수용해 생태계 정합성을 맞춥니다.
분석 결과, 구현체인 Hibernate는 이미 2021년 6.0(Alpha) 설계 당시부터 SQL 표준 준수를 위해 CASE 문의 ELSE를 선택 사항으로 정의해 두었습니다.
[Hibernate HqlParser.g4 소스코드]
// Hibernate 6.0+ 에서는 이미 ELSE 절에 '?'(Optional)가 적용되어 있음
simpleCaseList
: CASE expressionOrPredicate simpleCaseWhen+ caseOtherwise? END ;
이번 기여의 핵심은 라이브러리 간의 규격 동기화에 있습니다.
ELSE가 없는 쿼리를 실행할 수 있었으나, 상위 계층인 Spring Data JPA 파서의 구식 규격이 이를 가로막고 있었습니다.이번 기여를 통해 얻은 핵심 수확은 라이브러리를 '공급자적 시각'에서 바라보게 된 점입니다.
이번 경험을 발판 삼아, 앞으로는 프레임워크의 설계 철학까지 깊이 파고드는 기여자로 꾸준히 성장하겠습니다.
🔗 관련 Pull Request
Support optional ELSE in CASE expressions (JPA 4.0) #4150