it ·

자바 17/21 마이그레이션 가이드 (실무 체크리스트 + 트러블슈팅)

반응형

 

자바 17/21 마이그레이션 가이드 (실무 체크리스트 + 트러블슈팅)

Java 마이그레이션 대표이미지

대표이미지: Java/코드 기반 마이그레이션 가이드

자바 LTS를 17 또는 21로 올리는 작업은 “컴파일만 되면 끝”이 아니라, 빌드 도구 / 의존성 / 런타임 옵션 / 리플렉션 / 보안 정책까지 함께 점검해야 장애 없이 마무리됩니다. 이 글은 자바 17 또는 자바 21로 업그레이드할 때 실무에서 바로 쓰는 순서와 체크리스트를 정리한 가이드입니다.

※ 최신 변경점은 공식 문서 기준으로 확인하여 작성했습니다. (예: JDK 17 내부 API 강한 캡슐화, JDK 21 가상 스레드, Security Manager 관련 정책 변화 등)


1) 먼저 결론: 17로 갈까? 21로 갈까?

✅ 추천 기준

  • 신규 프로젝트 / 장기 운영 서비스: 가능하면 21(LTS) 권장 (가상 스레드 등 동시성 개선 폭 큼) :contentReference[oaicite:0]{index=0}
  • 보수적 업그레이드(의존성 리스크 큰 레거시): 17(LTS)로 1차 안정화 후 21로 2차 업그레이드 권장 :contentReference[oaicite:1]{index=1}
  • 상용 배포에서 라이선스/업데이트 정책이 민감하다면: 벤더(배포판) 정책까지 같이 결정 필요 :contentReference[oaicite:2]{index=2}

🔎 알아두면 좋은 “현실적인” 포인트 (벤더 정책)

예를 들어 :contentReference[oaicite:3]{index=3} JDK 17은 특정 시점 이후 업데이트 라이선스 정책이 바뀌는 이슈가 있어, 조직 정책에 따라 21로 이동을 요구받는 경우가 있습니다. (반드시 “우리 회사가 쓰는 배포판” 기준으로 확인하세요.) :contentReference[oaicite:4]{index=4}


2) 마이그레이션 설계 (실무에서 가장 안전한 순서)

Step A. “업그레이드 범위”를 먼저 고정

  • 업그레이드 대상: 런타임만? 빌드/CI까지? 컨테이너/서버 이미지까지?
  • 목표 버전: 17 또는 21
  • 자바 배포판: :contentReference[oaicite:5]{index=5} 계열 / 벤더 JDK(Oracle, Azul, Amazon Corretto 등)
  • 프레임워크: (예) :contentReference[oaicite:6]{index=6}, Jakarta 기반 서버, 배치/스케줄러 등

Step B. CI에서 “2단 빌드”로 안전장치 만들기

바로 21로 한 번에 올리는 것보다, CI에서 기존 JDK + 신규 JDK를 동시에 돌려 “차이”를 먼저 잡는 방식이 안정적입니다.

  • CI 매트릭스: JDK 11/17/21 같이 돌리기(가능하면)
  • 테스트: 단위 + 통합 + E2E + 성능(간단한 부하라도)
  • 배포 전: 스테이징에서 실제 트래픽 패턴 리플레이(가능하면)

Step C. 의존성(라이브러리/플러그인)부터 올린다

자바 업그레이드 실패의 대부분은 “우리 코드”가 아니라 의존성에서 터집니다.

  • 빌드 도구: :contentReference[oaicite:7]{index=7} / :contentReference[oaicite:8]{index=8} 최신 라인으로
  • 테스트 프레임워크/플러그인: surefire, junit, mockito, jacoco 등
  • 서버/컨테이너: 톰캣/넷티/로그 프레임워크 버전 확인

3) 자바 17로 올릴 때 자주 터지는 핵심 포인트

3-1. JDK 내부 API “강한 캡슐화” (리플렉션/Unsafe 접근)

자바 17에서 큰 변화 중 하나는 JDK 내부 요소의 강한 캡슐화입니다. 예전처럼 내부 API를 리플렉션으로 뚫으려 하면 InaccessibleObjectException이 발생할 수 있습니다. :contentReference[oaicite:9]{index=9}

✅ 증상

  • 런타임에서 java.lang.reflect.InaccessibleObjectException
  • 특정 라이브러리(바이트코드 조작, 프록시, 모니터링 에이전트 등)에서만 발생

✅ 대응 전략(우선순위 순)

  1. 라이브러리 업그레이드: 가장 권장(근본 해결)
  2. 대체 API로 교체: 내부 API 의존 제거
  3. 임시 런타임 옵션: --add-opens, --add-exports로 필요한 모듈만 개방 :contentReference[oaicite:10]{index=10}

예시(임시 우회) – 필요한 경우에만 최소 범위로:

# 예시: 특정 모듈의 특정 패키지를 unnamed module(대부분의 앱)에 오픈
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED

⚠️ 주의: 이런 옵션은 “근본 해결”이 아니라 “이행 기간용”입니다. 시간이 지나면 더 강하게 막히는 방향으로 가기 때문에, 장기적으로는 의존성/코드에서 내부 접근 자체를 제거하는 게 안전합니다. :contentReference[oaicite:11]{index=11}

3-2. Security Manager 관련 변화(경고/호환성)

Security Manager는 오래전부터 사용이 줄었고, 자바 17에서 “제거 예정”으로 지정되었습니다. :contentReference[oaicite:12]{index=12} 자바 21에서도 여전히 “제거 예정” 상태이며, 일부 동작/경고 정책이 달라질 수 있어 레거시 도구에서 이슈가 생깁니다. :contentReference[oaicite:13]{index=13}

현업 팁

  • 로그/경고만 늘어나도 운영팀이 민감해합니다 → 스테이징에서 먼저 확인
  • 특정 앱/도구가 System.setSecurityManager를 호출한다면, 21에서 정책 대응이 필요할 수 있습니다 :contentReference[oaicite:14]{index=14}

4) 자바 21로 올릴 때 “얻는 것”과 “주의할 것”

4-1. 가상 스레드(Virtual Threads) – 동시성 비용을 크게 낮춤

자바 21의 대표 변화는 가상 스레드입니다. I/O 중심 서버에서 “스레드 수” 때문에 복잡해지던 설계를 단순화할 수 있습니다. :contentReference[oaicite:15]{index=15}

언제 효과가 큰가?

  • 대량의 동시 요청 처리(웹/API 서버)
  • DB/외부 API 호출 등 I/O 대기가 많음
  • 기존에는 스레드 풀 튜닝이 매우 민감했던 서비스

주의

  • 가상 스레드는 “모든 상황에서 무조건 빠름”이 아니라, I/O 대기형에 특히 강합니다.
  • 블로킹 호출/동기 코드가 많아도 구조를 덜 바꾸고 개선 여지가 생기는 것이 장점입니다.

4-2. Sequenced Collections – 컬렉션 API가 더 직관적으로

자바 21에서는 순서가 중요한 컬렉션에서 “첫/마지막/역순” 같은 작업이 표준화된 형태로 다루기 쉬워집니다. :contentReference[oaicite:16]{index=16}

4-3. “제거/비권장” 항목 체크

자바 21로 갈 때는 제거되었거나 제거 방향인 기능/옵션들이 존재합니다. 특히 오래된 API(예: finalization 관련) 의존이 있으면 경고가 쌓이거나 향후 제거 리스크가 생깁니다. :contentReference[oaicite:17]{index=17}


5) 빌드 설정 예시 (Maven / Gradle / Toolchain)

5-1. Maven (컴파일 타깃 고정)

<properties>
  <maven.compiler.release>21</maven.compiler.release>
</properties>

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.11.0</version>
      <configuration>
        <release>21</release>
      </configuration>
    </plugin>
  </plugins>
</build>

5-2. Gradle (toolchain 권장)

java {
  toolchain {
    languageVersion.set(JavaLanguageVersion.of(21))
  }
}

tasks.withType<JavaCompile> {
  options.release.set(21)
}

5-3. Docker/서버 런타임 이미지도 같이 올리기

운영에서 “빌드는 21인데 런타임 컨테이너는 17/11” 같은 혼종 상태가 의외로 자주 생깁니다. 빌드 파이프라인과 배포 이미지의 JDK를 함께 점검하세요.


6) 실전 마이그레이션 체크리스트 (복붙용)

✅ 사전 점검

  • 현재 JDK/빌드 도구/프레임워크 버전 목록화
  • 리플렉션/에이전트/바이트코드 조작 라이브러리 사용 여부 확인
  • Security Manager, finalization, deprecated API 사용 여부 확인 :contentReference[oaicite:18]{index=18}

✅ 업그레이드 순서

  1. 빌드 도구/플러그인 먼저 업그레이드
  2. 서드파티 라이브러리 업그레이드(가능하면 최신 minor/patch)
  3. CI에서 신규 JDK로 테스트 병행
  4. 스테이징에서 런타임 옵션/경고/로그 확인
  5. 운영 반영(점진 배포 권장)

✅ 장애가 나면 가장 먼저 볼 것 TOP 5

  • InaccessibleObjectException → 내부 캡슐화/리플렉션 문제 :contentReference[oaicite:19]{index=19}
  • 특정 플러그인/테스트만 실패 → 빌드 플러그인 버전
  • 운영에서만 깨짐 → 런타임 JDK 불일치/컨테이너 이미지 확인
  • 경고 폭증(보안/종료 관련) → Security Manager/Deprecated API :contentReference[oaicite:20]{index=20}
  • 성능 변동 → GC/스레딩 설정, 부하 재확인

7) (선택) 17 → 21 “2단 업그레이드” 추천 시나리오

레거시가 크고 의존성이 많다면 아래 전략이 가장 덜 아픕니다.

  • 1차(17): 내부 캡슐화 이슈, 빌드/플러그인 호환성 정리
  • 2차(21): 가상 스레드/최신 기능 적용은 “선택”으로 천천히

운영 관점에서 중요한 건 “최신 기능 도입”보다 업그레이드로 인한 리스크를 통제하는 것입니다.


8) 마무리

자바 17/21 마이그레이션은 한 번만 잘 해두면 이후 유지보수가 훨씬 편해집니다. 특히 21의 가상 스레드는 서버 개발자 입장에서 “코드 구조를 크게 바꾸지 않고” 동시성 한계를 완화할 수 있는 카드라서, 장기적으로 볼 때 업그레이드 가치가 큽니다. :contentReference[oaicite:21]{index=21}

다음 글에서는 Spring Boot/Jakarta 기반 프로젝트에서 실제로 터지는 케이스(의존성 충돌, Tomcat/Netty, 로깅/모니터링 에이전트, CI 이슈)를 “에러 로그 → 원인 → 해결” 형태로 더 실전적으로 정리해보겠습니다.


SEO 정보

Meta Description (160자)

자바 17/21(LTS) 마이그레이션을 위한 실무 가이드. 내부 캡슐화(InaccessibleObjectException), Security Manager 변화, 빌드 설정(Maven/Gradle), 체크리스트와 트러블슈팅까지 정리.

관련 키워드 태그 10개

#Java17 #Java21 #JDK마이그레이션 #자바업그레이드 #Maven #Gradle #VirtualThreads #InaccessibleObjectException #JEP403 #백엔드개발

반응형