Spring - Data 전달객체 [VO, DTO]

지니·2023년 8월 20일
0

spring

목록 보기
7/13

스프링에서 Data를 전달하는 객체에는 VO, DTO가 있다. 이에 대해 알아보자

VO (Value Object)와 DTO (Data Transfer Object)는 모두 Java 및 Spring과 같은 객체 지향 프로그래밍 및 프레임워크에서 데이터를 표현하고 전달하는 데 사용되는 설계 패턴이다.

1. VO, DTO의 차이점

불변성(Immutability)

  • VO(Value Object)

    • VO는 불변 객체이다. 즉, 일단 생성된 후에는 그 상태를 변경할 수 없다.

    • VO는 일반적으로 속성을 가진 작은 객체로, 이들 속성은 객체가 생성될 때 설정되고 그 후에는 변경되지 않는다.

    • 불변성은 VO의 중요한 특징 중 하나이며, 이는 복잡성을 줄이고 프로그램의 안정성을 향상시킨다.

  • DTO(Data Transfer Object)

    • DTO는 원칙적으로 불변성을 가져야하지만, 실제 사용시에는 상황에 따라 변경 가능성을 가질 수도 있다.

    • DTO는 다른 계층 또는 서비스간의 통신에서 사용되는 객체로, 그 목적은 단순히 데이터를 한 데 묶어 전달하는 것이다. DTO가 변경 가능한 객체인지 불변한 객체인지는 그 사용 방법에 따라 달라질 수 있다. 일부 경우에는, 데이터를 생성한 후에 추가적인 변형이나 가공을 위해 DTO를 변경해야 할 수도 있다.

    • 그러나 원칙적으로 DTO는 불변성을 가지는 것이 좋다. DTO는 데이터 전송의 목적으로 사용되기 때문에, 데이터 전송이 완료된 이후에는 원래의 상태가 유지되는 것이 일반적이다. 이렇게 함으로써, 데이터의 일관성이 유지되고 버그의 가능성이 줄어들기 때문이다.


2. 동등성(Equality)

VO(Value Object)의 동등성

  • VO의 동등성은 모든 속성 값에 의해 결정된다.
  • 즉, 두 VO가 동등하다는 것은 그들의 모든 속성 값이 동일하다는 것을 의미한다. 이런 의미에서 VO는 값에 의해 정의되므로 "Value Object"라고 불린다.

VO 동등성 예시

  • 예를 들어, 아래와 같은 Money 클래스가 있다고 가정해보자.
public class Money {
    private final int amount;
    private final String currency;

    public Money(int amount, String currency) {
        this.amount = amount;
        this.currency = currency;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Money money = (Money) obj;
        return amount == money.amount && currency.equals(money.currency);
    }

    // hashCode() 메서드도 재정의
}
  • 이 코드의 경우, 두 Money 객체가 동일한 amount와 currency 값을 가지면 동등하다고 판단된다. -> 값이 모두 같아야 한다.
Money money1 = new Money(100, "USD");
Money money2 = new Money(100, "USD");
System.out.println(money1.equals(money2)); // true
  • DTO(Data Transfer Object)의 동등성
  • DTO의 동등성은 일반적으로 그것의 식별자(ID)나 일부 핵심 속성에 의해 결정된다.
    이는 DTO가 데이터베이스의 레코드를 표현하는 경우가 많고, 이런 레코드는 고유한 ID를 갖는 경우가 일반적이기 때문이다.

DTO 동등성 예시

  • DTO는 일반적으로 식별자(ID)나 일부 핵심 속성에 의해 동등성이 결정된다.
    예를 들어, 아래와 같은 UserDTO 클래스가 있다고 가정해보자.
public class UserDTO {
    private Long id;
    private String name;
    private String email;

    // getters and setters

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        UserDTO userDTO = (UserDTO) obj;
        return id.equals(userDTO.id);
    }

    // hashCode() 메서드도 재정의
}
  • 이 경우, 두 UserDTO 객체가 동일한 id 값을 가지면 동등하다고 판단된다.
UserDTO user1 = new UserDTO();
user1.setId(1L);
user1.setName("Alice");
user1.setEmail("alice@example.com");

UserDTO user2 = new UserDTO();
user2.setId(1L);
user2.setName("Bob");
user2.setEmail("bob@example.com");

System.out.println(user1.equals(user2)); // true
  • 여기서 user1과 user2는 이름과 이메일이 다르지만, ID가 같으므로 동등하다고 판단된다.

VO와 DTO의 동등성 정리

  • VO
    모든 속성 값이 같아야 동등하다고 판단된다. 값 자체에 의해 정의되므로 "Value Object"라고 불린다.

  • DTO
    식별자나 핵심 속성이 같으면 동등하다고 판단된다. 데이터베이스 레코드를 표현하는 경우가 많으므로, 고유한 ID를 기준으로 판단하는 경우가 일반적이다.


3. VO, DTO의 사용 목적

VO(Value Object)

  • VO는 비즈니스 로직을 수행하는 데 사용되는 정보를 캡슐화한다. 이는 도메인의 의미 있는 개념을 표현하며, 불변성을 갖는 경우가 많다.

VO예시: Money class

  • 목적: 금액과 통화를 나타내는 VO로, 비즈니스 로직에서 금액의 계산과 같은 작업을 수행하는 데 사용된다.
  • 특징: 모든 속성 값에 의해 동등성이 결정되며, 불변 객체로 설계되는 경우가 많다.
public class Money {
    private final int amount;
    private final String currency;

    public Money(int amount, String currency) {
        this.amount = amount;
        this.currency = currency;
    }

    // getters, equals, hashCode
}

DTO(Data Transfer Object)

  • DTO는 서로 다른 계층 또는 시스템 간에 데이터를 전송하는 데 사용된다. 이는 데이터의 컨테이너 역할을 하며, 로직을 포함하지 않는 순수한 데이터 구조이다.

DTO예시: UserDTO class

  • 목적: 서버와 클라이언트 간의 통신이나 데이터베이스의 레코드를 가져오는 등의 작업에 사용된다. 예를 들어, REST API의 응답으로 사용자 정보를 전달하는 데 사용할 수 있다.

  • 특징: 일반적으로 식별자나 핵심 속성에 의해 동등성이 결정되며, 다른 계층간의 데이터 전송을 위해 설계된다.

@Getter
@Setter
public class UserDTO {
    private Long id;
    private String name;
    private String email;
}

사용 목적 정리

  • VO: 의미 있는 비즈니스 개념을 표현하며, 불변성을 갖고, 비즈니스 로직에서 사용된다.

  • DTO: 계층 간 또는 시스템 간의 데이터 전송을 위한 구조로, 순수한 데이터 컨테이너 역할을 한다.

  • 이 두 개념은 설계 목적과 사용 방식이 다르므로, 적절한 상황에서 각각 사용하면 코드의 명확성과 유지보수성을 높일 수 있다.

4. VO, DTO 를 예시로 쉽게 이해해보자

VO (Value Object)는 '화폐'와 같다.

  • 1000원 짜리 지폐를 생각해보자. 이 지폐는 1000원의 가치를 나타내고, 그 가치는 변하지 않는다. 다른 1000원 짜리 지폐와 비교해도 가치는 동일하다.
    지폐의 색깔이나 크기, 무게가 달라도 1000원의 가치는 변하지 않는 것처럼, VO는 그 값을 나타내는 속성이 같다면 동일한 객체로 취급된다.

    • 예시: Money 클래스의 amount와 currency가 같다면, 두 객체는 동일하다고 볼 수 있다.

DTO (Data Transfer Object)는 '학생의 성적표'와 유사하다.

  • 학생의 성적표에는 이름, 과목별 점수 등 여러 정보가 들어있다. 성적표는 학기가 끝날 때마다 업데이트 될 수 있다.
    성적표의 내용이 바뀌더라도, 같은 학생의 성적표라면 동일한 성적표로 취급된다. 성적이 변하더라도, 그 성적표는 같은 학생의 것이기 때문이다.

    • 예시: UserDTO 클래스의 id가 같다면, 두 객체는 동일한 것으로 간주된다. 이름이나 이메일이 달라도, id가 같으면 같은 사용자의 데이터를 나타낸다.

결론

  • VO는 값의 불변성에 중점을 둔다. 값이 같다면 동일한 객체로 취급된다. 반면, DTO는 데이터의 전달과 변경에 초점을 맞춘다. 식별자가 같다면 동일한 객체로 취급되며, 내부 데이터는 변할 수 있다. 이 둘은 각각의 역할과 책임에 맞게 사용되어야 하며, 이를 통해 코드의 명확성과 유지보수성을 높일 수 있다.

5. JPA에서의 VO, DTO, Entity 클래스 사용 방식

Entity 클래스

  • DB의 테이블을 객체로 표현하며, 각 테이블의 행은 Entity 클래스의 인스턴스에 매핑된다. Entity 클래스의 인스턴스는 데이터베이스에서 가져온 데이터를 나타내므로 변경될 수 있다. 또한 비즈니스 로직을 포함할 수 있으며, 이 점은 DTO와 차이가 있다.

    • 예시: User Entity 클래스는 사용자 테이블을 표현하며, 각 사용자는 User 인스턴스로 매핑된다.

DTO (Data Transfer Object)

  • 여러 계층 또는 시스템 간에 데이터를 전송하는 데 사용된다. Entity 클래스의 인스턴스는 그대로 클라이언트에게 전달되지 않는다. 대신, 필요한 데이터만 DTO에 담아서 전달한다. 데이터베이스 구조의 세부 사항을 숨기고, 필요한 데이터만 제공하는 역할을 한다.

    • 예시: UserDTO는 클라이언트에게 전달할 사용자 정보를 담는 클래스다.

VO (Value Object)

  • JPA에서도 활용될 수 있으며, 엔티티의 일부분을 나타내는 데 사용될 수 있다. 불변성을 가질 수 있으며, 특정 비즈니스 개념을 표현한다.

    • 예시: Address VO는 '도시', '도', '우편번호' 등의 필드를 가질 수 있으며, User Entity의 일부로 사용될 수 있다.

결론

  • JPA 환경에서는 Entity 클래스가 데이터베이스와 직접 매핑되어 데이터를 표현하며, DTO는 클라이언트와 서버 간의 데이터 전송을 담당한다.
    VO는 특정 비즈니스 개념을 나타내는 데 사용되며, 엔티티의 일부로 활용될 수 있다.

  • 이 세 가지 개념은 각각의 역할과 책임에 따라 적절하게 사용되어야 하며, 이를 통해 코드의 유연성과 유지보수성을 높일 수 있다.


6. DTO와 VO의 사용: 언제 어떤 것을 사용할까?

DTO (Data Transfer Object)의 주요 사용 상황

  • ORM(Object-Relational Mapping) 또는 SQL 매퍼 프레임워크:

    • JPA와 MyBatis와 같은 환경에서는 데이터의 전달과 변환에 중점을 두므로 주로 DTO가 사용된다.
  • 단순 CRUD 작업:

    • 데이터의 생성, 읽기, 업데이트, 삭제 작업을 주로 수행하는 애플리케이션에서는 DTO만으로도 충분할 수 있다.
  • 클라이언트와 서버 간의 통신:

    • 필요한 데이터만을 담아 전달하는 역할을 하므로, 데이터베이스 구조의 세부 사항을 숨기는 데 유용하다.

VO (Value Object)의 주요 사용 상황

  • 도메인 주도 설계 (Domain Driven Design, DDD):

    • VO는 도메인에서 개념적으로 중요하거나 불변성을 가져야 하는 데이터를 모델링하는 데 유용하다.
  • 복잡한 도메인 모델링:

    • '주소', '화폐'와 같은 본질적으로 값이 변하면 새로운 객체로 취급되는 개념은 VO로 표현하기 적합하다.
  • 불변성 요구:

    • VO는 불변성을 가질 수 있으므로, 데이터의 일관성을 유지하는 데 사용될 수 있다.

결론

  • DTO:

    • 데이터의 전달과 변환에 중점을 둔다. 단순 CRUD 작업이나 클라이언트와 서버 간의 통신에 주로 사용된다.
  • VO:

    • 도메인의 복잡성과 불변성에 중점을 둔다. 복잡한 도메인 모델링이나 불변성이 요구되는 상황에서 사용된다.
  • 적절한 선택:

    • VO와 DTO는 각각의 목적과 역할에 따라 활용되어야 한다. 프로젝트의 특성, 도메인의 복잡성, 개발팀의 설계 전략에 따라 적절한 선택이 필요하다.
profile
탐구하는 Backend 개발자

0개의 댓글

관련 채용 정보