안녕하세요. 새내기 개발자입니다. 공부하면서 정리하는 글로 틀린 부분은 언제나 댓글로 환영합니다!
Java에서 DTO(Data Transfer Object)는 데이터 전송을 위해 사용되는 객체로, 일반적으로 여러 계층(Controller → Service → Repository) 간 데이터를 주고받을 때 활용됩니다.
DTO를 만들 때 사용할 수 있는 방법으로 class, record, interface 등이 있으며, 이 글에서는 각 방식의 차이점과 장단점을 살펴본 후, DTO에 자주 사용하는 어노테이션까지 정리해 보겠습니다.
1. class를 이용한 DTO
🔹 특징
- 일반적인 클래스를 사용하여 DTO를 정의
- 필드, 생성자, getter/setter, toString(), equals(), hashCode() 등을 포함 가능
- 가변(Mutable) 또는 불변(Immutable) 객체로 만들 수 있음
✅ 장점
- 가장 유연한 방법이며, Java의 모든 버전에서 사용 가능
- 불변 객체로 만들 수 있음 (final 필드와 생성자 활용)
- 직렬화(Serialization), 로직 추가 등의 확장이 용이
❌ 단점
- 보일러플레이트 코드(getter, setter, 생성자 등)가 많음
- equals() 및 hashCode()를 직접 구현해야 함 (또는 Lombok 사용)
💡 예제
public class UserDto {
private String name;
private int age;
public UserDto(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
@Override
public String toString() {
return "UserDto{name='" + name + "', age=" + age + "}";
}
}
2. record를 이용한 DTO (Java 14+)
🔹 특징
- Java 14부터 도입된 불변 객체를 간편하게 정의하는 방식
- getter, toString(), equals(), hashCode() 자동 생성
- 모든 필드는 final이며, setter를 가질 수 없음
✅ 장점
- 보일러플레이트 코드 최소화 (자동 생성)
- 불변성(Immutable) 보장 → Thread-safe
- equals(), hashCode(), toString() 자동 구현
❌ 단점
- Java 14+에서만 사용 가능 (이전 버전과 호환되지 않음)
- 상속 불가 (record는 클래스를 상속할 수 없음)
💡 예제
public record UserDto(String name, int age) {}
위 코드만으로 getter, toString(), equals(), hashCode()가 자동 생성됨
3. interface를 이용한 DTO
🔹 특징
- DTO의 구조만 정의하고, 이를 구현하는 클래스를 만들거나 Proxy 기반으로 활용 가능
- Java의 **인터페이스 기본 메서드(default)**를 활용 가능
✅ 장점
- 여러 구현체를 만들기 용이
- DTO의 구조만 정의할 수 있어 유연성이 높음
- default 메서드 활용 가능
❌ 단점
- 직접 객체 생성 불가 (class 또는 record 필요)
- 보일러플레이트 코드 증가 가능 (구현체 필요)
💡 예제
public interface UserDto {
String getName();
int getAge();
default String getFormattedName() {
return "User: " + getName();
}
}
// 구현체
public class DefaultUserDto implements UserDto {
private final String name;
private final int age;
public DefaultUserDto(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String getName() { return name; }
@Override
public int getAge() { return age; }
}
4. DTO에 자주 사용하는 어노테이션
🔹 Lombok (코드 간소화)
import lombok.*;
@Getter @Setter
@NoArgsConstructor @AllArgsConstructor
@ToString @EqualsAndHashCode
@Builder
public class UserDto {
private String name;
private int age;
}
✅ 장점: 보일러플레이트 코드 제거, 유지보수성 향상
🔹 JSON 직렬화 (Jackson)
import com.fasterxml.jackson.annotation.*;
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonPropertyOrder({ "age", "name" })
public class UserDto {
@JsonProperty("full_name")
private String name;
@JsonIgnore
private String password;
private int age;
}
✅ 장점: JSON 필드명 변경, 직렬화/역직렬화 제어 가능
🔹 데이터 검증 (Validation)
import jakarta.validation.constraints.*;
public class UserDto {
@NotNull(message = "이름은 필수입니다.")
@Size(min = 2, max = 20)
private String name;
@Min(value = 18) @Max(value = 100)
private int age;
@Email
private String email;
@Pattern(regexp = "^[a-zA-Z0-9]{8,}$")
private String password;
}
✅ 장점: 서버에서 데이터 유효성 검사 가능
🔹 JPA 연동 (Entity 변환 가능)
import jakarta.persistence.*;
@Entity
@Table(name = "users")
public class UserDto {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 50)
private String name;
}
✅ 장점: DTO를 엔티티로 변환 가능, DB 매핑 가능
🔹 REST API 요청/응답
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping
public UserDto createUser(@RequestBody UserDto userDto) {
return userDto;
}
}
✅ 장점: Spring MVC에서 DTO를 요청/응답 데이터로 활용 가능
5. 정리: DTO 선택 및 어노테이션 가이드
방법 | 코드량 | 불변성 | 상속 | 직렬화 | JAVA 버전 |
class | 많음 | 선택 가능 | O | O | 모든 버전 |
record | 적음 | O (불변) | X | O | Java 14+ |
interface | 적음 | X | O | X | 모든 버전 |
✅ DTO를 쉽게 만들려면?
→ record + Lombok(@Builder) 활용
✅ 기존 Java 코드와 호환해야 한다면?
→ class + @Getter @Setter + @NoArgsConstructor
✅ JSON 변환이 필요하다면?
→ @JsonProperty, @JsonIgnore 활용
✅ 데이터 검증이 필요하다면?
→ @NotNull, @Email, @Pattern 활용
✅ DB와 연동이 필요하다면?
→ @Entity, @Table 활용
🔥 결론
- DTO는 class, record, interface 중 선택 가능
- Lombok, JSON, Validation, JPA 어노테이션을 활용하면 효율적인 DTO 설계 가능
- 프로젝트 특성에 맞게 필요한 기능을 고려하여 DTO 설계하면 유지보수성과 생산성을 높일 수 있음 🚀
'백엔드' 카테고리의 다른 글
Java의 Error와 Exception 차이점 (1) | 2025.02.15 |
---|---|
Java 메모리 영역과 동작 원리 (1) | 2025.02.14 |
[자바/JAVA] DAO, DTO, VO, Entity의 차이점과 역할 (1) | 2025.02.11 |
[DB] 관계형(RDB) vs 비관계형(NoSQL) 데이터베이스 비교 (1) | 2025.02.10 |
[자바/JAVA] Null Pointer Exception (NPE) (1) | 2025.02.08 |