[Spring] 프로젝트 구조 및 쿼리 성능 향상시키기 (1)

한호성·2023년 3월 19일
0

Introduction

회사 프로젝트를 스프린트에 맞춰서 빠르게 진행하다보니, 프로젝트 구조 및 성능이 미흡한 부분이 많다고 생각했습니다. 요번 스프린트에서는 간단한 기능 추가 및 구조,성능의 부족한 부분에 대해서 공부하고, 방향성에 대해 생각하는 기간이라고 생각합니다. 이번 글에서는 QueryDSL를 이용한 쿼리 최적화 및 DTO 부분에 대한 고찰을 다루고자 합니다.
[목차]

  • DTO (Data Transfer Object) 사용범위
  • QueryDSL 와 FetchJoin 이용한 쿼리 최적화 및 가독성 증가

DTO (Data Transfer Object) 사용범위

DTO란?

DTO 란 계층 간 데이터 전송을 위해 도메인 모델 대신 사용하는 개체이다.
DTO의 특징은 다음과 같다.

  • 순수하게 데이터만을 가져야한다.
  • 비지니스 로직을 가지면 안된다.
  • 저장, 검색, 직렬화, 역직렬화 로직만을 가져야한다.
    cf) 직렬화란 DTO를 Byte,Json,Xml등의 형태로 변환하는 것을 의미합니다.

어떤 용어의 정의를 스스로 이야기할 수 있는지 확인해보는 것은 중요하다 생각합니다. 자신이 그 개념을 알고 제대로 설명할 수 있는가?와 연관있기 때문입니다.

DTO 사용의 장점

개인적으로 DTO를 Entity를 대신해서, request에 대한 응답으로 response를 DTO 객체를 내려줘야 한다고 알고는 있지만 정확히 이유를 말하고자 하면 모호한 부분이 있었습니다.

  • Entity 모델을 캡슐화 하여 보호할 수 있다는 점. 즉, Entity가 외부에 노출되지않고 결합을 느슨하게 할 수 있다는 장점이 있습니다.
  • 개발자가 실수할 여지가 있다고 생각합니다. (개인적인 생각입니다.. Entity를 직접적으로 사용할 때, Entity값에 영향을 주는 의도치 않은 코드가 있는 상태에서, Transcation으로 관리하고 있다면, dirty check에 의해 값이 변경될 위험을 갖고 있습니다 DB 정합성에 문제가 생길 수 있습니다.)
  • Entity를 controller layer까지 의존성을 갖게 함으로써, 결합도를 높일 수 있습니다. 좋은 구조가 아니게 됩니다.

DTO의 사용영역에 대한 고찰

좋은 구조에 고민하다보니, DTO를 어느 영역까지 사용해야하는가에 대한 의문까지 도달하게 되었습니다. 검색을 해보니 , 저와 같은 고민을 하는 사람들이 많은 것을 알 수 있었습니다. 결론을 이야기하자면, 어디까지 사용해야한다는 절대적인 내용은 없고, 자신의 상황에 맞춰 사용해라라는 모호한 결론에 도달했습니다.

제 개인적인 생각을 이야기하자면, Service layer까지 DTO로 전달을 하고, Service layer에서 Entity로 변환하여 사용하고, Controller layer 내보낼 때, DTO로 변환해서내 보내야 한다고 생각합니다.
Entity가 상대적으로 더 중요한 개체라 생각하고, Controller와 Entity간의 의존성을 갖게 하는것은 부자연스럽다고 생각합니다. 저와 다르게 생각하는 분들의 의견으로는, DTO를 기준으로 의존성을 갖게되면, Service layer의 함수를 재사용하지 못한다는 의견이 있었습니다. 어느정도 맞는말이라 생각하지만, 보통 하나의 Entity가 하나의 Service를 갖기 때문에, 크리티컬하다고 생각하지는 않다고 생각했습니다.

마틴 파울러가 한 Service layer의 정의를 보면 도메인을 보호하는 layer라고 합니다. 정의를 생각해보면, Entity를 DTO로 변환해서 controller로 보내는게 맞다고 한번 더 생각하게 되었습니다.

DTO <-> Entity 간의 변환

    public static Company of(CompanyRequestDto companyRequestDto, User user) {
        return Company.builder()
                .createUser(user)
                .updateUser(user)
                .companyName(companyRequestDto.getCompanyName())
                .companyType(companyRequestDto.getCompanyType())
                .companyRegNumber(companyRequestDto.getCompanyRegNumber())
                .companyLocation(companyRequestDto.getCompanyLocation())
                .companySector(companyRequestDto.getCompanySector())
                .companySalesScale(companyRequestDto.getCompanySalesScale())
                .companyDescription(companyRequestDto.getCompanyDescription())
                .build();
    }

이런식으로 변환하는 코드를 Company entity 개체 내에서 static 함수로 정의하면서 사용하였습니다. Entity 코드가 requestDto의 의존관계를 갖는 것은 다시 생각해보면 문제가 있다고 생각하게 되었습니다. 변환하는 코드는 service layer에서 진행되어야 한다고 생각했습니다. 반대로, requestDto는 Entity 의존성을 가져도 된다고 생각합니다. 왜냐하면, Dto는 Entity에 비해 순수한 Java class로써, 데이터의 저장, 검색, 직렬화, 역직렬화만의 역할을 갖고 있기 때문입니다.


이어서 다음 글에서는, QueryDsl과 Dto를 활용한 성능 최적화에 대한 의견을 적어보도록 하겠습니다.

Reference

https://tecoble.techcourse.co.kr/post/2021-04-25-dto-layer-scope/
https://hudi.blog/data-transfer-object/

profile
개발자 지망생입니다.

0개의 댓글