본문 바로가기
language/java

record(Java 16)

by 죄니안죄니 2026. 6. 17.
반응형

이번 글은 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 차이는?

record@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

댓글