자바의 Records란?
Java의 Records(레코드) 는 Java 14에서 프리뷰 기능으로 처음 도입되었고, Java 16에서 정식 기능으로 추가됨.
레코드는 특정 데이터를 저장하기 위한 객체를 간단히 만들 수 있도록 설계된 새로운 유형의 클래스.
일반적으로 데이터 저장용으로만 사용하는 클래스에서 Boilerplate Code(반복되는 코드)를 줄여주는 것이 핵심 기능.
기존 방식 vs. Records 방식
기존 방식: 일반 클래스 사용
데이터를 저장하는 단순한 객체를 만들 때도 여러 가지 메서드와 필드를 정의해야 함.
예제: Employee 클래스 (기존 방식)
public class Employee {
private final String name;
private final int employeeNumber;
// 생성자
public Employee(String name, int employeeNumber) {
this.name = name;
this.employeeNumber = employeeNumber;
}
// Getter 메서드
public String getName() {
return name;
}
public int getEmployeeNumber() {
return employeeNumber;
}
// toString() 메서드
@Override
public String toString() {
return "Employee{name='" + name + "', employeeNumber=" + employeeNumber + "}";
}
// equals() & hashCode() 메서드
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return employeeNumber == employee.employeeNumber &&
name.equals(employee.name);
}
@Override
public int hashCode() {
return Objects.hash(name, employeeNumber);
}
}
문제점:
- 단순한 데이터 저장용 클래스임에도 불구하고 50줄 이상의 코드가 필요.
- toString(), equals(), hashCode() 등의 반복적인 코드(Boilerplate Code)가 많음.
새로운 방식: Java Records 사용
위와 같은 클래스를 한 줄의 코드로 정의 가능!
public record Employee(String name, int employeeNumber) {}
기본적으로 포함되는 기능들:
- private final 필드 자동 생성
- getter() 메서드 자동 생성 (get 접두사 없이 name(), employeeNumber() 형태)
- toString(), equals(), hashCode() 자동 생성
- Canonical Constructor(기본 생성자) 자동 제공
Records의 주요 특징
1. 불변 객체 (Immutable)
- 레코드는 불변 객체 이므로, 생성된 후 필드 값을 변경할 수 없음.
- 따라서 Setter 메서드가 자동으로 생성되지 않음.
Employee emp = new Employee("John", 12345);
emp.name = "Mike"; // 컴파일 에러 ❌
대신 새 객체를 생성해야 변경 가능:
Employee emp2 = new Employee("Mike", emp.employeeNumber());
2. 자동 생성되는 메서드
- toString() 메서드:
Employee emp = new Employee("John", 12345);
System.out.println(emp);
// 출력: Employee[name=John, employeeNumber=12345]
- equals() & hashCode():
Employee emp1 = new Employee("John", 12345);
Employee emp2 = new Employee("John", 12345);
System.out.println(emp1.equals(emp2)); // true ✅
3. Compact Constructor (압축 생성자)
- 레코드는 기본적으로 Canonical Constructor(모든 필드를 초기화하는 생성자) 를 자동 생성.
- 하지만 특정 로직을 추가하려면 Compact Constructor 사용 가능.
예제: 직원 번호가 음수인 경우 예외 발생
public record Employee(String name, int employeeNumber) {
Employee {
if (employeeNumber < 0) {
throw new IllegalArgumentException("Employee number cannot be negative");
}
}
}
이렇게 하면 다음 코드 실행 시 예외 발생
Employee emp = new Employee("John", -5); // IllegalArgumentException 발생 ❌
4. Records의 제약 사항
✔ 가능한 것
✅ static 필드 및 static 메서드 추가 가능
✅ 메서드 정의 가능
✅ implements를 사용해 인터페이스 구현 가능
✅ Compact Constructor 사용 가능
❌ 불가능한 것
🚫 extends 사용 불가능 (다른 클래스를 상속할 수 없음)
🚫 private 또는 non-static 필드 추가 불가능
🚫 mutable 필드 추가 불가능
🚫 abstract 메서드 선언 불가능
인터페이스 구현 예제
public record Employee(String name, int employeeNumber) implements Comparable<Employee> {
@Override
public int compareTo(Employee other) {
return Integer.compare(this.employeeNumber, other.employeeNumber);
}
}
Records는 언제 사용할까?
✅ 사용하기 좋은 경우
- 데이터 전달 객체 (DTO)
- 데이터 저장 클래스 (단순 값 저장)
- 데이터베이스 엔티티
- API 응답 모델
❌ 사용하지 않는 것이 좋은 경우
- 도메인 객체 (비즈니스 로직 포함)
- 필드를 변경해야 하는 경우 (Setter가 필요할 때)
- 복잡한 로직이 포함된 클래스
결론
- Java Records를 사용하면 데이터 저장용 클래스를 매우 간결하게 정의 가능.
- 불변성을 유지하면서도 자동으로 필요한 메서드를 제공하여 코드 품질 향상.
- 기능 확장이 필요하지 않은 단순한 데이터 저장 클래스에 적합.
- Lombok을 사용하지 않아도 @Data와 같은 효과를 얻을 수 있음.
자주 묻는 질문 (Q&A)
Q1. 왜 레코드는 getter에 "get"이 붙지 않나요?
레코드는 기존 객체의 대체재가 아니라, 단순히 데이터를 전달하는 값 객체(Value Object) 개념에 초점이 맞춰져 있다.
✅ 일반적인 Java 클래스에서는 getName(), getEmployeeNumber()와 같은 게터 메서드를 사용하지만,
✅ Records에서는 get이 생략되고 name() 또는 employeeNumber() 같은 방식으로 필드 값을 가져올 수 있다.
public record Employee(String name, int employeeNumber) {}
Employee emp = new Employee("John Doe", 12345);
System.out.println(emp.name()); // John Doe (getter 메서드처럼 작동)
System.out.println(emp.employeeNumber()); // 12345
이유
1️⃣ Records는 일반적인 객체 모델을 따르지 않으며, 단순한 데이터 컨테이너 역할을 수행한다.
2️⃣ Boilerplate 코드를 줄이기 위한 목적이므로, 불필요한 get 접두사를 생략하여 가독성을 높임.
3️⃣ Java 언어 자체의 설계 철학에 맞게, Records는 객체보다는 데이터 구조에 가깝게 설계됨.
Q2. Lombok 대신 레코드를 사용하면 되나요?
Lombok과 Records는 비슷한 기능을 제공하지만, 사용 목적과 장점이 다르다.
👉 둘 중 어떤 것을 선택할지 고민된다면 상황에 따라 적절한 선택이 필요하다.
비교 항목 | Lombok | Records |
사용 목적 | 일반 객체의 Boilerplate 코드 감소 | 데이터 저장 및 전달용 객체 생성 |
주요 기능 | @Data, @Builder, @Getter, @Setter 등 다양한 기능 지원 | toString(), equals(), hashCode() 자동 생성 |
의존성 | Lombok 라이브러리 설치 필요 | JDK 16 이상에서 기본 제공 |
가독성 | Lombok 어노테이션으로 코드가 줄어듦 | 가장 간결한 문법 (1줄로 클래스 생성 가능) |
확장성 | 일반 클래스처럼 필드 추가, Setter 사용 가능 | 불변 객체이므로 Setter 없음 |
사용 가능 버전 | Java 8 이상 | Java 16 이상 |
📌 언제 Lombok을 사용할까?
✅ Setter가 필요할 때
✅ 객체가 비즈니스 로직을 포함하는 경우
✅ 추가적인 빌더 패턴(@Builder)이 필요할 때
📌 언제 Records를 사용할까?
✅ 단순한 데이터 저장과 전달이 목적일 때
✅ 코드 가독성을 높이고 유지보수를 편하게 하고 싶을 때
✅ 불변 객체(Immutable Object)가 필요할 때
🔹 예제: Lombok 사용
import lombok.Data;
@Data
public class Employee {
private final String name;
private final int employeeNumber;
}
🔹 예제: Records 사용
public record Employee(String name, int employeeNumber) {}
👉 같은 기능이지만, Records는 더 간결한 코드를 제공함.
👉 하지만, Setter가 필요하거나 객체의 상태를 변경해야 한다면 Lombok이 더 적합할 수 있음.
Q3. 레코드를 도메인 객체에 사용할 수 있을까요?
🚫 권장하지 않음!
도메인 객체(Domain Object)란, 비즈니스 로직을 포함하는 객체이다.
즉, 단순히 데이터를 저장하는 것뿐만 아니라, 특정 기능과 규칙을 수행해야 한다.
✅ Records는 데이터 저장 및 전달을 위한 용도이므로, 비즈니스 로직을 포함하는 도메인 객체로 사용하기에는 적절하지 않다.
✅ 도메인 객체에는 일반 클래스 또는 Lombok을 사용하는 것이 더 적절하다.
📌 왜 Records를 도메인 객체로 사용하면 안 될까?
1️⃣ Setter가 없어 필드 값을 변경할 수 없음
→ 도메인 객체에서는 상태 변경이 필요한 경우가 많음 (예: 사용자의 점수 증가, 주문 상태 변경 등).
2️⃣ 비즈니스 로직을 추가하기 어려움
→ Records는 단순한 데이터 전달을 위해 설계되었으므로, 메서드를 추가하는 것이 비효율적임.
3️⃣ 도메인 객체는 상태를 유지하는 것이 중요
→ Records는 기본적으로 불변(Immutable)하므로, 상태 변경이 필요한 객체로 사용하기 어려움.
🔹 예제: 잘못된 사용 (Records를 도메인 객체로 사용)
public record Order(String orderId, String status) {
public void changeStatus(String newStatus) { // 불가능한 설계
this.status = newStatus; // ERROR ❌ (Setter 불가)
}
✅ 대신 일반 클래스를 사용하는 것이 더 적절하다.
🔹 예제: 일반 클래스로 도메인 객체 구현
public class Order {
private String orderId;
private String status;
public Order(String orderId, String status) {
this.orderId = orderId;
this.status = status;
}
public void changeStatus(String newStatus) {
this.status = newStatus; // 상태 변경 가능 ✅
}
public String getStatus() {
return status;
}
}
📌 언제 Records를 사용하고, 언제 일반 클래스를 사용할까?
사용 사례 | Records 사용 | 일반 클래스 사용 |
데이터 저장 | ✅ DTO, API 응답 객체 | ❌ |
데이터 변경 (Setter 필요) | ❌ | ✅ 가능 |
비즈니스 로직 포함 | ❌ | ✅ 가능 |
상태 유지 | ❌ (Records는 Immutable) | ✅ 가능 |
불변 객체가 필요할 때 | ✅ (Setter 없음) | ❌ |
🔹 Records는 DTO(Data Transfer Object)에 적합하지만,
🔹 비즈니스 로직이 필요한 도메인 객체에는 적합하지 않음.
🔥 정리
질문 | 답변 요약 |
Q1. 왜 레코드는 getter에 "get"이 붙지 않나요? | Records는 기존 객체 모델과 다르며, 데이터 저장용으로 설계되었기 때문에 불필요한 get을 생략하여 간결한 문법을 제공함. |
Q2. Lombok 대신 레코드를 사용하면 되나요? | 상황에 따라 다름. 간결한 코드가 필요하면 Records, 추가 기능이 필요하면 Lombok 선택. |
Q3. 레코드를 도메인 객체에 사용할 수 있을까요? | 권장하지 않음. 도메인 객체는 비즈니스 로직을 포함하므로 일반 클래스를 사용하는 것이 적절함. |
✅ Records는 단순한 데이터 저장 및 전달용으로 사용하면 매우 유용하지만, 일반 클래스의 완전한 대체는 아니다.
✅ 상황에 맞게 적절한 도구(Records, Lombok, 일반 클래스)를 선택하는 것이 중요하다!
'Web Programming Language > JAVA' 카테고리의 다른 글
BitSet 클래스 (0) | 2025.02.17 |
---|---|
DTO와 VO (0) | 2025.02.12 |
orElse vs orElseGet 차이점 (0) | 2025.02.12 |
JAVA) print, printf, println 차이점 (0) | 2021.03.11 |
JAVA) Java 프로그램 실행 구조 (0) | 2020.11.18 |
댓글