왜 DTO를 사용해야 할까?

lango·2022년 7월 15일
2

스프링(Spring)

목록 보기
1/5
post-thumbnail

spring boot를 통해 개발공부를 하는 도중 Entity를 통해 클라이언트와 서버 통신을 주고 받도록 설계하고 개발하고 있었는데 문득 의문이 들었다.

"굳이 보여주지 않아도 되는 정보까지 통신하는 것 아닌가?"

그래서 열심히 찾아보니 이 의문점은 현실이 되었다.

spring에서 JPA를 이용하게 되면 Entity는 단순히 데이터를 담는 객체가 아니라 DB Layer(실제 데이터베이스)와 데이터를 주고받을 때 사용하는 것이기에 View Layer(요청 및 응답 클래스)와 데이터를 주고 받을 때는 DTO를 사용해야 한다는 것을 알게 되었다.

그렇다면 DTO와 Entity가 무엇인지 알아보자.


Entity란?

  • Entity란 실제 DB의 테이블과 매핑되는 핵심 클래스
  • DB 테이블에 존재하는 컬럼들을 필드로 가지는 객체
  • Entity 클래스 또는 가장 Core한 클래스라고 부른다.
  1. 데이터 요청/응답 객체로의 사용은 지양해야 한다.
    Entity는 데이터베이스 영속성(persistent)의 목적으로 사용되는 객체이기에 요청 및 응답값을 전달하는 클래스로의 사용은 좋지 않다. 이런 내용조차 모르고 Entity를 사용했다니 반성해야 겠다..

  2. Entity에서는 setter 사용을 지양해야 한다.
    이유는 Entity 값을 변경할 수 있으므로 일관성을 보장할 수 없다고 한다.
    setter 메서드 사용을 지양해야 하는지도 새로운 포스트로 작성할 예정이다.


DTO란?

  • DTO(Data Transfer Object)란 계층(Layer)간 데이터 교환을 위해 사용되는 객체
  • DB에서 데이터를 가져와 Service 및 Controller 등으로 전송할 때 사용하는 객체
  1. 특별한 로직을 가지지 않는 순수한 데이터 객체로서의 역할을 한다.

  2. getter/setter 메서드만을 갖는다.
    하지만 DB에서 가져온 값을 변경할 필요는 없기에 setter를 만드는 것보다 생성자에서 값을 할당한다.


다음으로 왜 DTO를 쓰는지 알아보기 전에 먼저 코드로 간단하게 정리해보자.

// controller
@GetMapping("/member/{id}")
public ResponseEntity<Member> info(...) {
	...
	return ResponseEntity,ok(Member);
}

// Member Entity
@Entity
public class Member{
    @Id @GeneratedValue Long id;
    String name;
    string address;
    string phone;
}

위의 코드를 통해서 Member라는 Entity를 요청과 응답으로 사용하면 안된다는 것을 알았으니 고쳐보자.

// controller
@GetMapping("/member/{id}")
public ResponseEntity<MemberResponseDto> info(...) {
	...
	return ResponseEntity,ok(Member);
}

// MemberResponseDto
public class MemberResponseDto{
    private Long id;
    private String name;
    private string address;
    private string phone;
}

MemberResponseDto라는 DTO를 정의하여 데이터 요청객체로 사용하도록 고쳐보았다.


왜 DTO를 만들어 사용해야 할까?

귀찮게 DTO를 별도로 만들어 사용해야할까?
Entity와 분리하여 DTO를 사용할 때의 이점에 대해 알아보자.

1. Entity를 캡슐화할 수 있다.

  • Entity는 DB 테이블과 매핑되는 클래스로 데이터 전달 역할을 하도록 getter와 setter를 가지게 된다면 controller와 같은 곳에서 변경될 가능성이 있다.

  • 또한 View Layer를 통해 통신하게 된다면 테이블 설계를 그대로 공개하는 것이기에 보안적으로 좋지 못하다고 볼 수 있다. 그렇기에 별도로 요청 및 응답 역할을 담당하는 DTO 객체를 만들어 사용해야 한다.

2. View Layer와 DB Layer의 역할을 분리할 수 있다.

  • Entity를 요청 및 응답 객체로 사용한다면 사용자의 이름만 보여주면 되는 상황에서 Entity를 통해 사용자 이름뿐 아니라, 다른 정보까지 포함하여 보여주게 된다.

  • 그러면 모든 요청과 응답에서 Entity의 모든 내부 정보를 함께 전송하기에 속도도 느려지고 보안적으로도 바람직하지 못하다.

아래 Member Entity를 살펴보자.

@Entity
public class Member{
    @Id @GeneratedValue Long id;
    String pwd;
    String name;
    string address;
    string phone;
    
}

위 Member Entity를 통해서 어떤 속성을 화면으로 전송할지 한눈에 파악하기 힘들고, 필요한 속성들만 별도로 선택할 수 없다는 것을 알 수 있다.

특정 API에 필요한 데이터를 포함하여 만든 DTO를 살펴보자.

public class MemberResponseDto{
    private Long id;
    private String name;
}

위와 같이 DTO를 구성하게 되면 화면에서 필요한 데이터만 가지고 요청 및 응답을 할 수 있다는 것을 알 수 있다.

3. 순환 참조를 예방할 수 있다.

사실 순환참조에 대해서도 그리 깊게 알고 있진 않다.. 공부할게 참 많다.

  • 양방향 참조된 Entity를 controller에서 응답으로 보내면 Entity가 참조하고 있는 객체는 지연로딩이 되며 로딩된 객체는 자신이 참조하고 있는 객체를 호출하여 무한루프가 발생한다.

  • Entity를 그대로 return하면 연관 관계가 형성된 테이블 전체가 함께 리턴되기에 DTO를 사용하여 필요한 컬럼만을 리턴하도록 구성하면 해당 문제를 방지할 수 있다.

4. 관심사를 적절하게 분리할 수 있다.

  • Entity는 DB의 테이블과 매핑되는 필드가 선언되어 있으며, 각 필드들에는 모델링을 매핑하기 위한 코드(@Column, @JoinColumn, @ManyToOne, @OneToMany 등)가 추가된다.

  • 그런데 데이터 응답 및 요청 역할을 하는 DTO가 없다면 Entity 내부에 validation을 위한 필드나 코드가 추가로 작성될 가능성이 있어 Entity는 복잡해지고 가독성이 떨어지게 된다.

  • 그러므로 데이터 요청 및 응답에 필요한 로직을 DTO에서 정의하게 된다면, Entity를 도메인 모델링 및 비즈니스 로직에 집중할 수 있도록 만들 수 있어 관심사의 분리를 잘 따를 수 있다.

관심사의 분리란?
서로 다른 관심사들을 분리하여 주어진 역할을 수행하는데 집중하도록 구성하여 변경 가능성을 최소화하고, 유연하며 확장가능한 클린 아키텍처를 구축하도록 도와준다.



Final..

Entity 대신에 DTO를 쓰는 이유에 대해서 자료 조사 없이 스스로 생각했을 땐 단순히 "Entity의 정보를 모두 보여줄 필요는 없겠지" 하는 의문만을 해결할 수 있을 줄 알았지만, 그 외에도 엄청난 이점을 가지고 있었다.

또한 한편으론 개발하는 API 마다 별도의 DTO를 구성하게 된다면, 관리하기 어려울 것 같다는 생각이 들었다.

하지만 Entity를 통해 데이터를 주고받는 행위 자체가 애플리케이션의 오점이기 때문에 앞으로는 데이터 교환시 DTO를 적절하게 구성하여 사용할 수 있도록 꾸준히 공부하며 개발해야겠다.


이제 Entity와 DTO를 구분하는 이유에 대해선 명확하게 이해하였다.

그러면 DTO는 어디서부터 어디까지 사용해야 할까?

  • Service와 Repository(DB)간에는 Entity를 통해 데이터를 주고받는 것은 확실히 알겠다. 그러나 Controller와 Service 간에는 DTO를 사용할지 Entity를 사용할지 고민이 된다.

Entity to DTO, DTO to Entity 방식은 어떻게 구현해야 할까?

  • DTO의 사용범위에 따라서 Entity를 DTO로 변환해야 하고, DTO를 Entity로 변환해야할 필요가 있어 보이는데 이 과정은 어떻게 구현해야할지 또한 고민이 많아졌다.
    항상 느끼지만 공부해야할 것은 너무나 많다..

위 2가지 궁금증에 대해서도 알아보고 공부하고 가능하다면 포스팅으로 작성해보려 한다.
스프링 개발자로서 너무나 부족하고 배울게 산더미라는 것을 매주매주 느끼게 된다.






참조 문서

profile
찍어 먹기보단 부어 먹기를 좋아하는 개발자

2개의 댓글

comment-user-thumbnail
2023년 12월 9일

깔끔하고 쉽게 정리해주셔서 감사합니다!
덕분에 쉽게 이해됐습니다! 😆👍

1개의 답글