이번 글은 Java 16에서 정식으로 추가된 문법이며,
최근 Spring Boot 3.x, Java 17 LTS 프로젝트에서는 DTO를 만들 때 가장 많이 사용되는 문법 중 하나이다.
많은 사람들이
"Getter를 자동으로 만들어 주는 클래스"
정도로 이해하지만,
record는 단순한 문법 설탕(Syntactic Sugar)이 아니다.
데이터만 표현하는 불변(Immutable) 객체를 언어 차원에서 지원하기 위해 만들어진 새로운 타입
이다.
record(Java 16)
데이터(Data)를 표현하기 위한 불변(Immutable) 클래스
기존 Java의 문제
DTO 하나를 만들기 위해서도
엄청난 양의 코드를 작성해야 했다.
public class UserDto {
private final String name;
private final int age;
public UserDto(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() {
...
}
}
실제로 개발자가 작성하고 싶은 것은
name
age
두 개뿐이다.
record 등장
public record UserDto(
String name,
int age
) {
}
끝이다.
컴파일러는 무엇을 만들어 줄까?
다음 코드를
public record UserDto(
String name,
int age
) {
}
컴파일하면
컴파일러가 내부적으로 거의 다음과 같은 클래스를 생성한다.
public final class UserDto {
private final String name;
private final int age;
public UserDto(String name, int age) {
this.name = name;
this.age = age;
}
public String name() {
return name;
}
public int age() {
return age;
}
public boolean equals(...) {
...
}
public int hashCode() {
...
}
public String toString() {
...
}
}
즉,
constructor
↓
getter
↓
equals()
↓
hashCode()
↓
toString()
를 모두 자동 생성한다.
Getter 이름이 다르다.
일반 클래스
user.getName();
user.getAge();
record
user.name();
user.age();
필드명이 곧 메서드명이 된다.
record는 immutable이다.
public record User(
String name,
int age
) {
}
객체 생성
User user = new User("Kim", 20);
변경 시도
user.name = "Lee";
컴파일 오류
Setter도 없다.
user.setName("Lee");
컴파일 오류
즉,
생성
↓
읽기
↓
끝
이다.
왜 immutable일까?
DTO는 대부분
Request
Response
조회 결과
API 응답
처럼
생성 이후 변경할 이유가 거의 없다.
불변 객체가 되면
Thread-safe
예측 가능
버그 감소
HashMap Key 안정성
등의 장점이 생긴다.
equals()도 자동 생성된다.
User user1 = new User("Kim", 20);
User user2 = new User("Kim", 20);
비교
System.out.println(user1.equals(user2));
결과
true
모든 필드를 기준으로 자동 비교한다.
hashCode()도 자동 생성된다.
그래서
Map<User, String> map =
new HashMap<>();
에서도 안전하게 사용할 수 있다.
toString()도 자동 생성된다.
User user = new User("Kim", 20);
System.out.println(user);
출력
User[name=Kim, age=20]
로그를 확인할 때 매우 편하다.
record도 생성자를 가질 수 있다.
public record User(
String name,
int age
) {
public User {
if (age < 0) {
throw new IllegalArgumentException();
}
}
}
여기서
public User {
}
는
Compact Constructor라고 한다.
컴파일러가
자동으로
this.name = name;
this.age = age;
를 수행한다.
개발자는
검증 로직만 작성하면 된다.
일반 생성자도 가능하다.
public record User(
String name,
int age
) {
public User(String name) {
this(name, 0);
}
}
생성자 오버로딩도 가능하다.
record 안에도 메서드를 만들 수 있다.
public record User(
String name,
int age
) {
public boolean isAdult() {
return age >= 20;
}
}
사용
user.isAdult();
가능하다.
record의 제약
상속 불가
record는
이미
public final class User
와 비슷하게 동작한다.
그래서
class AdminUser extends User
불가능하다.
다른 클래스를 상속받을 수 없다.
record User(...)
extends Person
불가능하다.
하지만
Interface 구현은 가능하다.
record User(...)
implements Serializable
가능하다.
Lombok과 비교
@Data
@Data
class User {
private String name;
private int age;
}
Setter 존재
user.setName("Lee");
가능
Mutable 객체
@Value
@Value
class User {
String name;
int age;
}
Immutable
하지만
Lombok 의존성이 필요하다.
record
record User(
String name,
int age
) {
}
Java 언어 자체 지원
Immutable
equals/hashCode/toString 자동 생성
Spring Boot에서는 어떻게 사용할까?
Request DTO
public record UserRequest(
String name,
int age
) {
}
Controller
@PostMapping
public void save(
@RequestBody UserRequest request
) {
System.out.println(request.name());
}
Response DTO
public record UserResponse(
Long id,
String name
) {
}
Service
return new UserResponse(
user.getId(),
user.getName()
);
최근 Spring Boot에서는
Request/Response DTO를
record로 만드는 프로젝트가 매우 많다.
Entity에는 사용할까?
보통 사용하지 않는다.
왜냐하면
JPA Entity는
기본 생성자 필요
Proxy 생성
Setter 또는 상태 변경
지연 로딩
등이 필요하기 때문이다.
record는
Immutable이므로
Entity와 잘 맞지 않는다.
언제 사용하면 좋을까?
추천
DTO
Request
Response
조회 결과
Value Object
API 모델
추천하지 않는 경우
JPA Entity
상태 변경이 많은 객체
상속이 필요한 객체
면접 포인트
record란?
데이터를 표현하기 위한 불변(Immutable) 클래스를 간결하게 작성할 수 있도록 Java 16에서 도입된 새로운 타입이다.
record가 자동 생성하는 것은?
- Constructor
- Accessor(name())
- equals()
- hashCode()
- toString()
왜 DTO에 많이 사용하는가?
- 코드가 매우 간결하다.
- Immutable이라 안전하다.
- equals/hashCode/toString을 직접 작성할 필요가 없다.
- Spring Boot Request/Response DTO와 잘 어울린다.
record와 Lombok @Data 차이는?
| Immutable | Mutable |
| Java 언어 기능 | Lombok 라이브러리 |
| Setter 없음 | Setter 생성 |
| DTO에 적합 | Entity/DTO 모두 사용 가능 |
다음 글
다음은 sealed class(Java 17) 이다.
여기서는
- final과 무엇이 다른지
- permits 키워드의 의미
- 왜 Pattern Matching for switch와 최고의 궁합인지
- 컴파일러가 default 없이도 모든 case를 검사할 수 있는 이유
- Spring과 실무에서는 어떤 상황에서 사용하는지
까지 깊이 있게 설명하겠다.
'language > java' 카테고리의 다른 글
| Optional 심화 (0) | 2026.06.17 |
|---|---|
| sealed class(Java 17) (0) | 2026.06.17 |
| Pattern Matching for switch(Java 21) (0) | 2026.06.17 |
| Switch Expression(Java 14) (0) | 2026.06.17 |
| Text Block(Java 15) (0) | 2026.06.17 |
댓글