본문 바로가기
Back-end & 알고리즘

Java 21 레코드를 DTO로 쓸 때 반드시 체크해야 할 3가지 주의점과 해결법

by CodeByJin 2026. 4. 25.
반응형

Java 21 레코드를 DTO로 쓸 때 생기는 의외의 복병과 해결책

자바 개발자로 살면서 보일러플레이트 코드 때문에 스트레스받던 시절, Java 14에서 맛보기로 나왔던 Records는 정말 구원 투수 같았습니다. 하지만 Java 21이 표준이 된 2026년 지금, 단순히 편리함만 보고 레코드를 DTO로 썼다가 예상치 못한 곳에서 뒤통수를 맞는 경우를 꽤 자주 봅니다.

 

Lombok의 @Data나 @Value를 대체할 목적으로 가볍게 도입했다가, 막상 실무 프로젝트의 복잡한 매핑이나 상속 구조에서 꼬이기 시작하면 "그냥 클래스로 쓸걸" 하는 후회가 밀려오기도 하죠. 오늘은 제가 현업에서 Java 21 레코드를 DTO로 활용하며 겪었던 시행착오와, 이를 깔끔하게 해결하는 실전 가이드를 공유해 드리겠습니다.

편리함 뒤에 숨겨진 불변성(Immutability)의 딜레마

레코드의 가장 큰 특징은 모든 필드가 final이라는 점입니다. 이게 데이터의 신뢰성을 높여주지만, 역직렬화(Deserialization)나 부분적인 데이터 수정이 필요한 라이브러리를 만날 때 문제가 터집니다.

  • JPA Entity로 사용할 수 없음: 하이버네이트 같은 ORM은 프록시 객체를 생성해야 하는데, 레코드는 final 클래스라 상속이 불가능합니다.
  • 기본 생성자의 부재: JSON 데이터를 객체로 바꿀 때 기본 생성자가 반드시 필요한 구식 라이브러리들은 레코드와 궁합이 최악입니다.
  • 부분 수정의 번거로움: 필드 하나만 바꾸고 싶어도 전체 객체를 다시 생성해야 합니다.

개인적으로 이 부분이 실무에서 가장 큰 허들이었습니다. 화면에서 넘어오는 폼 데이터를 담는 단순 DTO라면 문제가 없지만, 비즈니스 로직을 타면서 필드 값이 조금씩 변해야 하는 '상태 변화'가 잦은 구간에서는 오히려 클래스보다 코드가 더 지저분해지는 역효과가 나더군요.

자바21 레코드 DTO 활용 - 생성형 AI 이미지

Java Record를 사용할 때 상속은 아예 안 되나요?

네, 레코드는 암시적으로 java.lang.Record를 상속받으며 final로 선언되기 때문에 다른 클래스를 상속받거나 다른 클래스가 레코드를 상속받을 수 없습니다. 대신 인터페이스 구현은 가능합니다. 공통적인 기능이 필요하다면 인터페이스를 통해 다형성을 확보하는 것이 2026년 현재 가장 권장되는 방식입니다.

실전에서 바로 써먹는 레코드 DTO 전략 3단계

직접 수십 개의 API를 레코드로 전환해 보면서 정착한 루틴입니다. 무지성 도입보다는 아래 순서를 따랐을 때 유지보수 비용이 확실히 줄어들었습니다.

 

1단계: 컴팩트 생성자(Compact Constructor) 활용하기 데이터 검증 로직은 반드시 여기에 넣으세요. 레코드는 생성자 파라미터를 생략한 채 로직만 적을 수 있는데, 여기서 null 체크나 범위 검증을 하면 DTO 자체가 하나의 '검증된 성벽'이 됩니다.

 

2단계: Wither 메서드 패턴 도입 필드 하나만 바꾸고 싶을 때는 withName(String name) 같은 메서드를 직접 만들어 새 레코드를 반환하게 하세요. Lombok의 @With와 비슷한 방식인데, 수동으로 작성하는 게 귀찮아도 불변성을 지키면서 데이터를 가공하는 가장 정석적인 방법입니다.

 

3단계: JSON 매핑 최적화 Jackson 2.12 버전 이후부터는 레코드를 잘 지원하지만, 간혹 필드명이 getXXX가 아니라 xxx() 형태인 것 때문에 매핑 사고가 납니다. @JsonProperty를 적극 활용하거나 프로젝트 전반의 ObjectMapper 설정을 한 번 점검하는 과정이 꼭 필요합니다.

DTO 선택 기준: 클래스 vs 레코드

무조건 레코드가 정답은 아닙니다. 제가 프로젝트를 리딩할 때 팀원들에게 제시하는 기준표를 정리해 봤습니다.

고려 요소 Java Record (추천) Standard Class (추천)
데이터 성격 단순 전달용 (Read-only) 수정이 잦은 도메인 모델
프레임워크 호환 현대적 API (Spring 6+) Legacy, JPA Entity
구조적 유연성 인터페이스 기반 구현 복잡한 상속 계층 필요 시

표를 보면 알 수 있듯이, 외부와 소통하는 순수 데이터 객체는 레코드가 압승입니다. 하지만 내부 로직에서 얽히고설킨 엔티티 성격의 객체는 여전히 클래스가 우위에 있죠. 직접 써보니 레코드로 바꿨을 때 코드 양이 약 40% 정도 줄어드는 경험을 했습니다. 가독성 면에서는 확실히 이득입니다.

이건 직접 해보면 차이가 꽤 납니다: 팁 하나 더

레코드를 쓰면서 toString()이나 equals()를 직접 오버라이딩하는 실수를 하지 마세요. 레코드가 자동으로 생성해주는 로직은 최적화가 잘 되어 있습니다. 만약 특정 필드를 로그에서 숨겨야 한다면 그때만 toString()을 커스텀하는 것이 성능과 보안을 모두 잡는 비결입니다.

 

자바 21의 레코드는 마치 잘 짜인 도시락 통 같습니다. 칸이 딱 정해져 있어서 담기는 편하지만, 중간에 반찬을 바꾸려면 통을 새로 꺼내야 하죠. 여러분의 프로젝트가 '담기만 하는' 단계인지, '계속 요리하는' 단계인지에 따라 현명하게 선택하시길 바랍니다.

 

결국 생산성을 높이는 것은 최신 기술 자체가 아니라, 그 기술이 가로막는 지점이 어디인지를 정확히 아는 능력이니까요. 오늘 내용이 여러분의 깔끔한 코딩 생활에 작은 보탬이 되었으면 좋겠습니다.

 

DFS와 BFS 차이, 17년 차 개발자가 딱 정해드리는 상황별 선택 기준

코딩 테스트나 실무에서 알고리즘 문제를 풀다 보면 DFS(깊이 우선 탐색)와 BFS(너비 우선 탐색) 사이에서 갈등하는 순간이 꼭 오죠? 이론은 머리로 이해했는데, 막상 빈 화면을 마주하면 "여기서

byteandbit.tistory.com

반응형