JDK 17 — 세 번째 LTS
Java 17은 2021년 9월에 출시된 세 번째 LTS(Long-Term Support) 릴리스입니다. JDK 8 → 11 → 17로 이어지는 LTS 계보에서, 많은 기업이 JDK 11에서 17로 마이그레이션을 진행하는 핵심 타깃 버전입니다. Sealed Classes 정식화, switch 패턴 매칭 프리뷰, 난수 생성기 개선 등이 포함되었습니다.
| JEP | 기능 | 상태 |
|---|---|---|
| JEP 409 | Sealed Classes | 정식 확정 |
| JEP 406 | Pattern Matching for switch | 프리뷰 |
| JEP 356 | 향상된 의사난수 생성기 | 정식 |
| JEP 412 | Foreign Function & Memory API | 인큐베이터 |
| JEP 391 | macOS/AArch64 포트 | 정식 |
| JEP 403 | 내부 API 강한 캡슐화 | 강화 |
| JEP 398 | Applet API 제거 예고 | Deprecated |
Sealed Classes — 상속을 제한하다 (JEP 409)
Sealed Class는 어떤 클래스가 자신을 상속할 수 있는지 명시적으로 선언하는 기능입니다. permits 키워드로 허용된 하위 클래스를 나열하면, 컴파일러가 그 외의 상속을 차단합니다.
도서관 대출 시스템을 떠올리면 쉽습니다. “이 책은 회원, 사서, 관리자만 빌릴 수 있습니다”처럼 허용 목록을 명시하는 것과 같습니다.
// Sealed 인터페이스: 허용된 구현체만 존재 가능
sealed interface Shape permits Circle, Rectangle, Triangle {}
// 각 구현체는 final, sealed, non-sealed 중 하나로 선언
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
// non-sealed: 추가 상속 허용
non-sealed class Triangle implements Shape {
private final double base;
private final double height;
Triangle(double base, double height) {
this.base = base;
this.height = height;
}
double base() { return base; }
double height() { return height; }
}
public class SealedClassExample {
// Sealed + Record 조합으로 면적 계산
static double area(Shape shape) {
if (shape instanceof Circle c) {
return Math.PI * c.radius() * c.radius();
} else if (shape instanceof Rectangle r) {
return r.width() * r.height();
} else if (shape instanceof Triangle t) {
return 0.5 * t.base() * t.height();
}
throw new IllegalArgumentException("알 수 없는 도형");
}
public static void main(String[] args) {
Shape circle = new Circle(5.0);
Shape rect = new Rectangle(4.0, 6.0);
Shape tri = new Triangle(3.0, 8.0);
System.out.printf("원 면적: %.2f%n", area(circle)); // 원 면적: 78.54
System.out.printf("사각형 면적: %.2f%n", area(rect)); // 사각형 면적: 24.00
System.out.printf("삼각형 면적: %.2f%n", area(tri)); // 삼각형 면적: 12.00
}
}
Sealed Class의 하위 클래스는 반드시 다음 중 하나를 선택해야 합니다.
| 제어자 | 의미 |
|---|---|
final | 더 이상 상속 불가 |
sealed | 다시 permits로 하위 클래스 제한 |
non-sealed | 상속 제한 해제 (기존 클래스처럼 자유 상속) |
Record는 암묵적으로 final이므로 별도 선언 없이 사용할 수 있습니다. 이후 JDK 21에서 switch 패턴 매칭이 정식화되면, Sealed Class와 결합한 exhaustive switch(빠짐없는 분기)가 가능해집니다.
Pattern Matching for switch — 프리뷰 (JEP 406)
JDK 17에서 switch 문에 패턴 매칭이 프리뷰로 도입되었습니다. instanceof를 switch 문으로 확장한 것으로, 여러 타입 분기를 깔끔하게 표현합니다.
public class SwitchPatternPreview {
// JDK 17 프리뷰 기능 — 컴파일 시 --enable-preview 필요
static String format(Object obj) {
return switch (obj) {
case Integer i -> "정수: " + i;
case Long l -> "Long: " + l;
case Double d -> String.format("실수: %.2f", d);
case String s -> "문자열(길이=" + s.length() + "): " + s;
case int[] arr -> "int 배열, 크기=" + arr.length;
case null -> "null 값";
default -> "기타: " + obj.getClass().getSimpleName();
};
}
public static void main(String[] args) {
System.out.println(format(42)); // 정수: 42
System.out.println(format(3.14)); // 실수: 3.14
System.out.println(format("Java 17")); // 문자열(길이=7): Java 17
System.out.println(format(new int[]{1, 2})); // int 배열, 크기=2
System.out.println(format(null)); // null 값
}
}
프리뷰 기능이므로 컴파일과 실행 시 --enable-preview 플래그가 필요합니다.
javac --enable-preview --release 17 SwitchPatternPreview.java
java --enable-preview SwitchPatternPreview
이 기능은 JDK 18~20에서 프리뷰를 반복한 뒤, JDK 21에서 정식 확정됩니다.
향상된 의사난수 생성기 (JEP 356)
기존 java.util.Random 클래스는 1995년부터 거의 변하지 않았습니다. JDK 17에서는 RandomGenerator 인터페이스를 도입하여 다양한 알고리즘을 통합된 API로 사용할 수 있게 되었습니다.
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import java.util.stream.Collectors;
public class RandomGeneratorExample {
public static void main(String[] args) {
// 기본 RandomGenerator 사용
RandomGenerator rng = RandomGenerator.getDefault();
System.out.println("기본 난수: " + rng.nextInt(100));
// 특정 알고리즘 선택
RandomGenerator xoshiro = RandomGenerator.of("Xoshiro256PlusPlus");
System.out.println("Xoshiro256++: " + xoshiro.nextInt(100));
// 사용 가능한 알고리즘 목록 출력
String algorithms = RandomGeneratorFactory.all()
.map(RandomGeneratorFactory::name)
.sorted()
.collect(Collectors.joining(", "));
System.out.println("알고리즘 목록: " + algorithms);
// 알고리즘 목록: L128X1024MixRandom, L128X128MixRandom, ...
// Xoshiro256PlusPlus, ...
// 스트림 API와 결합 — 1~100 사이 난수 5개 생성
String randomNumbers = xoshiro.ints(5, 1, 101)
.mapToObj(String::valueOf)
.collect(Collectors.joining(", "));
System.out.println("난수 5개: " + randomNumbers);
// 난수 5개: 72, 15, 88, 43, 61 (실행마다 다름)
}
}
| 알고리즘 | 특징 | 용도 |
|---|---|---|
| L64X128MixRandom | 빠름, 충분한 품질 | 일반 시뮬레이션 |
| Xoshiro256PlusPlus | 매우 빠름, 높은 품질 | 게임, 과학 계산 |
| L128X1024MixRandom | 매우 긴 주기 | 대규모 병렬 시뮬레이션 |
| SecureRandom | 암호학적 안전성 | 보안, 토큰 생성 |
주요 변경: 제거 및 Deprecated
JDK 17에서는 여러 레거시 기능이 제거되거나 제거 예고되었습니다.
Applet API 제거 예고 (JEP 398): java.applet.Applet이 forRemoval로 지정되었습니다. 모든 주요 브라우저가 이미 플러그인 지원을 중단했으므로, 실질적 영향은 없습니다.
Security Manager 제거 예고 (JEP 411): SecurityManager가 forRemoval로 지정되었습니다. 복잡한 정책 설정 대비 실제 사용률이 매우 낮았기 때문입니다.
RMI Activation 제거 (JEP 407): RMI Activation 메커니즘이 완전 제거되었습니다. RMI 자체는 유지됩니다.
AOT 및 Graal JIT 컴파일러 제거 (JEP 410): JDK에 내장된 실험적 AOT/JIT 컴파일러가 제거되었습니다. GraalVM을 별도로 사용하면 됩니다.
macOS/AArch64 포트 (JEP 391)
Apple Silicon(M1, M2) Mac에서 네이티브로 실행되는 JDK 빌드가 공식 포함되었습니다. Rosetta 2 에뮬레이션 없이 직접 실행되므로, Apple Silicon Mac 사용자는 상당한 성능 향상을 체감할 수 있습니다.
JDK 11에서 17로 마이그레이션 포인트
JDK 11 LTS에서 JDK 17 LTS로 업그레이드할 때 확인해야 할 주요 사항입니다.
| 영역 | 변경 사항 | 대응 |
|---|---|---|
| 내부 API | --illegal-access 옵션 제거 | --add-opens로 명시적 열기 |
| 텍스트 블록 | JDK 13~15 프리뷰 → 정식 | 멀티라인 문자열 활용 |
| Records | JDK 14~16 프리뷰 → 정식 | DTO/VO 클래스 교체 |
| Sealed Classes | JDK 15~17 프리뷰 → 정식 | 타입 계층 제한에 활용 |
| instanceof 패턴 | JDK 14~16 프리뷰 → 정식 | 캐스팅 보일러플레이트 제거 |
| NullPointerException | JDK 14부터 메시지 개선 | 디버깅 편의성 향상 |
| GC | ZGC/Shenandoah 정식 | 저지연 GC 검토 |
가장 흔한 마이그레이션 이슈는 내부 API 접근 차단입니다. javax.xml.bind, javax.annotation 등은 JDK 11에서 이미 제거되었으므로 별도 의존성을 추가해야 합니다.
<!-- javax.xml.bind → jakarta.xml.bind로 마이그레이션 -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>4.0.0</version>
</dependency>
정리
JDK 17은 LTS로서의 안정성과 현대적 기능을 모두 갖춘 버전입니다.
- Sealed Classes:
permits로 상속 계층을 명시적으로 제한. Record와 조합하면 대수적 타입(ADT)에 가까운 모델링 가능 - switch 패턴 매칭 (프리뷰):
instanceofif-else 체인을 switch로 대체. JDK 21에서 정식화 - RandomGenerator: 다양한 난수 알고리즘을 통합 인터페이스로 사용
- Apple Silicon 네이티브: macOS ARM64에서 네이티브 성능
- 레거시 정리: Applet, Security Manager, RMI Activation 제거 또는 제거 예고
JDK 11에서 17로의 마이그레이션은 대부분 순조롭지만, 내부 API 직접 접근과 Java EE 모듈 제거 영향을 사전에 점검해야 합니다. Spring Boot 3.x, Jakarta EE 10 등 최신 프레임워크는 JDK 17을 최소 요구 버전으로 지정하고 있으므로, 아직 마이그레이션하지 않았다면 적극 검토하는 것을 권장합니다.