DTO 클래스를 만들 때마다 필드, 생성자, getter, equals, hashCode, toString을 반복해서 쓰고 있다면? Lombok 없이도 해결할 수 있습니다.

이게 뭔가요?

Record 는 Java 16에서 도입된 불변 데이터 전용 클래스입니다. 필드를 선언하면 생성자, getter, equals(), hashCode(), toString()이 자동 생성됩니다.

왜 필요한가요?

데이터를 담기만 하는 클래스에 보일러플레이트가 너무 많습니다:

JAVA
// 기존: 30줄 이상
public class Point {
    private final int x;
    private final int y;

    public Point(int x, int y) { this.x = x; this.y = y; }
    public int getX() { return x; }
    public int getY() { return y; }
    // equals, hashCode, toString...
}

// Record: 1줄
public record Point(int x, int y) { }

자동 생성되는 것들

JAVA
public record User(Long id, String name, String email) { }

이 한 줄로 다음이 모두 생성됩니다:

  • private final 필드 3개
  • 모든 필드를 받는 정규 생성자(Canonical Constructor)
  • 접근자 메서드: id(), name(), email() (get 접두사 없음)
  • 모든 필드를 비교하는 equals()hashCode()
  • 모든 필드를 출력하는 toString()
JAVA
User user = new User(1L, "홍길동", "hong@email.com");
user.name();      // "홍길동" (getter가 아닌 필드명과 같은 메서드)
user.toString();  // "User[id=1, name=홍길동, email=hong@email.com]"

컴팩트 생성자 — 유효성 검증

JAVA
public record Email(String address) {
    // 컴팩트 생성자: 파라미터와 할당을 자동 처리
    public Email {
        if (address == null || !address.contains("@")) {
            throw new IllegalArgumentException("잘못된 이메일: " + address);
        }
        address = address.toLowerCase(); // 정규화
        // this.address = address; ← 자동으로 추가됨
    }
}

커스텀 생성자

JAVA
public record Range(int min, int max) {
    // 정규 생성자
    public Range {
        if (min > max) throw new IllegalArgumentException("min > max");
    }

    // 추가 생성자 — 정규 생성자를 호출해야 함
    public Range(int value) {
        this(value, value); // 정규 생성자 위임
    }
}

메서드 추가

Record에도 메서드를 추가할 수 있습니다.

JAVA
public record Money(BigDecimal amount, Currency currency) {

    public Money add(Money other) {
        if (!this.currency.equals(other.currency)) {
            throw new IllegalArgumentException("통화 불일치");
        }
        return new Money(this.amount.add(other.amount), this.currency);
    }

    public boolean isPositive() {
        return amount.compareTo(BigDecimal.ZERO) > 0;
    }
}

인터페이스 구현

JAVA
public record Product(String name, int price)
        implements Comparable<Product> {

    @Override
    public int compareTo(Product other) {
        return Integer.compare(this.price, other.price);
    }
}

Record와 Sealed Class

패턴 매칭과 함께 대수적 데이터 타입을 표현할 수 있습니다.

JAVA
public sealed interface Shape permits Circle, Rectangle, Triangle {
}

public record Circle(double radius) implements Shape { }
public record Rectangle(double width, double height) implements Shape { }
public record Triangle(double base, double height) implements Shape { }

// 패턴 매칭 (Java 21+)
public static double area(Shape shape) {
    return switch (shape) {
        case Circle c -> Math.PI * c.radius() * c.radius();
        case Rectangle r -> r.width() * r.height();
        case Triangle t -> 0.5 * t.base() * t.height();
    };
}

Record의 제약

  • 상속 불가: Record는 암묵적으로 java.lang.Record를 상속하므로 다른 클래스를 상속할 수 없습니다.
  • 필드 추가 불가: 레코드 헤더에 선언한 필드 외에 인스턴스 필드를 추가할 수 없습니다. (static 필드는 가능)
  • 필드 변경 불가: 모든 필드가 final이므로 setter가 없습니다.

DTO로 활용

JAVA
// 요청 DTO
public record CreateUserRequest(
    @NotBlank String name,
    @Email String email,
    @Min(0) int age
) { }

// 응답 DTO
public record UserResponse(Long id, String name, String email) {
    // 엔티티 → DTO 변환 팩토리 메서드
    public static UserResponse from(User user) {
        return new UserResponse(user.getId(), user.getName(), user.getEmail());
    }
}

Jackson은 Record를 기본 지원합니다. @RequestBody@ResponseBody에 바로 사용할 수 있습니다.

Record vs Lombok @Value

항목RecordLombok @Value
언어 표준O (Java 16+)X (라이브러리)
IDE 지원완벽플러그인 필요
상속불가가능
빌더없음@Builder 지원
접근자name()getName()

자주 헷갈리는 포인트

  • 접근자 이름: Record의 접근자는 getName()이 아니라 name()입니다. JavaBeans 규칙과 다릅니다.
  • JPA 엔티티로 사용 불가: JPA 엔티티는 기본 생성자와 setter가 필요합니다. Record는 불변이므로 엔티티가 아닌 DTO/Projection에 사용하세요.
  • 직렬화: Record는 Serializable을 구현할 수 있지만, 역직렬화 시 정규 생성자를 사용하므로 유효성 검증이 자동으로 적용됩니다.

정리

항목설명
도입Java 16 (정식)
자동 생성생성자, 접근자, equals, hashCode, toString
유효성 검증컴팩트 생성자에서 처리
제약상속 불가, 인스턴스 필드 추가 불가, 불변
적합한 용도DTO, 값 객체, 설정 데이터, 패턴 매칭

References

댓글 로딩 중...