<p style="text-align:justify;">본문은 요즘IT와 번역가 Jane Heo가 함께 아슈토쉬 크리슈나(Ashutosh Krishna)의 글 <<a href="https://blog.ashutoshkrris.in/dto-vs-record-in-java-which-should-you-use"><u>DTO vs Record in Java: Which Should You Use?</u></a>>을 번역한 글입니다. 필자인 아슈토쉬 크리슈나는 쏘트웍스(Thoughtworks) 인도지사에서 애플리케이션 개발자로 일하며, 해쉬노드(Hashnode), 미디엄(Medium) 등에서 개발자들을 위한 글을 연재 중입니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;">필자에게 허락을 받고 번역했으며, 글에 포함된 각주(*표시)는 ‘번역자주’입니다.</p><div class="page-break" style="page-break-after:always;"><span style="display:none;"> </span></div><p style="text-align:justify;">오늘은 Java 개발에서 DTO(Data Transfer Object, 데이터 전송 객체)와 Record(레코드)의 차이점과 사용 사례를 살펴보려고 합니다. 우리는 종종 Java 애플리케이션의 다양한 계층 간 또는 서비스 간에 데이터를 전송해야 합니다. 이를 위해 DTO(Data Transfer Object, 데이터 전송 객체)를 사용합니다. DTO는 복잡한 동작이나 로직 없이 데이터를 담기 위한 간단한 객체입니다. 이 객체의 역할은 데이터를 묶어서 필요한 곳에 전달하는 것이죠.</p><p style="text-align:justify;"> </p><p style="text-align:justify;">그런데 Java 14에서 새로운 기능인 ‘Record’가 도입되었습니다. Record는 DTO와 마찬가지로 데이터를 담는 데 초점을 맞춘 특별한 클래스 유형입니다. 중요한 차이점은 Record는 우리가 반복적으로 해야 하는 많은 작업을 자동으로 처리해 준다는 것입니다. 예를 들어, *Getter(게터)를 자동으로 생성하고, 동등성 검사나 toString() 메서드도 알아서 처리해 줍니다. 이 기능은 Java 16에서 완전히 사용 가능해지면서 Record는 Java에서 데이터를 처리하는 깔끔하고 현대적인 방법이 되었습니다.</p><p style="text-align:justify;"><span style="color:#999999;">*데이터를 가져오는 메서드</span></p><p style="text-align:justify;"> </p><p style="text-align:justify;">그렇다면 왜 DTO와 Record를 비교하는 걸까요? 그 이유는 둘 다 데이터를 운반하는 비슷한 목적을 가지고 있기 때문입니다. 하지만 Java가 계속 발전함에 따라 언제 DTO를 사용하고 언제 Record를 사용해야 하는지 이해하는 것이 중요합니다. 이 글에서는 DTO와 Record의 차이점을 알아보고, 특히 현대적인 Java 애플리케이션에서 어떤 것이 더 적합한지 결정하는 데 도움을 드리고자 합니다.</p><p style="text-align:justify;"> </p><figure class="image image_resized" style="width:100%;"><img src="https://yozm.wishket.com/media/news/2814/caspar-camille-rubin-fPkvU7RDmCo-unsplash__1_.jpg"><figcaption><출처: <a href="https://unsplash.com/ko/%EC%82%AC%EC%A7%84/%EC%BB%B4%ED%93%A8%ED%84%B0-%EC%96%B8%EC%96%B4-%EC%BD%94%EB%93%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80%EA%B0%80-%EC%9E%88%EB%8A%94-macbook-pro-fPkvU7RDmCo?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash"><u>Unsplash</u></a>, <a href="https://unsplash.com/ko/@casparrubin?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash"><u>Caspar Camille Rubin</u></a>></figcaption></figure><p style="text-align:justify;"> </p><h3 style="text-align:justify;"><strong>DTO란?</strong></h3><p style="text-align:justify;">DTO(Data Transfer Object)는 애플리케이션의 다양한 부분 간에 데이터를 이동시키는 데 사용되는 간단한 Java 객체입니다. 애플리케이션의 계층 간에 데이터를 운반하는 컨테이너라고 생각할 수 있죠. 예를 들어, 웹 애플리케이션에서 DTO는 서비스 계층에서 컨트롤러로 데이터를 전송하거나, 컨트롤러에서 뷰 계층으로 데이터를 전달할 때 사용될 수 있습니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;">DTO는 애플리케이션의 다른 부분들을 서로 분리하는 데 도움을 주어, 코드가 더 체계적이고 유지 관리하기 쉬워집니다. 일반적으로 DTO에는 비즈니스 로직이나 복잡한 동작이 없습니다. 그저 데이터를 담고 있는 역할만 합니다.</p><p style="text-align:justify;"> </p><h4 style="text-align:justify;"><strong>DTO는 어떻게 구현될까?</strong></h4><p style="text-align:justify;">DTO는 보통 일반적인 Java 클래스로 구현됩니다. 일반적인 DTO는 다음과 같은 요소를 포함합니다.</p><p style="text-align:justify;"> </p><ul><li style="text-align:justify;">데이터를 담는 private 필드</li><li style="text-align:justify;">데이터에 접근하고 수정하기 위한 Getter와 Setter 메서드</li><li style="text-align:justify;">객체를 생성하는 생성자(Constructor)</li><li style="text-align:justify;">객체를 비교하거나 출력할 때 유용한 toString(), hashCode(), equals() 메서드를 재정의(Override)</li></ul><p style="text-align:justify;"> </p><p style="text-align:justify;">다음은 UserDTO 클래스의 예입니다:</p><pre><code class="language-java">import java.util.Objects; public class UserDTO { private String name; private int age; private String email; // 생성자 public UserDTO(String name, int age, String email) { this.name = name; this.age = age; this.email = email; } // Getter와 Setter public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } // toString() 메서드 재정의 @Override public String toString() { return "UserDTO{" + "name='" + name + '\'' + ", age=" + age + ", email='" + email + '\'' + '}'; } // 객체 비교를 위해 equals() 메서드 재정의 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; UserDTO userDTO = (UserDTO) o; return age == userDTO.age && Objects.equals(name, userDTO.name) && Objects.equals(email, userDTO.email); } // hashCode() 메서드 재정의 @Override public int hashCode() { return Objects.hash(name, age, email); } </code></pre><p style="text-align:justify;"> </p><p style="text-align:justify;">이 UserDTO 클래스는 사용자에 대한 정보를 담고 있습니다: 이름, 나이, 이메일과 같은 정보입니다. 또한 두 개의 UserDTO 객체를 비교할 수 있는 equals() 메서드, 고유한 해시 코드를 생성하는 hashCode() 메서드, 그리고 읽기 쉽도록 출력하는 toString() 메서드와 같은 기본 기능도 제공합니다.</p><p style="text-align:justify;"> </p><blockquote><p style="text-align:justify;">*Lombok(롬복) 같은 도구를 사용하면 반복적인 코드를 직접 작성하지 않고도 완전한 기능을 갖춘 DTO를 만들 수 있습니다. 하지만 Record는 기본적으로 불변성(immutability)을 가지며, 반복적인 코드를 제거하는 또 다른 방식을 제공합니다.<br><span style="color:#999999;">*Java 코드를 더 간단하게 만들어주는 자동화 도구</span></p></blockquote><p style="text-align:justify;"> </p><p style="text-align:justify;">UserDTO를 사용하는 방법은 다음과 같습니다:</p><pre><code class="language-java">public class UserDTOUsageExample { public static void main(String[] args) { UserDTO user = new UserDTO("Ashutosh", 25, "ashutosh@example.com"); // 데이터 접근 System.out.println(user.getName()); System.out.println(user.getAge()); System.out.println(user.getEmail()); // toString() 메서드 사용 System.out.println(user); // 객체 비교 UserDTO anotherUser = new UserDTO("Vishakha", 22, "vishakha@example.com"); System.out.println(user.equals(anotherUser)); } }</code></pre><p style="text-align:justify;"> </p><p style="text-align:justify;">출력:</p><pre><code class="language-java">Ashutosh 25 ashutosh@example.com UserDTO{name='Ashutosh', age=25, email='ashutosh@example.com'} false</code></pre><p style="text-align:justify;"> </p><p style="text-align:justify;"> </p><h3 style="text-align:justify;"><strong>Record란?</strong></h3><p style="text-align:justify;">Record는 Java 14에서 미리보기 기능으로 도입되었고, Java 16에서 정식으로 출시된 특별한 유형의 클래스입니다. Record는 불변(Immutable) 데이터를 간결하고 읽기 쉽게 담는 데 초점을 맞추고 있으며, 기존 클래스(DTO 등)에서 필요했던 반복적인 코드를 많이 줄여줍니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;">DTO에서는 생성자, Getter, equals(), hashCode(), toString() 메서드를 직접 작성해야 합니다. 하지만 Record를 사용하면 Java가 이 모든 것들을 자동으로 생성해 줍니다. 덕분에 간단한 불변 객체, 즉 데이터만 담는 역할을 하는 객체를 만들 때 매우 유용합니다.</p><p style="text-align:justify;"> </p><h4 style="text-align:justify;"><strong>주요 특징</strong></h4><ul><li style="text-align:justify;">불변성: 일반적으로 변경할 수 있는 DTO와는 다르게 한 번 Record를 만들면 그 데이터를 변경할 수 없습니다.</li><li style="text-align:justify;">간결한 문법: 필드만 선언하면 Java가 자동으로 생성자, Getter, equals(), hashCode(), toString() 메서드를 만들어 줍니다.</li><li style="text-align:justify;">Setter 없음: Record는 불변 객체이기 때문에 Setter 메서드를 제공하지 않습니다.</li></ul><p style="text-align:justify;"> </p><p style="text-align:justify;">다음은 앞서 보았던 UserDTO와 동일한 사용자 데이터를 담는 UserRecord를 Record로 만드는 예입니다:</p><pre><code class="language-java">public record UserRecord(String name, int age, String email) { }</code></pre><p style="text-align:justify;"> </p><p style="text-align:justify;">이게 끝입니다. 단 한 줄만으로 Java는 자동으로 다음을 생성해 줍니다:</p><p style="text-align:justify;"> </p><ul><li style="text-align:justify;">생성자: new UserRecord(String name, int age, String email)</li><li style="text-align:justify;">각 필드에 대한 Getter 메서드: name(), age(), email()</li><li style="text-align:justify;">두 개의 UserRecord 객체를 비교하는 equals() 메서드</li><li style="text-align:justify;">고유한 해시 코드를 생성하는 hashCode() 메서드</li><li style="text-align:justify;">문자열 표현을 반환하는 toString() 메서드: 예를 들어, UserRecord[name=Ashutosh, age=25, email=ashutosh@example.com] 같은 형태로 출력됩니다.</li></ul><p style="text-align:justify;"> </p><p style="text-align:justify;">UserRecord를 사용하는 방법은 다음과 같습니다:</p><pre><code class="language-java">public class UserRecordUsageExample { public static void main(String[] args) { UserRecord user = new UserRecord("Ashutosh", 25, "ashutosh@example.com"); // 데이터 접근 System.out.println(user.name()); System.out.println(user.age()); System.out.println(user.email()); // toString() 메서드 사용 System.out.println(user); // 객체 비교 UserRecord anotherUser = new UserRecord("Vishakha", 22, "vishakha@example.com"); System.out.println(user.equals(anotherUser)); } }</code></pre><p style="text-align:justify;"> </p><p style="text-align:justify;">출력:</p><pre><code class="language-java">Ashutosh 25 ashutosh@example.com UserRecord[name=Ashutosh, age=25, email=ashutosh@example.com] false</code></pre><p style="text-align:justify;"> </p><p style="text-align:justify;">UserRecord를 사용하면 Getter, 생성자, equals(), hashCode(), toString() 메서드를 일일이 작성할 필요가 없습니다. Record는 불변 객체를 간단하고 깔끔하게 만들 방법을 제공합니다.</p><p style="text-align:justify;"> </p><h4 style="text-align:justify;"><strong>왜 Record를 사용할까?</strong></h4><ul><li style="text-align:justify;">불필요한 코드 감소: 반복적으로 작성해야 하는 Getter 메서드나 equals() 메서드 같은 코드를 쓸 필요가 없습니다.</li><li style="text-align:justify;">불변성 보장: 객체가 생성된 후 데이터를 변경할 수 없게 만들어, 다중 스레드 환경에서 더 안전하게 사용할 수 있습니다.</li><li style="text-align:justify;">명확한 의도: Record를 사용하면 해당 객체가 추가적인 동작이나 로직 없이 데이터를 전달하기 위한 것임을 분명하게 알 수 있습니다.</li></ul><p style="text-align:justify;"> </p><p style="text-align:justify;"> </p><h3 style="text-align:justify;"><strong>DTO와 Record 비교하기</strong></h3><p style="text-align:justify;">이제 DTO와 Record에 대해 알아봤으니, 어떤 차이가 있는지 비교해 보겠습니다.</p><p style="text-align:justify;"> </p><h4 style="text-align:justify;"><strong>불변성(Immutability)</strong></h4><p style="text-align:justify;">Record는 기본적으로 불변성을 가지며, 한 번 생성된 Record 인스턴스의 데이터는 변경할 수 없습니다. 이러한 불변성 덕분에 데이터는 일관성을 유지하며, 추가적인 코드 없이도 스레드 안전(Thread-safe)합니다. 예를 들어, UserRecord에서는 name, age, email 필드가 생성될 때만 설정되고, 이후에는 수정할 수 없습니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;">반면, DTO는 일반적으로 가변성을 가지며, 객체가 생성된 후에도 필드를 변경할 수 있습니다. DTO를 불변으로 만들려면 Setter 메서드를 사용하지 않거나, final 필드를 사용하여 신중하게 설계해야 합니다. 불변성을 구현하는 방식은 다음과 같습니다.</p><p style="text-align:justify;"> </p><ul><li style="text-align:justify;">Record: 기본적으로 불변성을 보장합니다.</li><li style="text-align:justify;">DTO: 불변성을 유지하려면 추가적인 코드 설계가 필요하며, 복잡한 코드나 잠재적인 버그가 생길 수 있습니다.</li></ul><p style="text-align:justify;"> </p><pre><code class="language-java">public class ImmutabilityExample { public static void main(String[] args) { UserDTO userDTO = new UserDTO("Ashutosh", 25, "ashutosh@example.com"); userDTO.setAge(26); // DTO는 기본적으로 값 변경을 허용합니다. UserRecord userRecord = new UserRecord("Ashutosh", 25, "ashutosh@example.com"); userRecord.name = "Jane"; // 이는 컴파일 에러가 발생합니다. } }</code></pre><p style="text-align:justify;"> </p><h4 style="text-align:justify;"><strong>보일러플레이트 코드(Boilerplate Code)</strong></h4><p style="text-align:justify;">Record의 큰 장점 중 하나는 보일러플레이트 코드, 즉 반복적인 코드를 크게 줄일 수 있다는 점입니다. DTO를 사용할 때는 보통 Getter, Setter, 생성자, equals(), hashCode(), toString() 메서드를 직접 작성해야 하지만, Record는 이러한 메서드들을 자동으로 생성해 줍니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;">반대로, DTO는 직접 코드를 작성해야 합니다. Lombok 같은 도구를 사용하면 보일러플레이트 코드를 줄일 수 있지만, Record만큼 간단하지는 않습니다.</p><p style="text-align:justify;"> </p><ul><li style="text-align:justify;">Record: 생성자, Getter, equals(), hashCode(), toString()이 자동으로 생성됩니다.</li><li style="text-align:justify;">DTO: 직접 구현이 필요하거나 Lombok 같은 도구를 사용해야 합니다.</li></ul><p style="text-align:justify;"> </p><h4 style="text-align:justify;"><strong>데이터 표현 방식</strong></h4><p style="text-align:justify;">Record는 데이터를 간결하고 직관적으로 표현하는 방법을 제공합니다. Record 선언에는 필드만 포함되므로 코드가 더 깔끔하고 읽기 쉽습니다. 특히 데이터 모델이 많은 프로젝트에서 유지보수가 더 용이합니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;">예시:</p><pre><code class="language-java">// Record: 간결하고 단순한 코드 public record UserRecord(String name, int age, String email) {}</code></pre><p style="text-align:justify;"> </p><p style="text-align:justify;">반면, DTO는 코드가 더 길어집니다:</p><pre><code class="language-java">// DTO: 더 많은 코드 필요 public class UserDTO { private String name; private int age; private String email; // 생성자, Getter, Setter, toString(), equals(), hashCode()... } </code></pre><p style="text-align:justify;"> </p><p style="text-align:justify;">Record는 단순히 데이터를 전달하는 용도임을 명확하게 드러내며, DTO는 반복적인 코드나 추가 로직으로 인해 복잡해질 수 있습니다.</p><p style="text-align:justify;"> </p><h4 style="text-align:justify;"><strong>커스터마이징(Customization)</strong></h4><p style="text-align:justify;">DTO의 장점 중 하나는 커스터마이징이 용이하다는 점입니다. DTO에서는 데이터 유효성 검사, 데이터 변환 메서드 또는 비즈니스 로직을 추가할 수 있습니다(순수 DTO에서는 권장되지 않지만 가능). 예를 들어, email 필드가 유효한 형식인지 확인하는 검증 메서드를 추가할 수 있습니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;">반면, Record는 커스터마이징이 제한적입니다. Record는 가볍고 불변성을 유지하도록 설계되었기 때문에 내부 상태를 수정하거나 복잡한 로직을 쉽게 추가할 수 없습니다. 만약 데이터 객체에 커스터마이징된 동작이나 로직이 필요하다면, DTO가 더 유연한 선택입니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;">DTO에서 커스터마이징 예시:</p><pre><code class="language-java">public class UserDTO { private String email; // 이메일 형식을 검증하는 메서드 public boolean isValidEmail() { return email != null && email.contains("@"); } }</code></pre><p style="text-align:justify;"> </p><p style="text-align:justify;">Record에서는 이런 커스터마이징은 보통 Record 외부에서 처리해야 합니다. Record는 데이터를 전달하는 데만 집중하고, 검증 같은 로직은 다른 곳에서 처리하는 것이 일반적입니다.</p><p style="text-align:justify;"> </p><h4 style="text-align:justify;"><strong>함수형 프로그래밍과의 연관성</strong></h4><p style="text-align:justify;">함수형 프로그래밍의 핵심 원칙 중 하나는 불변성(Immutability)입니다. 즉, 데이터 객체가 한 번 생성된 후에는 변경되지 않아야 한다는 것입니다. Record는 기본적으로 불변이므로 함수형 프로그래밍 원칙과 잘 맞습니다. 따라서 불변 데이터 객체를 사용하고자 하는 시스템에 적합한 선택입니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;"><strong>Record:</strong></p><ul><li style="text-align:justify;">불변성에 맞게 설계되었으며, 함수형 프로그래밍이 강조하는 상태 변화 없는 데이터 구조와 잘 맞습니다.</li><li style="text-align:justify;">불변 데이터를 전달할 수 있어 예측할 수 있고, 이해하기 쉬운 코드 스타일을 촉진합니다.</li></ul><p style="text-align:justify;"> </p><p style="text-align:justify;">반면, DTO는 가변성을 가지며, 불변으로 만들려면 수동적인 설정이 필요합니다. DTO는 상태 변경이 흔한 객체 지향 프로그래밍 스타일에 더 적합합니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;"><strong>DTO:</strong></p><ul><li style="text-align:justify;">유연하여 명령형 또는 객체 지향 프로그래밍 스타일에 적합합니다.</li><li style="text-align:justify;">가변성을 사용하면 함수형 프로그래밍에서 일반적으로 권장되지 않는 부작용이 발생할 수 있습니다.</li></ul><p style="text-align:justify;"> </p><p style="text-align:justify;"> </p><h3 style="text-align:justify;"><strong>DTO와 Record는 언제 사용해야 할까?</strong></h3><p style="text-align:justify;">DTO와 Record 중 어느 것을 사용할지 결정할 때는 사용 사례, 프로젝트 요구사항, 그리고 사용하는 Java 버전에 크게 좌우됩니다.</p><p style="text-align:justify;"> </p><h4 style="text-align:justify;"><strong>DTO를 사용해야 할 때</strong></h4><p style="text-align:justify;"><strong>1. 데이터 수정이 필요한 경우</strong></p><p style="text-align:justify;">객체의 데이터를 생성 후 수정해야 할 때는 DTO가 더 적합합니다. DTO는 보통 가변적이어서 필드 값을 필요에 따라 변경할 수 있습니다. 객체의 수명 주기 동안 데이터가 계속해서 업데이트되는 상황에서 유용합니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;">예시) 웹 애플리케이션에서, 사용자 등록 양식을 통해 처음에 일부 필드가 비어 있는 상태로 UserDTO가 생성될 수 있습니다. 사용자가 프로필을 업데이트하면서 해당 UserDTO의 값들이 변경되어야 할 수도 있습니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;"><strong>2. 추가적인 동작이나 검증 로직이 필요한 경우</strong></p><p style="text-align:justify;">DTO는 검증, 변환, 또는 추가 메서드 등 맞춤형 동작을 추가하는 데 더 유연합니다. 데이터 객체가 단순히 데이터를 전달하는 것 이상으로 동작해야 할 때 예를 들어, 이메일 형식을 확인하거나 입력을 정제하는 로직이 필요할 때는 DTO가 더 적합합니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;">예시) UserDTO에 이메일 형식을 검증하는 메서드를 추가하여, 애플리케이션 계층 간에 데이터를 전달하기 전에 이메일이 유효한지 확인할 수 있습니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;"><strong>3. 이전 버전의 Java(16 이전 버전)와 호환이 필요한 경우</strong></p><p style="text-align:justify;">Java 16 이전 버전을 사용하는 프로젝트라면 Record를 사용할 수 없습니다. 이런 경우에는 기존의 DTO나 Lombok(롬복)과 같은 대안으로 코드를 간소화해야 합니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;">예시) 만약 애플리케이션이 Java 11 또는 Java 8을 지원해야 한다면 Record는 선택지가 아니므로, 기존의 DTO 방식을 유지해야 합니다.</p><p style="text-align:justify;"> </p><h4 style="text-align:justify;"><strong>Record를 사용해야 할 때</strong></h4><p style="text-align:justify;"><strong>1. 간결하고 불변성을 가진 데이터 전달 객체가 필요한 경우</strong></p><p style="text-align:justify;">Record는 가볍고 불변성을 가진 객체로 데이터를 전달해야 할 때 이상적입니다. Record는 필수적인 메서드들(생성자, Getter, equals(), hashCode(), toString())을 자동으로 생성해 주기 때문에 데이터 표현을 간결하고 효율적으로 처리할 수 있습니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;">예시) 마이크로서비스 아키텍처에서 서비스 간 데이터를 전달할 때 데이터를 수정할 필요가 없다면, UserRecord가 완벽한 선택일 수 있습니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;"><strong>2. 읽기 전용 데이터 전송이 필요한 경우</strong></p><p style="text-align:justify;">애플리케이션에서 데이터를 전달하기만 하고 수정할 필요가 없다면 Record를 사용하는 것이 좋습니다. Record는 불변성을 보장하여 데이터 일관성을 유지하기 때문에, 데이터베이스에서 서비스 계층으로 또는 서비스 간 데이터를 전달하는 데 적합합니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;">예시) Record는 데이터베이스 계층에서 REST 컨트롤러로 사용자 데이터를 전달할 때 사용할 수 있습니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;"><strong>3. 최신 Java 애플리케이션(Java 16 이상)에서</strong></p><p style="text-align:justify;">프로젝트에서 Java 16 이상을 사용하고 있다면 Record를 충분히 활용할 수 있습니다. Record는 최신 Java 애플리케이션에서 데이터 표현을 간소화하도록 설계되었으며, 기존 DTO가 가지고 있던 불필요한 반복적인 코드를 줄이는 데 도움이 됩니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;">예시) Java 17 기반의 웹 서비스에서 애플리케이션 계층 간의 데이터 전송을 위해 Record를 사용하면, 코드 베이스를 간결하고 유지 관리하기 쉽게 만들 수 있습니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;"> </p><h3 style="text-align:justify;"><strong>성능 고려 사항은?</strong></h3><p style="text-align:justify;">DTO와 Record의 성능을 비교할 때 차이는 크지 않지만, 몇 가지 중요한 요소들을 고려해야 합니다.</p><p style="text-align:justify;"> </p><h4 style="text-align:justify;"><strong>메모리 효율성</strong></h4><p style="text-align:justify;">Record는 설계상 간결하므로 DTO보다 메모리를 조금 덜 사용할 수 있습니다. 그 이유는 Record가 직접 Getter, Setter, equals(), hashCode(), toString() 메서드를 구현할 필요가 없기 때문입니다. 이러한 메서드들이 Java 컴파일러에 의해 자동으로 최적화되어 생성되므로, 메모리 사용량이 줄어듭니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;">예시:</p><ul><li style="text-align:justify;">DTO는 각각의 연산에 대해 별도의 필드와 메서드(getName(), setName() 등)가 필요합니다.</li><li style="text-align:justify;">반면, Record는 내부적으로 필드만 가지고 필요한 메서드를 자동으로 생성하므로, 더 적은 자원을 사용할 가능성이 있습니다.</li></ul><p style="text-align:justify;"> </p><h4 style="text-align:justify;"><strong>불변성과 스레드 안전성</strong></h4><p style="text-align:justify;">Record는 불변(immutable)이므로, 특히 멀티 스레드 환경에서 성능상의 이점을 제공합니다. Record는 불변이기 때문에 스레드 간에 공유될 때 동기화나 잠금(locking) 메커니즘이 필요하지 않습니다. 이는 스레드 간 경쟁(thread contention)으로 성능이 저하되는 상황에서 성능을 향상할 수 있습니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;">반면, 가변(mutable) DTO를 멀티 스레드 환경에서 사용할 경우, 스레드 안전성을 보장하기 위해 접근을 동기화하거나 다른 메커니즘을 사용해야 하므로 성능에 추가적인 부담이 생기고 애플리케이션이 느려질 수 있습니다.</p><p style="text-align:justify;"> </p><h4 style="text-align:justify;"><strong>가비지 컬렉션</strong></h4><p style="text-align:justify;">DTO와 Record 모두 일반적인 Java 객체(POJO, Plain old Java object)이므로 동일한 가비지 컬렉션 처리에 따라 관리됩니다. 하지만 Record가 더 간결하므로 메모리에 적은 객체가 생성되거나 유지될 수 있어, 가비지 컬렉션이 조금 더 빠르게 이루어질 가능성이 있습니다. 이는 대량의 데이터 객체를 처리하는 장기 실행 애플리케이션에서 성능 향상에 기여할 수 있습니다.</p><p style="text-align:justify;"> </p><h4 style="text-align:justify;"><strong>CPU 오버헤드</strong></h4><p style="text-align:justify;">Record는 컴파일러에 의해 자동 생성되며 성능을 최적화하도록 설계되어 있어, 객체 생성, 메서드 호출, 비교 작업(equals(), hashCode())에서 CPU 성능이 조금 더 향상될 수 있습니다. 특히 복잡한 DTO의 경우, 수동으로 구현된 메서드에서 비효율성이 발생할 수 있는데, Record는 일관되고 최적화된 방식으로 이러한 작업을 처리하므로 더 효율적입니다.</p><p style="text-align:justify;"> </p><h4 style="text-align:justify;"><strong>실제 성능</strong></h4><p style="text-align:justify;">실제로는 DTO와 Record 간의 성능 차이는 대부분의 애플리케이션에서 매우 적거나 무시할 만한 수준일 것입니다. Record의 간결함이 특정 시나리오에서 약간의 성능 향상을 가져올 수 있지만, 실제로는 대용량 데이터 처리, 높은 처리량을 요구하는 애플리케이션, 또는 리소스가 제한된 환경(예: 모바일 또는 IoT 장치)에서만 눈에 띌 정도의 성능 차이를 경험할 수 있습니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;"> </p><h3 style="text-align:justify;"><strong>정리</strong></h3><p style="text-align:justify;">지금까지 DTO와 Record의 주요 차이점, 각 사용 사례, 그리고 함수형 프로그래밍과 같은 다양한 프로그래밍 패러다임에 맞는 방법에 대해 살펴보았습니다. DTO는 유연성, 가변성, 그리고 맞춤형 동작을 제공하는 반면, Record는 데이터를 간결하고 불변하게 모델링하는 데 이상적이며, 최신 Java 애플리케이션에 적합합니다.</p><p style="text-align:justify;"> </p><p style="text-align:justify;">DTO와 Record를 선택하는 것은 결국 특정 요구 사항에 따라 달라집니다.</p><ul><li style="text-align:justify;">가변성이 필요하거나 커스텀 로직을 추가해야 한다면, DTO가 더 적합합니다.</li><li style="text-align:justify;">간결하고 불변적인 구조를 선호하고 Java 16 이상을 사용한다면, Record가 더 깔끔하고 효율적인 선택이 될 수 있습니다.</li></ul><p style="text-align:justify;"> </p><p style="text-align:justify;">두 가지 접근 방식 모두 각각의 장점을 가지고 있으므로, 이를 언제 사용해야 할지 이해하면 더 효율적이고 유지보수가 쉬운 Java 코드를 작성할 수 있습니다.</p><hr><p style="text-align:justify;"><원문></p><p style="text-align:justify;"><a href="https://blog.ashutoshkrris.in/dto-vs-record-in-java-which-should-you-use"><u>DTO vs Record in Java: Which Should You Use?</u></a></p><p style="text-align:justify;"> </p><p style="text-align:center;"><span style="color:#999999;">위 번역글의 원 저작권은 Ashutosh Krishna에게 있으며, 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다</span></p>