|

자바 record는 class와 무엇이 다를까: 데이터 운반 객체를 짧게 쓰는 기준

자바 record는 class와 무엇이 다를까: 데이터 운반 객체를 짧게 쓰는 기준
record는 데이터 운반 객체를 간결하게 표현하기 위한 특수한 Java 클래스입니다.

자바 record는 class를 짧게 쓰는 문법처럼 보이지만, 실제 목적은 데이터 운반 객체를 명확하고 간결하게 표현하는 것입니다. 반복되는 생성자, accessor, equals, hashCode, toString 코드를 줄여 줍니다.

핵심은 객체의 의미가 값 묶음에 있는지, 행동과 상태 변화에 있는지 구분하는 것입니다. 이 글은 Java 공식 record 문서를 기준으로 record와 class를 선택하는 기준을 정리합니다.

자바 record와 class 선택 기준 요약 카드
record는 모든 class를 대체하지 않고, 값 중심 데이터 모델에 잘 맞습니다.

자바 record를 먼저 한 줄로 정리하면

record는 데이터를 투명하게 운반하기 위한 특수한 class입니다. record header에 적은 component를 기준으로 생성자, accessor, equals, hashCode, toString이 만들어집니다.

public record UserDto(Long id, String name) {
}

UserDto user = new UserDto(1L, "kim");
System.out.println(user.name()); // kim

일반 class였다면 필드, 생성자, getter, equals, hashCode, toString을 직접 만들거나 IDE로 생성해야 했습니다. record는 이 반복을 언어 차원에서 줄입니다.

class와 record의 가장 큰 차이

일반 class는 상태와 행동을 자유롭게 설계할 수 있습니다. record도 class의 한 종류지만, 데이터 구성 요소를 중심으로 제한된 형태를 가집니다. 그래서 모든 class를 record로 바꾸는 것은 맞지 않습니다.

public class BankAccount {
    private long balance;

    public void deposit(long amount) {
        if (amount <= 0) throw new IllegalArgumentException();
        balance += amount;
    }
}

이런 객체는 단순 데이터 묶음보다 규칙과 상태 변화가 중요합니다. 일반 class가 더 자연스럽습니다.

record가 DTO에 잘 맞는 이유

API 응답, 조회 결과, 이벤트 payload처럼 값을 담아 옮기는 객체는 record와 잘 맞습니다. 객체의 의미가 component 값 묶음에 있기 때문입니다.

public record ProductResponse(
    Long id,
    String name,
    int price
) {
}

코드가 짧아지는 것보다 더 중요한 장점은 의도가 분명해진다는 점입니다. 이 타입은 상태를 계속 바꾸는 객체가 아니라 데이터를 담아 전달하는 객체라는 신호를 줍니다.

equals와 hashCode가 자동으로 맞춰진다

record는 component를 기준으로 equals와 hashCode를 제공합니다. 그래서 값이 같은 두 record 인스턴스는 같은 값 객체처럼 비교됩니다.

public record UserKey(String email) {
}

System.out.println(
    new UserKey("a@b.com").equals(new UserKey("a@b.com"))
); // true

HashMap key처럼 값 기준 비교가 중요한 작은 객체에서도 record가 실수를 줄일 수 있습니다.

불변성을 과하게 오해하지 말자

record component는 다시 대입할 수 없지만, component가 참조하는 객체 자체가 항상 깊게 불변인 것은 아닙니다. mutable list를 record에 넣으면 리스트 내용은 바뀔 수 있습니다.

public record Team(List<String> members) {
}

진짜 불변 값 객체를 원한다면 생성 시점에 방어적 복사나 불변 컬렉션 사용을 함께 검토해야 합니다.

record를 피하는 편이 나은 경우

  • 객체 내부 상태가 여러 단계로 변한다
  • 생성 이후 비즈니스 규칙에 따라 값을 계속 갱신한다
  • 프레임워크가 기본 생성자나 setter 중심 구조를 강하게 요구한다
  • 상속 계층과 다형적 동작 설계가 핵심이다

record는 데이터 운반 객체를 위한 도구이지 모든 객체 설계의 기본값은 아닙니다. 값 중심인지 행동 중심인지 먼저 나누면 선택이 쉬워집니다.

실무 체크리스트

  1. 객체의 의미가 component 값 묶음이면 record를 검토한다
  2. DTO, 조회 결과, 이벤트 payload는 record 후보로 본다
  3. 상태 변경 규칙과 행동이 중요하면 일반 class를 쓴다
  4. mutable component가 들어가면 방어적 복사를 고려한다
  5. equals/hashCode 구현 실수를 줄이고 싶은 값 객체에 record를 활용한다

정리

record를 이해할 때는 문법을 짧게 쓰는 것보다, 코드가 표현해야 하는 의도를 먼저 보는 편이 좋습니다. 오늘 글의 기준은 ‘읽는 사람이 실수를 줄일 수 있는가’입니다.

함께 보면 좋은 내부 글은 자바 equals hashCode 규약: 왜 같이 구현해야 컬렉션 버그가 줄어들까, 자바 enum은 상수 모음보다 더 강하다: 필드와 메서드를 넣는 이유, 파이썬 dataclass는 왜 쓸까: class를 짧게 쓰는 것보다 중요한 이유입니다. 외부 기준은 Java SE Docs – Record, Oracle Java Language Guide – Records, Java Language Specification – Record Classes를 확인했습니다.

함께보면 좋은 글