Contents
see List작성일 2025. 11. 30.
Java Record와 Sealed Class 활용
Java 14에서 도입된 Record와 Java 15에서 도입된 Sealed Class는 불변 데이터 모델과 제한된 상속 구조를 간결하게 표현합니다. DTO, Value Object 작성 시 보일러플레이트 코드를 크게 줄여줍니다.
언제 사용하나요?
- DTO(Data Transfer Object) 정의
- 불변 값 객체 생성
- API 응답/요청 모델
- 상속 계층 제한이 필요한 도메인 모델
Record 기본 사용
// 기존 방식 - 많은 보일러플레이트 코드
public class User {
private final Long id;
private final String name;
private final String email;
public User(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
public Long getId() { return id; }
public String getName() { return name; }
public String getEmail() { return email; }
@Override
public boolean equals(Object o) { ... }
@Override
public int hashCode() { ... }
@Override
public String toString() { ... }
}
// Record 사용 - 한 줄로 끝!
public record User(Long id, String name, String email) {}Record 특징
public record User(Long id, String name, String email) {
// 자동 생성되는 것들:
// - private final 필드
// - 생성자
// - getter (id(), name(), email() - get 접두사 없음)
// - equals(), hashCode(), toString()
// 추가 메서드 정의 가능
public String displayName() {
return name + " (" + email + ")";
}
// 정적 팩토리 메서드
public static User of(String name, String email) {
return new User(null, name, email);
}
}
// 사용
User user = new User(1L, "홍길동", "[email protected]");
System.out.println(user.name()); // 홍길동
System.out.println(user.displayName()); // 홍길동 ([email protected])Record 유효성 검사
public record User(Long id, String name, String email) {
// Compact Constructor - 유효성 검사
public User {
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("이름은 필수입니다");
}
if (email != null && !email.contains("@")) {
throw new IllegalArgumentException("유효한 이메일이 아닙니다");
}
// 값 변환도 가능
name = name.trim();
}
}
// 또는 명시적 생성자
public record User(Long id, String name, String email) {
public User(Long id, String name, String email) {
this.id = id;
this.name = Objects.requireNonNull(name, "이름 필수");
this.email = email;
}
}Record + JPA
// Record는 Entity로 직접 사용 불가 (불변이므로)
// DTO로 활용
public record UserDto(Long id, String name, String email) {
public static UserDto from(User entity) {
return new UserDto(
entity.getId(),
entity.getName(),
entity.getEmail()
);
}
public User toEntity() {
return new User(id, name, email);
}
}
// JPQL에서 직접 매핑
@Query("SELECT new com.example.UserDto(u.id, u.name, u.email) FROM User u")
List<UserDto> findAllAsDto();Sealed Class 기본
// 허용된 서브클래스만 상속 가능
public sealed class Shape
permits Circle, Rectangle, Triangle {
public abstract double area();
}
// 서브클래스 정의
public final class Circle extends Shape {
private final double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
}
public final class Rectangle extends Shape {
private final double width, height;
@Override
public double area() {
return width * height;
}
}
// non-sealed로 추가 상속 허용
public non-sealed class Triangle extends Shape {
// 다른 클래스가 Triangle 상속 가능
}Sealed + Pattern Matching
// Java 21+ switch 패턴 매칭
public double calculateArea(Shape shape) {
return switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.width() * r.height();
case Triangle t -> t.base() * t.height() / 2;
// 모든 케이스 커버됨 - default 불필요!
};
}
// Sealed + Record 조합
public sealed interface Result<T> {
record Success<T>(T value) implements Result<T> {}
record Failure<T>(String error) implements Result<T> {}
}
public void handleResult(Result<User> result) {
switch (result) {
case Result.Success<User> s -> processUser(s.value());
case Result.Failure<User> f -> handleError(f.error());
}
}Record vs Lombok
| 항목 | Record | Lombok @Data |
|---|---|---|
| 불변성 | 강제 | 선택 |
| 의존성 | 없음 | 필요 |
| 상속 | 불가 | 가능 |
| Java 버전 | 14+ | 제한 없음 |
주의사항
- Record는 필드 변경 불가 (불변)
- Record는 다른 클래스 상속 불가
- Sealed Class는 같은 모듈/패키지에서만 서브클래스 정의 가능
- JPA Entity로는 Record 직접 사용 불가
java
| No | 작성일 | Title |
|---|---|---|
| 2198 | 2026. 02. 11. | Java Record와 Sealed Class 실전 디자인 패턴 |
| 2197 | 2026. 02. 11. | Java 23 신기능: Structured Concurrency와 Scoped Values |
| 2141 | 2026. 02. 11. | Java 빌드 도구 비교: Gradle vs Maven 2025 |
| 2140 | 2026. 02. 11. | JUnit 5와 Mockito를 활용한 효과적인 단위 테스트 |
| 2139 | 2026. 02. 11. | Java에서의 함수형 프로그래밍 패턴 |
| 2138 | 2026. 02. 11. | Project Loom, Panama, Valhalla: Java의 미래 |
| 2137 | 2026. 02. 11. | Java 메모리 관리와 GC 튜닝 실전 가이드 |
| 2136 | 2026. 02. 11. | GraalVM과 Java: 네이티브 컴파일의 실전 적용 |
| 2135 | 2026. 02. 11. | Java Stream API 고급 활용과 성능 최적화 |
| 2134 | 2026. 02. 11. | Record와 Sealed Class로 견고한 도메인 모델링 |