1. DTO와 VO란?

1) DTO(Data Transfer Object)

  • DTO는 데이터를 전송하기 위한 객체로, 주로 클라이언트와 서버 간의 데이터 교환을 목적으로 사용됩니다.
  • 주로 Getter와 Setter만 가지며, 비즈니스 로직을 포함하지 않습니다.
  • 불변성을 보장하지 않아 데이터를 자유롭게 수정할 수 있습니다.
  • 데이터베이스에서 가져온 데이터를 가공하여 클라이언트로 전달하거나, 클라이언트에서 받은 데이터를 처리하기 위해 사용됩니다.

2) VO(Value Object)

  • VO는 값 자체를 표현하는 객체로, 주로 불변성이 중요한 곳에서 사용됩니다.
  • final 클래스와 final 필드를 사용하여 값을 변경할 수 없게 만듭니다.
  • 비즈니스 로직을 포함할 수 있으며, 주로 도메인 모델에서 활용됩니다.
  • equals()hashCode() 메서드를 재정의하여 값의 동등성을 비교합니다.


2. DTO와 VO의 차이점

특징 DTO VO
주요 목적 데이터 전송 값을 표현 및 동등성 비교
불변성 보장하지 않음 (Setter 사용 가능) 보장 (final 필드와 클래스)
비즈니스 로직 포함 여부 포함하지 않음 포함 가능
equals(), hashCode() 재정의하지 않아도 됨 반드시 재정의
사용 예 클라이언트-서버 간 데이터 전송 두 점 사이의 거리 계산, 좌표 같은 불변 데이터 표현


3. DTO 구현 예시

1) UserDTO 클래스

UserDTO는 사용자 정보를 서버와 클라이언트 간에 전송하기 위한 객체입니다.
Lombok 라이브러리를 활용하여 간결하게 작성할 수 있습니다.

import lombok.*;

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@ToString
public class UserDTO {
    private Long id;
    private String username;
    private String email;
    private int age;
}
  • @AllArgsConstructor: 모든 필드를 포함한 생성자를 자동 생성.
  • @NoArgsConstructor: 기본 생성자를 자동 생성.
  • @Getter, @Setter: 각 필드의 Getter와 Setter를 생성.
  • @ToString: 객체를 문자열로 표현하는 메서드를 생성.

2) DTO 사용 예시

public class UserExample {
    public static void main(String[] args) {
        // DTO 객체 생성 및 데이터 설정
        UserDTO u1 = new UserDTO();
        u1.setId(1L);
        u1.setUsername("John Doe");
        u1.setEmail("johndoe@example.com");
        u1.setAge(30);

        System.out.println("u1 = " + u1);

        // 모든 필드를 포함하는 생성자 사용
        UserDTO u2 = new UserDTO(2L, "Jane Smith", "janesmith@example.com", 25);
        System.out.println("u2 = " + u2);

        // Getter로 데이터 접근
        System.out.println("Username: " + u2.getUsername());
        System.out.println("Email: " + u2.getEmail());

        // Setter로 데이터 수정
        u2.setAge(26);
        System.out.println("Updated u2 = " + u2);
    }
}


4. VO 구현 예시

1) Point 클래스

Point 클래스는 2D 좌표를 표현하는 VO 객체입니다.
좌표 값은 불변하며, 두 점 사이의 거리 계산 같은 비즈니스 로직을 포함할 수 있습니다.

import java.util.Objects;

public final class Point {
    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() { return x; }
    public int getY() { return y; }

    public double distanceTo(Point other) {
        int dx = this.x - other.x;
        int dy = this.y - other.y;
        return Math.sqrt(dx * dx + dy * dy);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Point point = (Point) o;
        return x == point.x && y == point.y;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }

    @Override
    public String toString() {
        return "Point{" + "x=" + x + ", y=" + y + '}';
    }
}

2) VO 사용 예시

public class PointExample {
    public static void main(String[] args) {
        Point p1 = new Point(0, 0);
        Point p2 = new Point(3, 4);

        System.out.println("p1 = " + p1);
        System.out.println("p2 = " + p2);

        // 두 점 사이의 거리 계산
        System.out.println("두 점 사이의 거리: " + p1.distanceTo(p2));

        // 객체 동등성 비교
        Point p3 = new Point(3, 4);
        System.out.println("p2와 p3는 같은가? " + p2.equals(p3));

        // 해시코드 비교
        System.out.println("p2의 hashCode: " + p2.hashCode());
        System.out.println("p3의 hashCode: " + p3.hashCode());
    }
}


5. DTO와 VO의 활용

1) DTO

  • 클라이언트에서 서버로 데이터를 전송할 때 사용됩니다.
  • 예: 사용자 로그인 요청, 상품 주문 정보 전송 등.

2) VO

  • 값 자체의 불변성을 보장하며, 동일한 값은 같은 객체로 판단합니다.
  • 예: 좌표(Point), 금액(Money), 날짜(Date) 등.