record type
📌 DTO에 record를 도입해보고 싶었던 이유
개인 프로젝트를 진행하다가 문득 DTO를 정의할 때의 불편함과 불필요한 어노테이션 사용에 대해 고민이 생겼다.
특히, Java에서 Lombok을 사용하는 방식이 점점 어노테이션 지옥처럼 느껴지기 시작했다.
예를 들어, 아래처럼 일반 DTO를 작성하면:
@Getter @AllArgsConstructor @NoArgsConstructor @Setter @Builder public class UserInfoResponseDto { private Long userId; private String username; private String email; private Role role; }
클래스 위에 수많은 어노테이션이 덕지덕지 붙는다.
분명 코드 길이는 짧아졌지만,
IDE의 자동완성과 롬복에 대한 의존 때문에 구조를 이해하기 더 어려워질 수도 있다.
🤔 어노테이션에 대한 거부감
개인적으로 어노테이션으로 모든 걸 처리하는 방식은
개발자가 코드보다 프레임워크에 더 의존하게 되는 방향이라 느꼈고,
이는 객체지향적 설계에서 거리를 둔다고 생각했다.
그래서 대안을 고민하다가 Java 14부터 정식 도입된 record에 주목하게 되었다.
✅ record로 바꿔본 DTO
기존 클래스 DTO를 record로 변환하면 다음과 같다:
@Builder public record UserInfoResponseDto( Long userId, String username, String email, Role role ) {}
✨ 장점 요약
- 코드가 깔끔하고 간결하다.
- 모든 필드는 **자동으로 private final**로 선언된다.
- 기본적으로 **불변성(immutability)**을 지니므로 안정적인 설계가 가능하다.
- 생성자, equals(), hashCode(), toString() 모두 자동 생성된다.
- DTO처럼 단순 데이터 전달용 객체에 딱 맞는 구조다.
💬 Kotlin의 data class와 유사
이 구조는 코틀린의 data class와 매우 유사하다.
불변성, 자동 생성 메서드, 간결함을 제공한다는 점에서 동일하다.
자바가 이제서야 이 정도 표현력을 갖추게 된 것도 신기하고,
앞으로 DTO를 만들 때는 가급적이면 record를 활용해보려 한다.
🤨 그럼 Lombok은 이제 안 쓸 건가?
개인적으로 @Builder 하나 정도는 타협할 수 있지만,
@Getter, @Setter, @NoArgsConstructor, @AllArgsConstructor 등을
무분별하게 사용하는 건 지양하고 싶다.
Lombok은 분명 생산성을 높여주지만,
코드의 명시성과 구조를 숨기고,
프레임워크 의존도를 높인다는 점에서 아쉬운 부분이 많다.
다음 글에서는 왜 Lombok이 위험할 수 있고,
어떤 대안이 있는지를 정리해볼 생각이다.
📚 참고 문서
앞으로 Java 14 이상 환경에서는 DTO에 record를 적극 도입해보고 싶다.
불필요한 어노테이션에서 벗어나 더 읽기 쉽고 예측 가능한 코드를 작성하는 데 도움이 될 것 같다.