[Nest.js]DTO란? Entity란?

resilient

·

2021. 11. 27. 17:53

728x90
반응형

요즘 진행 중인 백앤드 리뉴얼 프로젝트는 express 기반에서 Nest.js 기반으로 migration 하고 있습니다.
프레임워크를 바꾸는 만큼 대공사가 진행 중인데요, express에서는 mongoose로 하나의 controller에서 모든 쿼리를 수행했습니다. (어마어마하게 비효율적으로 코드가 짜여 있었지만, 차마 손댈 엄두가 나지 않았었습니다..)

 

Nest.js를 사용해서 하나하나 진행하다 보니, 작업을 할 때 사용하던  entity 와 dto에 대해서 누군가에게 entity가 뭐고 dto가 뭐야? 왜 써야하는 거야?라고 질문을 받았다면 과연 정확하게 말할 수 있나? 를 생각해봤을 때 답은 아니오 였습니다.

 

그래서 이번 게시물에서는 아래의 내용들을 확실하게 짚고 넘어가려고 합니다.

  • entity, dto 가 정확히 무엇인지?
  • 그렇다면 dto를 왜 써야하는지? 

 

그렇다면, entity는 뭐야?

 

DB를 처음 접했을 때, 들었던 개념 중에 entity가 있었습니다.

entity는 간단하게 말하면 실체, 객체입니다.  개념을 한번 정리하고 넘어갈까요?

  • entity는 사람, 물건, 개념, 정의 등과 같이 명사입니다.
  • entity는 '업무상 관리가 필요한 것'에 해당합니다.
  • entity는 서비스안에서 '저장되어야 하는 어떠한 것'이라고 할 수 있습니다.

너무 딱딱하게 이론적으로만 설명을 했네요. 이번에는 예를 들어보겠습니다.

 

  • 학교라는 곳에선 과목이라는 엔티티가 존재할 수 있습니다.
  • 그리고 엔티티는 인스턴스의 집합으로 나타나게 됩니다. 즉 과목이라는 엔터티가 있다면, 수학, 영어, 국어와 같은 인스턴스가 과목이라는 엔터티에 포함되는 것이죠.
  • 이때 엔터티는 자신이 가지고 있는 인스턴스를 설명할 수 있는, 나타낼 수 있는 속성(Attribute)을 가지게 됩니다.
  • 앞에서 이야기한 수학, 영어, 국어와 같은 인스턴스가 존재한다면 이 녀석들은 과목이라는 엔터티에서 이름이라는 속성을 가지고 있는 것이죠.

 

(현재 진행중인 프로젝트에서 user.entity 파일은 아래와 같습니다. 예시로 참고하고 이해하면 좋을 것 같습니다.)

 

그럼, DTO란?

 

DTO는 Data Transfer Object의 약자로, 데이터 전송 객체로, 프로세스 간 데이터를 전달하는 객체를 의미합니다.

백앤드에서 요청을 받고 응답을 내보내는 과정에서 데이터를 호출하는 비용은 상당합니다.

효율적인 백앤드 설계를 하려면 요청의 횟수를 최소화하고, 최소화 하기 위해서 한 번의 요청에 최대한 많은 데이터를 전송해야 하겠죠?
그래서 사용하는 것이 데이터 전송 객체, DTO입니다.

자, 그러면 DTO에 대해서 한 번 정리를 하고 넘어갈까요?

  • 웹 프로그래밍을 하는 과정에서 호출은 굉장히 부담이 됩니다. 많은 개발자들이 호출을 줄이기 위해 노력하죠. 호출을 줄이고 더욱 효율적으로 값을 전달 해야만 합니다.
  • 이를 위해 데이터를 모아 한번에 전달하는 방법이 고안되었고, 이때 모은 데이터 객체를 객체를 DTO라고 합니다.

 

(현재 진행중인 프로젝트에서 user.dto 파일은 아래와 같습니다. 예시로 참고하고 이해하면 좋을 것 같습니다.)

 

그럼 왜 DTO를 써야 할까요?

 

위에서 설명을 한번 했지만, DTO를 사용하지 않으면 호출 횟수가 자연스럽게 많아지고 이는 통신의 횟수 증가로 이어집니다. 로직 또한 누가 봐도 비효율적인 로직이 만들어질 수밖에 없다고 생각해요. 

 

이제 예시를 들어서 이해해 볼까요?

 

간단하게 게시판 프로젝트를 진행한다고 가정하겠습니다.

유저가 게시물을 올린다고 했을 때, 게시물에는 

  • 유저 아이디
  • 게시물 제목
  • 게시물 내용
  • 카테고리

의 내용이 들어 갑니다.

 

DTO 없이 게시물을 Create 하는 로직을 생각해보겠습니다.

  • 첫 번째, 유저 아이디를 요청으로 보내고, 백앤드에서는 따로 저장을 하고 validation처리를 합니다.
  • 두 번째, 게시물 제목을 요청으로 보내고, 백앤드에서는 따로 저장을 하고 validation처리를 합니다.
  • 세 번째, 게시물 내용을 요청으로 보내고, 백앤드에서는 따로 저장을 하고 validation처리를 합니다.
  • 네 번쨰, 카테고리를 요청으로 보내고, 백앤드에서는 따로 저장을 하고 validation처리를 합니다.

이 과정들 중 하나라도 빠지거나 순서가 바뀌거나 잘못된 값이 전달되어 validation 처리를 통과하지 못한다면 처음부터 요청을 다시 보내야 하는 불상사가 생깁니다. (물론 트랜잭션이나 다른 방법을 사용하면 꽤나 효율적으로 요청을 보낼 수도 있겠지만 극단적인 예시를 들어보았습니다.) 그럼 어떻게 고쳐야 할까요? 저는 아래와 같이 생각을 했습니다.

 

  • 모든 단계에서 데이터를 따로따로 보내는 게 비효율적인 거 같은데, 묶어서 보내면 한 번만 보내면 되겠구나
  • validation 로직 처리도 중간에 틀려도 한 번씩만 요청을 보낼 수 있겠다. 

 

 

또 어떤 장점이 있을까요?

 

첫 번째, Dto를 사용하면 유닛 테스트 할 때 굉장히 쉬워집니다. Service 레이어 에서 유닛테스트를 할 때, Entity를 그대로 사용해버리면 Service 테스트를 하게 됩니다. 이렇게 되면 DB 까지 끌어와서 테스트를 하게 되는데 굉장히 불필요하죠.

추가적으로, Entity를 Controller 같은 클라이언트단과 직접 마주하는 계층에 직접 전달하는 대신 DTO를 사용해 데이터를 교환해야 합니다. Dto는 그저 계층간 데이터 교환이 이루어 질 수 있도록 하는 객체이기 때문에, 특별한 로직을 가지지 않는 순수한 데이터 객체여야 합니다.


두 번째, 유지보수가 굉장히 쉬워집니다. 예를 들어, User 라는 Entity에 Name, Age, Password라는 필드가 있다고 가정해 봅시다. Entity를 그대로 가져와서 하나하나 매핑을 해서 userService 로직을 짰는데 어라? User Entity 안에 hobby라는 필드가 추가가 됐습니다.

이렇게 되면 여태껏 만들어 놓은 여러 userService로직 params에 hobby를 하나하나 추가해줘야 합니다. 생각만 해도 귀찮죠...

이를 해결하기 위해 userDto를 만들었습니다. userDto 에 Name, Age, Password를 타입과 함께 넣어주고 userService 로직 params에 userDto를 넣어준 뒤, 매핑해서 사용하면됩니다. 어라? hobby필드가 추가됐어? 그렇다면 userDto에 hobby만 넣어주면 모든 userDto를 params로 갖는 userService로직에 적용이 되죠.

 

 

 

 

 

이번 시간에는 Entity 와 Dto에 대해서 정리해봤습니다. 다음과 같이 프로젝에서도 Dto를 기능별로 많이 사용하고 있는데요, 이번 정리를 통해 Dto의 장점이 무엇인지, 왜 써야하는지 제대로 알 수 있었습니다. 읽어주셔서 감사합니다!

 

프로젝트 내부의 user Dto

 

 

 

 

 

 

 

반응형