* 이 게시물은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
(자바 버전 여행 ⑯)
모듈 D를 달려오며 switch·텍스트 블록·record·패턴 매칭을 하나씩 봤다. 이것들이 한 LTS 안에 모여 정식 기능이 된 게 자바 17이다. 그래서 요즘 많은 프로젝트가 “최소 17″을 기준으로 잡는다. 이번 편은 17을 한 장으로 정리한다.
17에 모여 정식이 된 것들
| 기능 | 미리보기로 등장 | 17에서 |
|---|---|---|
| switch 표현식 | 12~13 | 정식(14) |
| 텍스트 블록 | 13~14 | 정식(15) |
| record | 14~15 | 정식(16) |
| instanceof 패턴 | 14~15 | 정식(16) |
| sealed 클래스 | 15~16 | 정식(17) |
자바는 큰 기능을 미리보기(preview)로 먼저 내보내 피드백을 받고, 몇 버전 뒤 정식화한다. 17은 그 정식화가 한꺼번에 도착한 지점이라, 8이나 11에서 올라오면 문법이 확 현대적으로 느껴진다.
보이지 않는 큰 변화 — 강한 캡슐화
문법 말고 중요한 게 하나 더 있다. 자바 17부터 JDK 내부 API가 기본적으로 봉인됐다. 예전엔 sun.misc.Unsafe 같은 내부 클래스를 몰래 갖다 쓰는 코드가 많았는데, 9에서 경고하다 17에서 --illegal-access 옵션 자체가 사라지며 기본 차단이 됐다.
그래서 8→17 마이그레이션에서 낡은 라이브러리가 InaccessibleObjectException으로 터지기도 한다. 대부분은 라이브러리를 최신으로 올리면 풀리고, 정말 필요하면 --add-opens로 특정 패키지만 열 수 있다.
그래서 17을 써야 하나
- 새 프로젝트라면 17 이상이 기본값으로 무난하다(더 최신을 원하면 21).
- 8·11 레거시는, 라이브러리·프레임워크가 17을 지원하는지 확인하고 올린다.
- 17의 문법(record·sealed·switch)은 코드를 눈에 띄게 짧고 안전하게 만든다.
직접 해보기 — 자바 8 코드를 17 문법으로 종합 리팩터
지금까지 배운 걸 한 번에 써먹어 보자. 8 스타일 코드를 17 스타일로 줄이는 연습이다.
- 필드 몇 개짜리 데이터 클래스를
record로 바꾼다. - if-else 분기를 switch 표현식으로 바꾼다.
- 타입 검사+캐스팅을 instanceof 패턴으로 바꾼다.
- 여러 줄 문자열을 텍스트 블록으로 바꾼다.
sealed interface Event permits Login, Logout {}
record Login(String user) implements Event {}
record Logout(String user) implements Event {}
static String render(Event e) {
return switch (e) {
case Login l -> l.user() + " 로그인";
case Logout l -> l.user() + " 로그아웃";
};
}
// render(new Login("cozmo")) -> "cozmo 로그인"
실행해 보면 이렇게 나온다.
cozmo 로그인
여기서 조금씩 바꿔 보자.
Event에record Signup(...) implements Event를 추가하고, render의 switch가 컴파일 에러로 잡아주는지 확인한다.- 같은 로직을 자바 8 문법(클래스+instanceof 캐스팅+if)으로 되돌려 써 보고 줄 수를 비교한다.
한 걸음 더 — 이 코드를 sdk use java 11에서 컴파일해 보고, 어떤 문법이 11에선 안 되는지 확인한다.
✅ 확인 — 17이 어떤 기능들의 ‘정식화 집합’인지, 강한 캡슐화가 마이그레이션에 어떤 영향을 주는지 설명할 수 있으면 모듈 D는 끝이다.
왜 ‘최소 17’이 기준이 됐나
자바 8 다음의 실질적 표준이 17이다. 프레임워크와 빌드 도구가 속속 17을 최소 요구로 올렸고(스프링 부트 3, 최신 Gradle 등), LTS라 오래 지원받으며, 8~16의 문법이 전부 정식으로 여기에 모였다. 그래서 ‘새 프로젝트는 17 이상‘이 사실상 관례가 됐다. 21이 나온 지금도 ‘최소 17’을 잡는 팀이 많은 이유다.
강한 캡슐화가 왜 큰 변화인가
17의 ‘보이지 않는 변화’가 강한 캡슐화다. 그동안 눈감아주던 JDK 내부 API(sun.misc.Unsafe 등) 접근이 기본 차단됐다. 오래된 라이브러리가 이걸 몰래 쓰고 있으면 17에서 깨진다 — 그래서 8→17 마이그레이션의 진짜 난관은 문법이 아니라 이 부분(11편)이다. 대신 그 대가로 JDK가 안전하게 진화할 자유를 얻었다.
8 → 17 체크리스트
- 제거된 모듈(Java EE·CORBA) 의존성을 교체한다.
- 내부 API(
sun.*) 접근 코드를 걷어낸다. - 빌드 도구·라이브러리 버전을 올린다.
--illegal-access옵션이 사라졌음을 감안한다.- 테스트로 확인 — 한 번에 21보다 17 경유가 안전.
17에서 매일 쓰게 될 문법
정리하면 손에 붙일 것은 이렇다: var, 텍스트 블록, record, switch 표현식, instanceof 패턴, sealed. 이것만 익숙해져도 코드가 확 짧고 안전해진다. 21의 화려한 기능(가상 스레드·패턴 switch) 이전에, 17의 이 기본기가 자바 현대화의 뼈대다. 그래서 ‘어디서 시작할까’ 싶으면 17이 가장 무난한 출발점이다.
자바 17까지 왔다. 여기까지만 해도 현업의 상당수를 커버한다. 다음 모듈은 가장 최신 LTS, 자바 21이다. 가상 스레드라는 큰 사건과 패턴 매칭의 완성을 다룬다. 그 첫 편은 record 패턴이다.
🔧 현장에서 — 17로 올릴 때 진짜 힘든 건 문법이 아니었다
Boot 2→3 · Java 8→21 마이그레이션을 해보니, record 같은 새 문법보다 힘든 건 강한 캡슐화 · Deprecated 설정 프로퍼티 · 라이브러리 비호환의 연쇄였다. Bean 생성 과정의 최초 예외부터 역추적하고 계층별로 정상화하는 식으로 풀었다.
교훈 — LTS 이동은 언어를 바꾸는 게 아니라 플랫폼을 옮기는 일에 가깝다. (실전 과정은 24편)
자주 묻는 질문 (FAQ)
왜 자바 17이 기준이 됐나요?
record·sealed·switch 표현식·텍스트 블록이 한 LTS에 모여 정식화됐기 때문입니다. 그래서 많은 프로젝트가 ‘최소 17’을 기준으로 잡습니다.
8에서 17로 올릴 때 주의점은요?
강한 캡슐화로 내부 API 접근이 막혀 InaccessibleObjectException이 날 수 있습니다. 라이브러리를 최신으로 올리거나 –add-opens로 풉니다.

답글 남기기