Java 14에서 Preview로 등장해 Java 16에서 정식 기능으로 채택된 record는 오랜 골칫거리였던 "지저분한 보일러플레이트 코드"를 획기적으로 줄여주었다.
이번 글에서는 record의 정확한 정의와 내부 동작 방식을 자세히 정리해 본다.
1. Record란 무엇인가?
Record는 "데이터를 운반하는 불변(Immutable) 객체"를 아주 쉽게 생성할 수 있도록 만든 새로운 유형의 "클래스"이다.
기존에 데이터 전송 객체(DTO)를 만들 때 우리는 단순히 필드 몇 개를 정의하기 위해 수십 줄의 코드를 작성해야 했다. Record는 이 문제를 해결하기 위해 탄생했다.

2. 코드 비교
이름과 나이를 가진 간단한 Person 객체를 만든다고 가정해 보자.
기존 Class 방식
필드, 생성자, Getter, equals, hashCode, toString... IDE가 만들어준다고 해도 코드가 너무 길고 가독성이 떨어진다.
public class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public boolean equals(Object o) {
// ... (생략: 복잡한 비교 로직)
}
@Override
public int hashCode() {
// ... (생략)
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
Record 방식
위의 모든 코드가 단 한 줄로 끝난다.
public record Person(String name, int age) {
}
3. Record가 자동으로 해주는 일들
컴파일러는 record 키워드를 만나면 내부적으로 다음 기능들을 자동으로 생성한다. 개발자가 직접 작성할 필요가 없다.
- 필드 캡슐화: 모든 필드는
private final로 선언된다. (생성 후 수정 불가) - 생성자: 모든 필드를 초기화하는 '전체 생성자(Canonical Constructor)'가 생성된다.
- 접근자 메서드:
getXXX()가 아니라 필드명과 동일한name(),age()메서드가 생성된다. - Object 메서드:
equals(),hashCode(),toString()이 필드 값을 기반으로 자동 구현된다.
4. Compact Constructor (컴팩트 생성자)
Record는 값을 검증하거나 가공해야 할 때, 매개변수 괄호와 할당 로직을 생략한 아주 독특한 생성자 문법을 제공한다. 이를 Compact Constructor라고 한다.
public record Person(String name, int age) {
// 괄호 ()가 없다!
public Person {
if (age < 0) {
throw new IllegalArgumentException("나이는 0보다 커야 합니다.");
}
// this.name = name; 같은 할당 코드가 없어도 자동으로 할당된다.
}
}
이렇게 작성하면 불필요한 할당 코드는 줄이고 유효성 검사 로직만 깔끔하게 챙길 수 있다.

5. Record와 자바빈즈(JavaBeans)
Record를 사용할 때 가장 많이 헷갈리는 부분이 바로 "기존 라이브러리와의 호환성"이다. 이는 Record가 자바빈즈 규약을 따르지 않기 때문이다.
주요 차이점 비교

그래서 무슨 문제가 발생하는가?
과거의 프레임워크나 라이브러리(구형 Jackson, Hibernate 등)는 "Getter 이름 규칙(get...)"과 "기본 생성자"에 의존하여 동작했다. 그래서 record를 사용하면 필드를 인식하지 못하거나 객체를 생성하지 못하는 에러가 발생하곤 했다. "Record는 잘 안 쓴다"는 오해는 여기서 비롯되었다.
지금은 괜찮은가?
그렇다. 대부분 해결되었다.
- Jackson (JSON): 최신 버전(2.12+)에서는 Record를 완벽하게 지원한다.
- Spring Boot: 웹 계층(Controller)에서 DTO로 사용할 때 문제없다.
단, JPA Entity로는 사용할 수 없다. JPA는 프록시(Proxy) 생성을 위해 '기본 생성자'가 필수이고, 데이터 변경이 필요하기 때문이다.
6. 결론: 언제 써야 할까?
상황에 맞춰 적절하게 사용하는 것이 중요하다.
✅ 적극 추천 (YES)
- DTO (Data Transfer Object): API 요청/응답 객체로 최고다. 불변성 덕분에 데이터가 중간에 오염될 걱정이 없다.
- 불변 데이터: 설정 정보, 좌표 값, 날짜 범위 등 값이 변하지 않는 객체.
- Map의 Key:
equals와hashCode가 필드 값 기준으로 잘 구현되어 있어 키 값으로 쓰기 좋다.
❌ 사용 금지 (NO)
- JPA Entity: DB 테이블과 매핑되는 객체에는 절대 사용 불가.
- 상속이 필요한 경우: Record는 암시적으로
java.lang.Record를 상속받기 때문에 다른 클래스를extends할 수 없다. (단, 인터페이스implements는 가능)
참고 자료
'공부 > JAVA' 카테고리의 다른 글
| [JAVA] 배열(Array) vs ArrayList vs Vector (0) | 2026.02.06 |
|---|---|
| [JAVA] 어노테이션과 리플렉션 원리 (1) | 2025.12.11 |
| [Java] 프로세스와 스레드 (0) | 2025.11.27 |
| [Spring]서블릿(Servlet)과 Spring MVC (0) | 2025.11.17 |
