Java 17은 왜 Kotlin과 잘 맞고, Java 25에서는 무엇을 다시 점검해야 할까
요즘 백엔드 개발을 하다 보면 Java와 Kotlin을 같이 쓰는 경우가 많다.
특히 Spring 진영에서는 Java로 시작한 프로젝트에 Kotlin을 일부 도입하거나, Kotlin 기반 서비스가 Java 라이브러리와 함께 운영되는 경우가 흔하다. Kotlin 공식 문서도 Kotlin/JVM이 Java와 100% 상호운용을 목표로 하며, Java 코드와 Kotlin 코드가 자연스럽게 함께 동작하도록 설계되었다고 설명한다.
그렇다면 실무에서 자주 나오는 질문은 두 가지다.
첫째, 왜 Java 17과 Kotlin 조합은 비교적 안정적으로 느껴질까?
둘째, 왜 Java 25로 가면 “호환되냐 안 되냐”보다 “어디가 깨질 수 있느냐”를 먼저 봐야 할까?
결론부터 말하면, Java 17 + Kotlin은 현재 실무 기준으로 꽤 안정적인 조합이고, Java 25는 JVM 자체가 문제라기보다 빌드 체인·바이트코드 타깃·레거시 API·서드파티 라이브러리 검증 포인트가 늘어나는 구간이다. Oracle의 JDK 25 마이그레이션 가이드도 “대부분의 코드는 JDK 25에서 동작하겠지만, 일부 라이브러리는 업그레이드가 필요할 수 있다”고 안내한다. �
Oracle Docs +1
1. Java 17과 Kotlin 조합이 안정적으로 느껴지는 이유
가장 큰 이유는 생태계 기준점이 이미 Java 17 쪽에 많이 맞춰져 있기 때문이다.
최근 Kotlin 공식 문서에서도 JVM 타깃을 17로 설정하는 예시를 기본적으로 안내하고 있고, Gradle 설정 문서에서는 Kotlin의 jvmTarget과 Java의 targetCompatibility가 다르면 문제가 생길 수 있다고 명시한다. 즉, Java 17은 단순히 “돌아간다” 수준이 아니라, 툴체인과 설정 관점에서 가장 무난하게 정렬하기 쉬운 버전이다. �
Kotlin +1
실무에서 중요한 건 언어 문법보다 빌드 정합성이다.
Kotlin은 Java와 상호운용이 좋지만, 프로젝트에서 Kotlin은 17 바이트코드로 컴파일하고 Java는 다른 타깃으로 컴파일하면 같은 JVM 프로젝트 안에서도 애매한 문제가 생긴다. Kotlin 공식 Gradle 문서는 이런 JVM 타깃 불일치를 명확히 경고한다. 그래서 Java 17 + Kotlin 조합이 편한 이유는 “둘이 친해서”가 아니라, 도구와 설정을 한 줄로 정렬하기 쉽기 때문이다. �
Kotlin +1
또 하나는 프레임워크와 라이브러리의 검증 폭이다.
Java 17은 LTS이고, 많은 빌드 도구와 프레임워크가 이 버전을 기준선처럼 다뤄 왔다. Kotlin 쪽도 Java 17을 대상으로 하는 설정과 예시가 성숙해 있다. 그래서 팀 프로젝트에서 Java 17 + Kotlin 조합은 “최신성”보다 운영 안정성, 팀 합의, CI/CD 재현성 측면에서 장점이 크다. Oracle도 지원 로드맵에서 LTS 릴리스 체계를 별도로 안내하고 있다. �
Oracle +1
2. Java 25는 “비호환”이라기보다 “검증 포인트 증가”로 이해해야 한다
많은 개발자가 Java 25를 볼 때 “이거 Kotlin이랑 안 맞는 거 아냐?”라고 먼저 묻는다.
그런데 이 질문은 반만 맞다. Kotlin 공식 문서에 따르면 Kotlin 2.3.0부터는 Java 25 바이트코드 생성이 지원된다. 즉, 최신 Kotlin을 쓰고 있다면 컴파일러 차원에서 Java 25 자체가 막혀 있는 건 아니다. �
Kotlin
반대로 말하면, Kotlin 버전이 낮으면 Java 25를 제대로 타깃팅하지 못할 수 있다는 뜻이다.
이 경우에도 애플리케이션을 Java 25 JVM 위에서 실행하는 것 자체는 가능할 수 있지만, Kotlin 쪽 바이트코드 타깃은 한 단계 낮춰야 할 수 있다. Kotlin 커뮤니티에서도 “최신 JVM에서 실행하는 것”과 “그 JVM 버전 바이트코드로 컴파일하는 것”은 별개라는 점이 반복적으로 언급된다. �
Kotlin +1
즉 Java 25의 핵심은 이거다.
JVM 실행 호환성
Kotlin 컴파일러의 바이트코드 타깃 지원
Gradle/플러그인/annotation processor/kapt/ksp 호환성
서드파티 라이브러리의 내부 API 사용 여부
이 네 가지를 따로 봐야 한다.
Java 17에서는 이 문제가 상대적으로 단순했지만, Java 25에서는 “JDK만 올리면 끝”이 아니라 툴체인 전체를 같이 올려야 할 가능성이 더 커진다. Oracle 역시 JDK 25 마이그레이션 가이드에서 최신 JDK에서 먼저 실행해 보고, 병행해서 라이브러리 업데이트와 jdeps 분석, 필요시 재컴파일을 하라고 권장한다. �
Oracle Docs +1
3. Java 25에서 실제로 부딪히는 비호환 포인트
3-1. Kotlin/Java 타깃 불일치
가장 흔하고 가장 현실적인 문제다.
예를 들어 Java는 toolchain 25를 쓰는데, Kotlin 플러그인이나 jvmTarget은 낮은 값에 묶여 있으면 컴파일 산출물 기준이 달라진다. Kotlin 공식 Gradle 문서는 jvmTarget과 targetCompatibility가 다를 때 문제가 생길 수 있다고 명확히 적고 있다. �
Kotlin
이 문제는 코드보다 설정에서 터진다.
그래서 Java 25를 쓰려면 먼저 “내 코드가 Java 25 문법을 쓰는가?”보다 “내 빌드 파이프라인이 Java 25 기준으로 정렬되어 있는가?”를 봐야 한다.
3-2. Kotlin 버전이 Java 25 바이트코드를 모르는 경우
Kotlin 2.2.0은 Java 24 바이트코드 지원을 추가했고, Kotlin 2.3.0부터 Java 25 바이트코드 지원이 들어갔다. 즉, Kotlin 플러그인이 너무 낮으면 Java 25 타깃 자체를 모를 수 있다. �
Kotlin +1
이때 실무에서 나오는 착각이 있다.
“JDK 25 설치했으니 Kotlin도 자동으로 Java 25 지원하겠지”라고 생각하는 것이다. 아니다. JDK 버전과 Kotlin 컴파일러 버전은 별도 축이다. 이걸 분리해서 봐야 한다. �
Kotlin +1
3-3. JDK 내부 API나 오래된 관성 코드
Oracle 마이그레이션 가이드는 JDK 내부 API를 쓰는 코드는 계속 실행될 수는 있지만, 지원 API로 옮기는 것이 바람직하다고 설명한다. 특히 JDK 25 기준 가이드에서는 jdeprscan --release 25 -l --for-removal로 제거 예정 API를 점검하라고 안내한다. �
Oracle Docs +1
대표적으로 계속 경계해야 하는 게 이런 것들이다.
sun.* 같은 내부 API 의존
제거 예정 deprecated API 의존
오래된 리플렉션/에이전트 기반 라이브러리
오래된 bytecode manipulation 라이브러리
이건 Java 25만의 문제가 아니라, 최신 JDK로 갈수록 레거시 관성 코드가 더 잘 드러난다는 쪽에 가깝다. Oracle 문서와 OpenJDK JEP 문서도 제거 예정 API와 마이그레이션 도구 활용을 계속 강조한다. �
Oracle Docs +2
3-4. sun.misc.Unsafe 계열 의존
OpenJDK는 sun.misc.Unsafe의 메모리 접근 메서드를 제거 대상으로 단계적으로 밀어내고 있고, 표준 대안으로 VarHandle과 FFM API를 제시하고 있다. 이런 저수준 API에 기대는 라이브러리는 최신 JDK에서 점검 우선순위가 높다. �
OpenJDK +1
실무에서는 내가 직접 Unsafe를 안 써도 안심할 수 없다.
문제는 보통 라이브러리 내부에서 터진다. Netty류, 직렬화 도구, 에이전트, 성능 튜닝 라이브러리, 일부 프록시/리플렉션 계층이 영향을 받을 수 있다. 그래서 Java 25로 갈 때는 “내 코드”보다 먼저 의존성 트리를 봐야 한다.
3-5. Finalization 같은 오래된 자원 정리 방식
OpenJDK는 Finalization을 제거 예정으로 두고 있고, 장기적으로 try-with-resources나 Cleaner 같은 대안으로 이동하라고 권고한다. 즉, 오래된 자원 정리 패턴에 기대는 코드나 라이브러리는 최신 JDK 전환 때 계속 리스크가 된다. �
OpenJDK +1
이것도 Java 25에서 당장 전부 깨진다는 뜻은 아니다.
하지만 “이제는 버틸 만큼 버텼다”는 신호라고 보는 게 맞다. Java 25 전환은 단순 버전업이 아니라, 이런 관성 코드를 정리할 기회다.
3-6. 32비트 x86 같은 오래된 플랫폼 의존
JDK 25에서는 32비트 x86 포트 제거가 반영되었다. 이건 대부분의 서버 개발자에게 직접적인 타격은 크지 않지만, 아주 오래된 환경이나 특수한 레거시 장비를 다루는 경우엔 실제 비호환 이슈가 될 수 있다. �
아줄 +1
즉 Java 25의 비호환은 “언어 문법 충돌”보다 환경과 생태계의 세대교체에 가깝다.
4. 그러면 실무에서는 Java 17과 Java 25를 어떻게 나눠 봐야 하나
제 기준은 단순하다.
Java 17은 운영 기준선이다.
팀 개발, 장기 유지보수, Spring 기반 서비스, Kotlin 혼합 프로젝트, CI/CD 안정성 측면에서 가장 무난하다. Kotlin과 함께 쓸 때도 설명이 쉽고, 트러블슈팅 포인트도 상대적으로 적다. Kotlin 공식 문서의 설정 예시와 Gradle 정합성 가이드도 이 방향과 잘 맞는다. �
Kotlin +1
Java 25는 검증형 업그레이드 대상이다.
최신 JDK 성능, 최신 기능, 최신 생태계를 흡수하려는 팀이라면 충분히 갈 수 있다. 다만 그 전제는 명확하다.
Kotlin 2.3.0 이상 등 최신 툴체인 정렬
Gradle과 플러그인 호환성 점검
서드파티 라이브러리 업그레이드
jdeps, jdeprscan 기반 레거시 의존 점검
실행 테스트와 재컴파일 테스트 분리
Oracle의 JDK 25 마이그레이션 가이드도 비슷한 순서를 권한다. 먼저 실행해 보고, 라이브러리를 업데이트하고, 의존성 분석과 재컴파일을 병행하라는 식이다. �
Oracle Docs +1
5. 내 결론
Java 17과 Kotlin 조합이 좋은 이유는 “둘이 원래 친해서”가 아니다.
현재 실무 생태계에서 가장 정렬하기 쉬운 조합이기 때문이다. 빌드, 프레임워크, 팀 협업, 운영 안정성까지 합치면 Java 17은 아직도 강력한 기준선이다. �
Kotlin +1
반면 Java 25는 “비호환 덩어리”가 아니다.
오히려 잘 준비하면 충분히 갈 수 있는 최신 플랫폼이다. 다만 Java 25에선 언어보다 생태계 검증이 더 중요하다. Kotlin 버전, Gradle 설정, 내부 API 의존, 오래된 라이브러리, 제거 예정 기능 사용 여부를 함께 봐야 한다. Kotlin 2.3.0부터 Java 25 바이트코드를 지원한다는 점은 분명한 호재지만, 그 사실이 곧바로 “프로젝트 전체가 아무 수정 없이 안전하다”는 뜻은 아니다. �
Kotlin +2
정리하면 이렇다.
안정 운영이 목표면 Java 17 + Kotlin은 여전히 강한 선택
최신 전환이 목표면 Java 25는 가능하지만, 검증 없는 점프는 위험
호환성의 핵심은 언어보다 툴체인과 라이브러리다
이게 실무 결론이다.
원하시면 다음 단계로 바로 이어서
이 글을 “IT 블로그 스타일”로 더 공격적으로 다듬은 버전이나
르무엘님 톤으로 더 직설적인 버전으로 다시 써드리겠습니다.

LIST

+ Recent posts