Java 16에서 정식으로 도입된 Record는 불변(immutable) 데이터 객체를 생성하는 혁신적인 방법을 제공합니다. 이 글에서는 Record의 핵심 개념부터 실전 활용까지 상세히 알아보겠습니다.
1. Record의 기본 개념과 특징 🎯
Records는 불변 데이터를 담는 투명한 데이터 캐리어를 선언하는 새로운 종류의 클래스입니다.
// 전통적인 방식
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
// getters, equals(), hashCode(), toString() 구현 필요
}
// Record 사용
public record Point(int x, int y) {} // 이게 전부입니다! 😎
2. Record의 자동 생성 기능 🚀
Record는 다음 요소들을 자동으로 생성합니다:
public record Customer(String name, String email, LocalDate birthDate) {
// 컴파일러가 자동으로 생성하는 것들:
// 1. private final 필드
// 2. public 생성자
// 3. public getter 메소드
// 4. equals()
// 5. hashCode()
// 6. toString()
}
// 사용 예시
Customer customer = new Customer("John Doe", "john@example.com",
LocalDate.of(1990, 1, 1));
System.out.println(customer.name()); // getter 호출
System.out.println(customer); // toString() 자동 구현
3. Record의 고급 기능 💡
3.1 컴팩트 생성자 (Compact Constructor)
public record Rectangle(double length, double width) {
// 컴팩트 생성자로 유효성 검사
public Rectangle { // 매개변수 목록 생략!
if (length <= 0 || width <= 0) {
throw new IllegalArgumentException("길이와 너비는 양수여야 합니다");
}
}
}
3.2 인스턴스 메소드 추가
public record Circle(double radius) {
// 추가 메소드 정의 가능
public double area() {
return Math.PI * radius * radius;
}
public double circumference() {
return 2 * Math.PI * radius;
}
}
4. 실전 활용 패턴 ⚡
4.1 DTO(Data Transfer Object) 패턴
// API 응답을 위한 Record
public record UserResponse(
Long id,
String username,
String email,
LocalDateTime lastLoginAt
) {
// 정적 팩토리 메소드
public static UserResponse from(User user) {
return new UserResponse(
user.getId(),
user.getUsername(),
user.getEmail(),
user.getLastLoginAt()
);
}
}
4.2 중첩 Record
public record OrderDetails(
Long orderId,
Customer customer,
List<OrderItem> items,
PaymentInfo payment
) {
// 중첩 records
public record OrderItem(
String productName,
int quantity,
BigDecimal price
) {}
public record PaymentInfo(
String method,
BigDecimal amount,
LocalDateTime paidAt
) {}
}
5. Record의 제한사항과 주의점 ⚠️
public record Employee(String name, double salary) {
// ❌ 불가능: 가변 필드 선언
private String department; // 컴파일 에러!
// ❌ 불가능: 상속
// extends AnotherClass // 컴파일 에러!
// ✅ 가능: 인터페이스 구현
implements Serializable {}
}
5.1 컬렉션 필드 처리
public record Team(String name, List<String> members) {
// 방어적 복사를 통한 불변성 보장
public Team {
members = List.copyOf(members); // 불변 리스트로 변환
}
// 커스텀 getter로 내부 데이터 보호
public List<String> members() {
return List.copyOf(members); // 복사본 반환
}
}
6. 성능과 메모리 최적화 🔍
public class RecordPerformanceTest {
public static void main(String[] args) {
// Record vs Traditional Class 성능 비교
long start = System.nanoTime();
for (int i = 0; i < 1_000_000; i++) {
Point p = new Point(i, i);
}
long recordTime = System.nanoTime() - start;
start = System.nanoTime();
for (int i = 0; i < 1_000_000; i++) {
TraditionalPoint p = new TraditionalPoint(i, i);
}
long classTime = System.nanoTime() - start;
System.out.println("Record 생성 시간: " + recordTime);
System.out.println("Traditional 클래스 생성 시간: " + classTime);
}
}
7. 실제 프로젝트 적용 예시 💼
7.1 Spring Boot와 함께 사용
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
record UserCreateRequest(
@NotNull String username,
@Email String email,
@NotBlank String password
) {}
@PostMapping
public ResponseEntity<UserResponse> createUser(
@Valid @RequestBody UserCreateRequest request
) {
User user = userService.createUser(
request.username(),
request.email(),
request.password()
);
return ResponseEntity.ok(UserResponse.from(user));
}
}
7.2 JPA와 함께 사용
public record ProductProjection(
String name,
BigDecimal price,
int stockQuantity
) {
// JPA 프로젝션으로 사용
public static interface ProductProjectionInterface {
String getName();
BigDecimal getPrice();
int getStockQuantity();
}
public static ProductProjection from(
ProductProjectionInterface projection
) {
return new ProductProjection(
projection.getName(),
projection.getPrice(),
projection.getStockQuantity()
);
}
}
결론 📝
Java Record는 불변 데이터 객체를 만드는 강력하고 간결한 방법을 제공합니다. 주요 이점:
- 보일러플레이트 코드 제거
- 불변성 보장
- 명확한 데이터 중심 클래스 표현
- 간결하고 가독성 높은 코드
사용 권장 사례 ✅
- DTO 패턴
- 값 객체(Value Objects)
- API 응답/요청 모델
- 데이터 프로젝션
주의 사항 ⚠️
- 가변 데이터가 필요한 경우는 피하기
- 상속이 필요한 경우는 전통적인 클래스 사용
- 컬렉션 필드는 방어적 복사 고려
Records는 Java의 데이터 처리를 현대화하는 중요한 기능입니다. 적절한 상황에서 활용하면 코드 품질과 생산성을 크게 향상시킬 수 있습니다.
‼️ Pro Tip: Records는 불변성을 보장하지만, 참조하는 객체의 불변성까지는 보장하지 않습니다. 컬렉션이나 가변 객체를 다룰 때는 추가적인 방어 로직이 필요할 수 있습니다!
'개발일지' 카테고리의 다른 글
Java Reflection API 개발일지: 동적 프로그래밍의 실전 여행 🔍 (0) | 2024.12.11 |
---|---|
Java 개발일지: CompletableFuture로 구현하는 비동기 프로그래밍 여정 📘 (0) | 2024.12.10 |
Java Optional: 우아한 null 처리의 완벽 가이드 ✨ (0) | 2024.12.10 |
Java Stream API 마스터하기: 함수형 프로그래밍의 강력함 🌊 (0) | 2024.12.10 |
Java String Pool의 내부 동작 원리: 메모리 최적화와 문자열 관리 심층 분석 📘 (0) | 2024.12.10 |