Java에서 Record는 데이터 클래스 작성 시 발생하는 반복적인 보일러플레이트 코드(최소한의 변경으로 여러 곳에서 반복적으로 사용되는 코드)를 줄이기 위한 해결책이다.
데이터를 담는 클래스를 만들 때마다 생성자, getter, equals(), hashCode(), toString() 등의 메서드를 반복해서 작성해야 했다. 이는 코드의 가독성을 떨어뜨리고 유지보수를 어렵게 만들었다. record
키워드는 이러한 문제를 해결하고 간결하고 명확한 데이터 객체를 쉽게 정의할 수 있도록 도입되었다.
필드 값이 불변이어야 하고, getter, equals(), hashCode(), toString() 등의 메서드를 자동으로 생성하는 경우 유리하다. 하지만 상속이 필요하거나, 복잡한 로직이 포함된 클래스를 구현할 때는 적합하지 않다. 또한 필드 변경이 불가능하므로 가변 데이터를 처리해야 하는 경우에는 다른 방식이 필요하다. 데이터 전달 객체(DTO)
나 단순한 값 객체(Value Object)
를 구현할 때 가장 적합하다.
나의 경우는 DTO를 구현하는데 반복적으로 롬복 어노테이션을 쓰지 말고 record를 도입해보자고 생각했다.
record 사용 시
public record UserDTO(Long id, String name, String email) {}
롬복 사용 시
@Getter
@AllArgsConstructor
@EqualsAndHashCode
@ToString
public class UserDTO {
private final Long id;
private final String name;
private final String email;
}
일반 Java 클래스
public class UserDTO {
private final Long id;
private final String name;
private final String email;
// 생성자, getter, equals, hashCode, toString 메서드 구현
}
record의 장점은 간결성과 불변성을 기본으로 제공한다는 것이다. 롬복은 더 유연한 커스터마이징을 허용하지만, 추가 의존성이 필요하다. 일반 Java 클래스는 완전한 제어를 제공하지만, 가장 많은 코드를 작성해야 한다.
record는 자동으로 Serializable 인터페이스를 구현하기 때문에, 별도의 코드 없이도 객체를 쉽게 직렬화할 수 있다. 이는 RESTful API에서 JSON으로의 변환이나 네트워크를 통한 데이터 전송 시 매우 유용하다. 또한, JPA나 다른 ORM 프레임워크와의 호환성도 향상된다. 하지만 커스텀 직렬화가 필요한 경우, 추가적인 처리가 필요할 수 있다.
record의 생성자 커스터마이징 기능은 유연성과 안전성을 동시에 제공한다. 기본적으로 record는 모든 필드를 초기화하는 정식 생성자를 자동으로 생성한다. 하지만 개발자가 직접 생성자를 정의하여 추가적인 로직을 포함시킬 수 있다. 이는 파라미터 유효성 검사
, 기본값 설정
, 복잡한 초기화 로직
등을 구현할 때 유용하다.
예를 들어, 이메일 형식 검증 등의 로직을 생성자에 포함시킬 수 있다. 이러한 기능은 데이터의 무결성을 보장하고, 비즈니스 로직을 캡슐화하는 데 도움을 준다. 하지만 커스텀 생성자를 사용할 때는 모든 필드를 초기화해야 하는 점에 주의해야 한다.
public record UserDTO(Long id, String name, String email) {
public UserDTO {
if (email == null || !email.matches("^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$")) {
throw new IllegalArgumentException("Invalid email format");
}
}
}
항상 관성처럼 롬복 어노테이션을 사용하고, 자바 클래스를 작성했지만 앞으로는 record
도 사용해봐야겠다.