
자바 record는 class를 짧게 쓰는 문법처럼 보이지만, 실제 목적은 데이터 운반 객체를 명확하고 간결하게 표현하는 것입니다. 반복되는 생성자, accessor, equals, hashCode, toString 코드를 줄여 줍니다.
핵심은 객체의 의미가 값 묶음에 있는지, 행동과 상태 변화에 있는지 구분하는 것입니다. 이 글은 Java 공식 record 문서를 기준으로 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"))
); // trueHashMap key처럼 값 기준 비교가 중요한 작은 객체에서도 record가 실수를 줄일 수 있습니다.
불변성을 과하게 오해하지 말자
record component는 다시 대입할 수 없지만, component가 참조하는 객체 자체가 항상 깊게 불변인 것은 아닙니다. mutable list를 record에 넣으면 리스트 내용은 바뀔 수 있습니다.
public record Team(List<String> members) {
}진짜 불변 값 객체를 원한다면 생성 시점에 방어적 복사나 불변 컬렉션 사용을 함께 검토해야 합니다.
record를 피하는 편이 나은 경우
- 객체 내부 상태가 여러 단계로 변한다
- 생성 이후 비즈니스 규칙에 따라 값을 계속 갱신한다
- 프레임워크가 기본 생성자나 setter 중심 구조를 강하게 요구한다
- 상속 계층과 다형적 동작 설계가 핵심이다
record는 데이터 운반 객체를 위한 도구이지 모든 객체 설계의 기본값은 아닙니다. 값 중심인지 행동 중심인지 먼저 나누면 선택이 쉬워집니다.
실무 체크리스트
- 객체의 의미가 component 값 묶음이면 record를 검토한다
- DTO, 조회 결과, 이벤트 payload는 record 후보로 본다
- 상태 변경 규칙과 행동이 중요하면 일반 class를 쓴다
- mutable component가 들어가면 방어적 복사를 고려한다
- equals/hashCode 구현 실수를 줄이고 싶은 값 객체에 record를 활용한다
정리
record를 이해할 때는 문법을 짧게 쓰는 것보다, 코드가 표현해야 하는 의도를 먼저 보는 편이 좋습니다. 오늘 글의 기준은 ‘읽는 사람이 실수를 줄일 수 있는가’입니다.
함께 보면 좋은 내부 글은 자바 equals hashCode 규약: 왜 같이 구현해야 컬렉션 버그가 줄어들까, 자바 enum은 상수 모음보다 더 강하다: 필드와 메서드를 넣는 이유, 파이썬 dataclass는 왜 쓸까: class를 짧게 쓰는 것보다 중요한 이유입니다. 외부 기준은 Java SE Docs – Record, Oracle Java Language Guide – Records, Java Language Specification – Record Classes를 확인했습니다.