본문 바로가기

Spring

Spring -Dto, Vo 개념 정리

DTO(Data Transfer Object, 데이터 전송 객체)

Database에서 데이터를 얻어 service나 controller 계층으로 전달할 때 사용하는 객체를 의미한다.

데이터를 전송하기 위해 사용하는 객체라서 그 안에 비즈니스 로직 같은 복잡한 코드는 없고 순수하게 전달하고 싶은 데이터만 담겨있다.

DTO와 Entity의 분리 (Request DTO)

 근데 왜 굳이 Entity 그자체를 넘기지 않고 Dto라는 별도의 데이터 전송만을 위한 객체를 만들어서 전달을 하는 것일까?

  • 엔티티 자체를 넘기지 않고 꼭 필요한 특정 데이터만을 포함하는 Dto를 전달함으로 불필요한 데이터의 전달을 막고, 이는 통신과정에서의 불필요한 오버헤드의 감소로 이어진다
  • 만일 엔티티 자체를 넘기는 경우, 데이터 베이스에 수정사항이 생기는등의 이유로 엔티티의 필드를 수정한다면 이와 연결된 모든 API가 다 망가질 수도 있다. 즉, API의 유지보수성을 높일 수 있다.

DTO와 Entity의 분리 (Response DTO)

요청시에 Request DTO를 통해서 데이터를 받았다면 받은 데이터를 처리한 후 클라이언트에게 Response를 보낼 때도 별도의 Response DTO를 통해 응답을 보낸다. 이 또한 이점이 존재한다.

  • 특정 데이터만을 응답으로 전달한다. 외부에 응답시 민감한 데이터(예: 비밀번호)의 노출을 막을 수 있다.
  • 통신에서의 오버헤드를 감소할 수 있다. 
package jpabook.jpashop.api;

import jpabook.jpashop.domain.Address;
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.domain.Order;
import jpabook.jpashop.service.MemberService;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import java.util.List;
import java.util.stream.Collectors;

@RestController
// @RestController = @Controller + @ResponseBody
// @ResponseBody: 자바객체를 json, xml로 변환해서 보낼 때 사용
@RequiredArgsConstructor
public class MemberApiController {

    private final MemberService memberService;
	
    /* Request DTO */
    @Data
    static class CreateMemberRequest {
        // 엔티티의 변화에도 대응할 수 있도록 값을 받는

        // Request 파라미터를 별도로 생성
        // 클라이언트 요청이 여기로 들어옴
        @NotEmpty(message = "이름은 필수 값 입니다")
        private String name;

        // Address
        private String city;
        private String street;
        private String zipcode;

        private Address address = new Address(city, street, zipcode);
    }
    
    /* Response DTO */
    @Data
    @AllArgsConstructor
    static class CreateMemberResponse {
        // 클라이언트에게 반환 값. Json의 형태로 클라이언트에게 반환됨
        private Long id;
    }

    /**
     * 조회 V2: 응답 값으로 엔티티가 아닌 별도의 DTO를 반환한다.
     * - 엔티티를 DTO로 변환해서 반환한다.
     * - 엔티티가 변해도 API 스펙이 변경되지 않는다.
     * - 추가로 Result 클래스로 컬렉션을 감싸서 향후 필요한 필드를 추가할 수 있다
     */

    @PostMapping("/api/v2/members")  // @RequestBody: 이 주소로 json, xml 데이터가 전달되면 이를 자바객체로 변환해서 매핑해줌
    public CreateMemberResponse saveMemberV2(@RequestBody @Valid CreateMemberRequest request) {
        //@Valid 어노테이션 안 붙이면 에러메시지 안뜸
        Member member = new Member();
        member.setName(request.getName());
        member.setAddress(request.getCity(),request.getStreet(),request.getZipcode());
        Long id = memberService.join(member);

        return new CreateMemberResponse(id);
    }
    
    /* wrapping 용도 */
    @Data
    @AllArgsConstructor
    private class Result<T> {

        private T data;
    }
}

 

DTO 정리

  1. DTO대신 엔티티를 사용하면 엔티티 구조가 모두 노출될 수 있다. DTO를 사용했을 때보다 보안에 취약해질 수 있다. DTO를 사용함으로서 엔티티 내부 구현을 캡슐화할 수 있다.
  2. 클라이언트로 넘겨줘야 할 데이터는 API마다 다를 수 있습니다. 때문에 엔티티를 반환값으로 사용하면 유지보수가 힘들다.
  3. DTO대신 엔티티를 사용하면 클라이언트 요구사항에 엔티티가 영향을 받을 수 있다.
  4. 엔티티와 DTO가 항상 동일한 상황이라면 DTO대신 엔티티를 사용해도 되지만 그런일은 거의 없다.
  5. DTO를 사용하면 클라이언트에 전달해야 할 데이터의 크기를 조절할 수 있다. 엔티티를 반환하면 불필요한 데이터가 클라이언트에 전송될 수 있다.
  6. validationswagger 등의 코드들과 엔티티 코드를 분리할 수 있다. 더 깔끔한 코드가 돼서 읽고 관리하기 용이해진다.

 

VO (Value Object)

 

각 계층간의 데이터 전달을 위한 객체를 의미한다. 일반적으로 VO는 DTO와 같은 개념이지만 약간의 차이가 존재한다.

VO는 필드값과 Getter, Setter만으로 구성된다.

 VO는 값의 변경이 없는 경우에 사용하며, DTO와는 달리 내부에 별도의 비즈니스 로직이 존재하지 않는다.

 

결론

 VO는 사용되는 값이 객체로 표현되며, 값의 변경이 없는 경우에 사용되고, DTO는 데이터의 전송을 위한 객체이며, 비즈니스 로직까지 담아서 사용하기도 한다.

 예를들어 외부 시스템과 데이터 통신을 할 경우에는 DTO로,  DB에서 가져오는 Data는 Vo로 정의해서 사용한다고 보면 될 것 같다.

'Spring' 카테고리의 다른 글

Spring- @xxxToOne  (0) 2023.02.07
Spring- 빈 스코프(웹 스코프)  (0) 2023.02.04
Spring- 빈 스코프(싱글톤, 프로토타입)  (0) 2023.02.02
Spring- 빈 생명 주기  (0) 2023.02.02
Spring- 조회한 빈이 모두 필요할 때(Map, List)  (0) 2023.01.31