Java 21에서 25로 올릴 때 실제로 확인해야 했던 변경점
문제 발생
Java 21 LTS로 돌던 서비스를 25 LTS로 올리기로 하고, "LTS끼리는 호환되니 런타임만 바꾸면 되겠지"라는 생각으로 CI 이미지의 JDK 버전만 교체했습니다. 일부 빌드 러너에서 JDK 설치 단계부터 실패했고, 코드는 한 줄도 건드리지 않았는데 파이프라인이 깨졌습니다.
원인 분석
LTS 업그레이드에서 실제로 사람을 잡는 건 새로 추가된 문법이 아니라 없어진 것입니다. 새 문법은 안 쓰면 그만이지만 제거된 것에는 선택지가 없습니다.
Java 25에서 눈에 띄는 제거는 JEP 503: 32비트 x86 포트 제거입니다. 32비트 리눅스 러너나 오래된 빌드 컨테이너를 쓰고 있었다면 그 환경에는 JDK 25가 아예 존재하지 않습니다. 코드 호환성 문제가 아니라 실행 환경 문제라, 컴파일 에러 하나 없이 "JDK를 찾을 수 없다"로 끝납니다.
반대로 이번에 preview를 졸업해 확정된 기능들은 기존 코드를 깨지 않고 얹히는 쪽입니다.
- JEP 506 Scoped Values — 정해진 실행 범위 안에서만 보이는 불변 값입니다.
ThreadLocal과 목적이 비슷하지만 대체재는 아닙니다. - JEP 511 Module Import Declarations —
import module java.base;한 줄로 그 모듈이 export하는 API 전체를 가져옵니다. 이름이 겹치면 그 클래스만 명시적으로 import해 해소합니다. - JEP 512 Compact Source Files and Instance Main Methods —
public static void main(String[])없이void main()으로 시작할 수 있습니다. - JEP 513 Flexible Constructor Bodies —
super(...)호출 앞에 문장을 둘 수 있습니다. 부모 생성자에 넘기기 전에 인자를 검증할 수 있게 됩니다. - JEP 510 Key Derivation Function API —
KDF.getInstance("HKDF-SHA256")형태의 표준 키 파생 API입니다.
// JEP 512: 이 세 줄이 온전한 프로그램입니다
void main() {
IO.println("Hello, World!");
}// JEP 513: super() 앞에서 검증할 수 있습니다
class PositiveAmount extends Amount {
PositiveAmount(long value) {
if (value < 0) throw new IllegalArgumentException("음수 금액");
super(value);
}
}해결 방안
- 제거 항목부터 읽습니다. 릴리스 노트에서 "Removed"와 "Deprecated for Removal"을 먼저 보고 새 기능은 그 다음입니다. 25에서는 32비트 x86 포트가 여기 해당하므로 CI 러너와 배포 대상의 아키텍처를 먼저 점검합니다.
--release 21로 먼저 컴파일해 봅니다. JDK 25로 빌드하되 타깃을 21로 두면 런타임 교체와 문법 도입을 분리해, 한 번에 하나씩만 검증할 수 있습니다.ThreadLocal을 쓰던 자리를 전부ScopedValue로 바꾸지 않습니다. ScopedValue는 불변이고 범위가 정해진 값이라, 요청 처리 도중에 값을 갈아끼우는 기존 코드와는 모델 자체가 다릅니다. 새로 작성하는 컨텍스트 전달부터 적용하는 편이 안전합니다.import module은 애플리케이션 코드부터 적용합니다. 라이브러리에 넣으면 이름 충돌 가능성을 그 라이브러리를 쓰는 쪽이 떠안게 됩니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.