728x90
반응형
[JAVA] Record 🎥
Record를 DTO로 사용하는 이유
1️⃣ Record
Record는 Java 16에서 정식 도입된 새로운 형태의 클래스입니다.
데이터를 단순히 저장하고 전달하기 위한 용도로 설계되어 불변(Immutable) 특성을 기본으로 가집니다.
✅ 주요 특징
- 모든 필드는 `final`이며, 객체 생성 후 값 변경 불가
- 즉, 불변 객체 → 내부 필드값 불변성 보장 → 유지보수 이점
- 컴파일 시 자동으로 다음 메서드 생성
→ `constructor`, `getter`, `equals()`, `hashCode()`, `toString()` - 보일러플레이트 코드 제거 (코드 간결성↑, 유지보수성↑)
- 스레드 안전성 확보 (불변 객체이므로 동시 접근 안전)
public record MemberDto(String name, String email {}
2️⃣ DTO vs Record
| 항목 | DTO | Record |
| 불변성 | 개발자가 직접 `final` 선언 필요 | 기본적으로 불변 |
| 코드 양 | 보일러플레이트 코드 많음 | 매우 간결함 |
| 생성자 | 직접 작성 | 자동 생성 |
| 메서드 | 직접 작성 (`getter`, `equals`, `hashCode`, `toString`) | 자동 생성 |
| 사용 목적 | 계층 간 데이터 전달 | 불변 데이터 전달 (DTO로 활용 가능) |
[DTO]
// 기존 클래스 기반 DTO
public class MemberDto {
private final String name;
private final String email;
public MemberDto(String name, String email) {
this.name = name;
this.email = email;
}
public String getName() {
return name;
}
public String getEamil() {
return email;
}
}
+) equals(), hashCode(), toString() 등
- 보일러플레이트 코드 많음
- 개념 : 최소한의 수정으로 재사용성 높이는 코드
- ex) 생성자, getter 등
- 필드값 변경 시 코드 수정 필요
- 모든 보일러플레이트 코드 수정 ⬇️ [age 필드 추가]
// 기존 클래스 기반 DTO public class MemberDto { private final String name; private final String email; private final int age; public MemberDto(String name, String email, int age) { this.name = name; this.email = email; this.age = age; } public String getName() { return name; } public String getEamil() { return email; } public int getAge() { return age; } } +) equals(), hashCode(), toString() 등
이러한 단점을 한 번에 해결하는 [Record]
// Record. 생성자, getter, hashCode(), equals(), toString() 자동 완성
public record MemberDto(String name, String email, int age) {}
- 생성자, `getter`, `hashCode()`, `equals()`, `toString()` 등 자동 완성
- 즉, 보일러플레이트 코드 자동 완성
3️⃣ 사용법
📌 선언
// Record
public record MemberDto(String name, String email, int age) {}
- 선언과 동시에 필드 정의
- 이때, 필드값 추가 시 에러 발생
- 예외로 `static`으로 된 정적 필드는 생성 가능
// Record public record MemberDto(String name, String email, int age) { private static final String value = "somthing"; }
📌 필드 접근
Member member = new Member("윤망꼬", "test@example.com");
String name = member.name(); // 윤망꼬
String email = member.email(); // test@example.com
- 일반 객체처럼 사용
- 필드값 접근 시 `getName()` 형태가 아니라 `name()`으로 직접 접근
📌 메서드 재정의
public record Member(String name, String email) {
@Override
public String toString() {
return "귀여운 " + name + "입니다.";
}
}
- 자동 정의 되지만, 필요한 메서드만 재정의 가능
📌 생성자
- 생성자에서 사용하는 파라미터 = 레코드 필드
→ `this.name = name` 과 같은 초기화 로직 생략 가능
→ 파라미터 생략 가능
4️⃣ getXXX가 아닌 이유
- record는 일반 객체의 문제점을 해결하기 위해 탄생된 것이 아니므로 독립적으로 봐야함
- 즉, record가 일반 객체 컨벤션을 따를 필요가 없음
5️⃣ Lombok과 비교
| 항목 | Record | Lombok |
| 의존성 | JDK 16 이상 기본 제공 | 외부 라이브러리 필요 |
| 코드 간결성 | ✅ 가장 간결 | `@Data`등으로 비교적 간결 |
| 기능 | 데이터 전달에 초점 | 빌더, 로깅 등 다양한 기능 제공 |
| 유연성 | 낮음 (불변 고정) | 높음 |
@Data
@AllArgsConstructer
public class MemberDto {
private final String name;
private final String email;
}
- 기능적으로 `Builder`, `SLF4J` 같은 다양한 기능 제공
- 의존성
- 외부 라이브러리이므로 설치 필요
- IDEA도 그에 맞는 플러그인 설치 필요
- 레코드는 자바 정식 스펙으로 JDK 16 이상이면 됨
public record Member(String name, String email) {}
- 간결성 좋음
- 의존성
- 자바 정식 스펙으로 JDK 16 이상이면 됨
📌 정리
Record → 간결하고 의존성 없는 프로젝트에 적합
Lombok → 유연성과 확장 기능이 필요한 프로젝트에 적합
6️⃣ Record의 한계
- 상속 불가 (extends 사용 불가)
- 모든 필드가 `final` → 값 변경 불가능
- 비즈니스 로직 포함 부적절
- Java 16 이상에서만 사용 가능
- 확장성 부족 → 복잡한 도메인 모델에 부적합
7️⃣ Record vs VO
| 구분 | Record | VO |
| 공통점 | 불변 객체, 값 기반 비교 (equals) | |
| 역할 | 단순 데이터 전달 | 도메인 내 의미 있는 값 표현 |
| 로직 포함 | ❌ | ✅ 비즈니스 규칙 포함 가능 |
| 사용 맥락 | 주로 DTO 계층 | 도메인 계층 |
📌 Record는 VO의 형태를 표현하기엔 적합하지만, 도메인 규칙을 포함하는 VO의 완전한 대체는 불가능 합니다.
8️⃣ 도메인 객체로 사용 지양
레코드 공식 문서
- 레코드는 불변 데이터를 전달하기 위한 캐리어
- 단순한 값의 집합을 표현하는 객체 지향적인 구성을 고민한다.
- 개발자가 확장 가능한 동작보다는 불변의 데이터 모델링에 집중 할 수 있도록 도와준다.
- 도메인 객체 = 데이터 + 비즈니스 로직
→ 단순히 데이터만 전달하기 위해 사용되는 record 목적성에 부합하지 않음
📌 즉, Record는 데이터만 전달하는 DTO에 적합하고, 도메인 로직이 필요한 엔터티나 VO에는 부적합합니다.
9️⃣ 요약
| 요약 | 내용 |
| 핵심 역할 | 불변 데이터 전달을 위한 간결한 클래스 |
| 장점 | 코드 간결성, 스레드 안전성, 유지보수성 |
| 한계 | 상속 불가, 비즈니스 로직 부적합 |
| 적합한 용도 | DTO, 단순 데이터 구조체 |
728x90
반응형
