
객체 지향 프로그래밍(OOP)의 핵심 원칙 중 하나는 '상속'입니다. 하지만 자유로운 상속이 언제나 정답은 아닙니다. 때로는 설계자가 의도한 클래스들만 특정 클래스를 확장할 수 있도록 제한해야 할 필요가 있습니다. 이전까지 자바에서는 이를 구현하기 위해 final 키워드로 상속을 완전히 막거나, 패키지 프라이빗(package-private) 생성자를 사용하는 등 우회적인 방법을 사용해야 했습니다. Java 17에서 정식 도입된 Sealed Classes(봉인된 클래스)는 이러한 설계상의 제약을 언어 차원에서 우아하게 해결합니다. 본 포스팅에서는 Sealed Classes의 개념부터 실무 활용법, 그리고 왜 이것이 현대 자바 설계의 필수 요소가 되었는지 심도 있게 살펴보겠습니다.
1. Sealed Classes란 무엇인가?
Sealed Classes는 어떤 클래스나 인터페이스가 해당 클래스를 상속하거나 구현할 수 있는지를 명시적으로 선언할 수 있게 해주는 기능입니다. 이를 통해 개발자는 클래스 계층 구조에 대한 강력한 제어권을 갖게 되며, 도메인 모델을 더욱 정교하고 안전하게 설계할 수 있습니다. 핵심 키워드는 다음과 같습니다:
sealed: 클래스를 봉인하여 허용된 하위 클래스만 상속할 수 있음을 선언합니다.permits: 상속을 허용할 클래스 목록을 나열합니다.non-sealed: 봉인된 클래스를 상속받은 하위 클래스가 다시 일반적인 상속이 가능하도록 개방합니다.
2. 왜 Sealed Classes를 사용해야 하는가?
가장 큰 이유는 '도메인 지식의 코드화'입니다. 예를 들어, 결제 수단(Payment)에는 카드, 현금, 포인트만 존재한다고 가정해 봅시다. 기존 방식으로는 누군가가 임의로 '비트코인' 결제 클래스를 추가하는 것을 막기 어려웠습니다. Sealed Classes를 사용하면 허용되지 않은 상속을 컴파일 시점에 완벽히 차단할 수 있습니다.
주요 장점
- 보안 및 무결성: 예기치 않은 하위 클래스 생성을 방지하여 코드의 동작을 예측 가능하게 만듭니다.
- 패턴 매칭과의 시너지:
switch표현식에서 모든 하위 클래스를 처리했는지 컴파일러가 검사할 수 있어default케이스가 필요 없는 안전한 코드를 작성할 수 있습니다. - 가독성: 클래스 선언부만 보고도 이 클래스가 어떤 구조로 확장될지 한눈에 파악할 수 있습니다.
3. 코드 샘플: 결제 시스템 도메인 모델링
실제 업무에서 활용될 수 있는 결제 수단 모델링 예시를 통해 문법을 익혀보겠습니다.
// 1. Sealed Interface 선언
public sealed interface PaymentMethod
permits Card, Cash, Point {
}
// 2. 하위 클래스는 반드시 final, sealed, 또는 non-sealed 중 하나여야 함
public final class Card implements PaymentMethod {
private final String cardNumber;
public Card(String cardNumber) { this.cardNumber = cardNumber; }
}
public final class Cash implements PaymentMethod {
// 현금 결제 로직
}
public non-sealed class Point implements PaymentMethod {
// 포인트는 나중에 다른 형태(제휴 포인트 등)로 확장될 수 있도록 non-sealed 선언
}
// 3. 패턴 매칭 활용 예시 (Java 17+)
public String getPaymentInfo(PaymentMethod method) {
return switch (method) {
case Card c -> "카드 번호: " + c.cardNumber();
case Cash h -> "현금 결제 완료";
case Point p -> "포인트 결제 처리";
// permits에 나열된 모든 타입을 처리했으므로 default 문이 필요 없음!
};
}
4. 하위 클래스의 세 가지 선택지
봉인된 클래스를 상속받는 클래스는 반드시 다음 세 가지 상태 중 하나를 명시해야 합니다.
| 제어자 (Modifier) | 설명 | 영향 |
|---|---|---|
| final | 더 이상 상속할 수 없도록 완전히 닫음 | 계층 구조의 종착점 |
| sealed | 자신을 상속받을 수 있는 하위 클래스를 다시 한 번 제한함 | 계층 구조의 중간 제어 |
| non-sealed | 상속 제한을 해제하여 누구나 상속받을 수 있게 함 | 의도적인 확장성 개방 |
5. 결론: 더 안전한 자바를 향하여
Java 17의 Sealed Classes는 단순히 상속을 막는 도구가 아닙니다. 이는 개발자가 코드의 의도를 명확히 선언하고, 컴파일러의 도움을 받아 런타임 오류를 예방하는 강력한 설계 도구입니다. Record와 함께 사용될 때 데이터 중심의 도메인 모델링에서 진정한 위력을 발휘하며, 현대적인 자바 애플리케이션의 품질을 한 단계 높여줍니다. 새 프로젝트를 시작하거나 기존 레거시를 리팩토링할 때, 상속 구조를 설계해야 한다면 반드시 Sealed Classes 도입을 고려해 보시기 바랍니다.
내용 출처:
- OpenJDK JEP 409: Sealed Classes Specification
- Oracle Java Documentation: Language Features - Sealed Classes
- Baeldung: Java 17 Sealed Classes and Interfaces Guide
'Language > Java' 카테고리의 다른 글
| [JAVA] String.isBlank()와 isEmpty()의 차이 : 실무에서 실수하기 쉬운 빈 문자열 검증 (0) | 2026.01.24 |
|---|---|
| [JAVA] Java LTS 버전 완벽 가이드 : 기업이 LTS를 선택하는 이유와 최신 동향 (0) | 2026.01.24 |
| [JAVA] Java 14/16의 Record 키워드 : 데이터 클래스의 혁명 (0) | 2026.01.23 |
| [JAVA] Java 11 : LTS의 시작과 새로운 HTTP 클라이언트 전환점 (0) | 2026.01.23 |
| [JAVA] Java 10 var 키워드 완벽 가이드 : 지역 변수 타입 추론의 마법 (0) | 2026.01.23 |