
최근 AI가 만들어준 Java 코드를 보다가 낯선 문법을 만났다.
record
처음 봤을 때는 솔직히 당황했다.

나는 기존 Java/Spring MVC 환경에서 DTO를 만들 때 클래스를 선언하고 필드, 생성자, getter, setter를 사용하는 방식에 익숙했다.
그런데 AI는 그걸 record 선언 하나로 표현해 두었다.
코드는 짧아졌는데 정작 내가 이해하지 못하는 부분이 생겼다.
(순간 멘붕이 강하게 와서 진짜 당황했다. 나는 끝났다고도 생각했다.)
그래서 기존 DTO와 무엇이 다른지부터 정리해봤다.
Java record란?
record는 데이터를 담는 클래스를 간결하게 선언할 수 있도록 Java 16에서 정식 도입된 기능이다. Java 17에서도 사용할 수 있다.
기존 DTO에서 반복해서 작성하던 생성자, 데이터 접근 메서드, equals(), hashCode(), toString() 등을 자동으로 제공한다.
예를 들어 이런 코드다.
public record UserDto(String name, String email) {
}
이 한 줄의 선언만으로 이름과 이메일을 가진 객체를 만들 수 있다.
하지만 일반 클래스와 완전히 같은 것은 아니다.
1. 기존 DTO 클래스와 비교해보면
기존 방식에서는 다음처럼 작성할 수 있다.
public class UserDto {
private String name;
private String email;
public UserDto(String name, String email) {
this.name = name;
this.email = email;
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
public void setName(String name) {
this.name = name;
}
}
반면 record는 다음과 같다.
public record UserDto(String name, String email) {
}
코드 양이 확실히 줄었다.
단, 위 두 코드는 완전히 같은 동작을 하는 클래스는 아니다. 기존 클래스에는 값을 변경하는 setter가 있지만 record에는 기본적으로 setter가 없다.
실제 코드에서는?
내가 만들고 있는프로젝트에서는 개인정보 탐지 결과를 담는 DetectedItem을 record로 선언했다.
public record DetectedItem(
String id,
PiiType type,
String maskedValue,
double confidence,
BoundingBox boundingBox
) {
}
탐지 항목의 ID, 개인정보 유형, 마스킹된 값, 신뢰도, 위치 정보를 담는 구조다.
기존 방식이었다면 각각의 필드를 선언하고 생성자와 getter 등을 작성했을 것이다.
그런데 record에서는 선언한 항목을 기준으로 생성자와 접근자가 자동으로 만들어진다.
실제 객체를 생성하는 코드도 살펴봤다.

detectedItems.add(
new DetectedItem(
id,
type,
maskedValue,
item.confidence(),
null
)
);
new DetectedItem(...)을 통해 앞서 선언한 다섯 개의 값이 순서대로 전달된다.
기존 Java 클래스의 생성자를 호출하는 방식과 크게 다르지 않다.
참고로 IntelliJ 화면에 회색으로 보이는 boundingBox:는 IDE가 보여주는 파라미터 이름 힌트다. 실제 코드에 입력된 문법은 아니다.
여기까지 비교해보니 record가 어떤 역할을 하는지 조금 이해되기 시작했다.
2. record에는 왜 getName()이 없을까?
내가 기존 Java 코드에 익숙해서 가장 먼저 확인한 부분이다.
일반 DTO라면 다음과 같이 값을 가져온다.
userDto.getName();
그런데 record가 자동 생성하는 접근자는 이름이 다르다.
UserDto userDto =
new UserDto("민수", "test@example.com");
String name = userDto.name();
String email = userDto.email();
getName()이 아니라 name()이다.
따라서 별도로 메서드를 선언하지 않은 record에서 getName()을 호출하면 컴파일 오류가 발생한다.
기존 JavaBean 방식과 record가 서로 다른 접근자 규칙을 사용하기 때문이다.
예를 들어 Security Lens의 DetectedItem에서도 탐지 신뢰도에 접근하려면 다음과 같은 형태를 사용한다.
DetectedItem detectedItem = new DetectedItem(
"1",
type,
"***-****-1234",
0.95,
null
);
double confidence = detectedItem.confidence();
기존 방식이었다면 getConfidence()가 익숙했을 텐데, record에서는 confidence()로 호출한다.
3. setter도 없는데 값을 어떻게 바꾸지?
record의 각 필드는 private final이다.
객체를 생성한 다음 필드에 다른 값을 다시 할당할 수 없다.
UserDto user =
new UserDto("민수", "test@example.com");
// 기본적으로 존재하지 않는 메서드
// user.setName("지수");
다른 값이 필요하다면 새로운 객체를 만든다.
UserDto updated =
new UserDto("지수", user.email());
여기서 하나 주의할 점이 있다.
record를 불변 객체라고 부르지만 정확히는 얕은 불변성(shallow immutability)을 제공한다. 필드가 List 같은 변경 가능한 객체를 참조하고 있다면 그 객체 내부의 데이터까지 자동으로 보호되지는 않는다.
4. 그러면 기존 DTO를 전부 record로 바꿔야 할까?
그럴 필요는 없다고 생각한다.
record는 데이터를 전달하고, 생성한 뒤 값을 변경할 필요가 없는 상황에 잘 맞는다.
예를 들면 API 응답에 필요한 데이터를 담는 DTO가 있다.
반면 기존 프로그램에서 setter를 이용해 데이터를 단계적으로 채우거나, 특정 라이브러리가 JavaBean 방식의 생성자와 접근자를 요구한다면 호환성을 따져봐야 한다.
특히 기존 MyBatis 기반 프로젝트를 유지보수한다면 모든 DTO를 일괄적으로 바꾸기보다 매핑 방식과 라이브러리 버전을 먼저 확인하는 편이 안전하다.
코드가 짧아졌다는 이유만으로 기존 코드를 전부 교체할 필요는 없다.
5. 기존 class와 record 차이 정리
구분일반 DTO 클래스record
| 선언 | 필드·메서드 직접 작성 | 컴포넌트 중심 선언 |
| 생성자 | 직접 작성하거나 도구 사용 | 자동 생성 |
| 값 접근 | 보통 getName() | name() |
| Setter | 작성 가능 | 기본 제공 안 함 |
| 필드 재할당 | 설계에 따라 가능 | 불가능 |
| equals/hashCode | 직접 구현 또는 도구 사용 | 자동 생성 |
| 상속 | 일반 클래스 상속 가능 | 다른 클래스 상속 불가 |
일반 클래스로도 불변 DTO를 만들 수 있다. 위 비교는 흔히 사용하는 가변 DTO를 기준으로 한 것이다.
지금의 내 생각
기존 Java/Spring MVC 방식으로 코드를 작성할 수 있다고 해서 최신 Java 문법까지 자연스럽게 이해하게 되는 것은 아니었다.
AI가 만들어준 코드는 더 짧고 간결했지만 처음에는 내가 알고 있던 코드보다 오히려 이해하기 어려웠다.
record 자체는 생각보다 복잡한 개념이 아니었다.
다만 기존 getter와 setter 방식에 익숙했던 내 입장에서는 왜 이렇게 작성하는지 이해하는 과정이 필요했다.
앞으로 AI가 만들어주는 Java 코드를 볼 때도 바로 적용하기보다 기존에 알고 있던 방식과 비교하면서 이해해보려고 한다.
참고 문서
'개발 노트' 카테고리의 다른 글
| Spring Boot MVP 배포 전 체크리스트 15가지 — 출시 전에 이것만 확인하세요 (0) | 2026.10.08 |
|---|---|
| Windows에서 Git 설치부터 GitHub push까지 | config·init·remote·-M·-u 정리 (0) | 2026.10.07 |
| IntelliJ Java 버전 안 맞을 때 확인할 곳 | Project SDK·Gradle JVM·JAVA_HOME (0) | 2026.10.06 |
| 개발자 노트북 RAM 16GB vs 32GB | 내가 32GB를 고른 이유 (0) | 2026.10.04 |
| 새 Windows 개발 노트북 세팅 순서 | JDK, IntelliJ, Git, Spring Boot까지 (0) | 2026.10.03 |