DTO - Entity 변환 메서드 구현 위치는 어디가 좋을까?

smc2315·2023년 6월 7일
2

spring-boot

목록 보기
1/7
post-thumbnail

1. DTO, Entity

DTO란

DTO(Data Transfer Object)란 계층간 데이터 교환을 위해 사용되는 객체이다. DB에서 꺼낸 데이터를 저장하는 EntityController와 같은 클라이언트단과 직접 마주하는 계층에 직접 전달하는 대신 DTO를 통해 데이터를 교환한다.

DTO의 역할은 계층간 데이터 교환이 이루어질 수 있도록 하는 객체이기 때문에, 특별한 로직을 가지지 않는 순수한 객체여야 한다.

DB에서 꺼낸 값을 DTO에서 임의로 조작할 필요가 없기 때문에 DTO에서는 Setter를 만들 필요가 없고 생성자 또는 Builder 패턴을 통해 값을 할당한다.

Entity와 DTO를 분리하는 이유

관심사의 분리
Entity는 주로 데이터베이스와 관련된 비즈니스 개체를 표현하는 데 사용된다. 반면에 DTO는 클라이언트와의 데이터 교환을 위해 사용된다. Entity는 데이터의 상태와 비즈니스 규칙을 포함하고 있으며, DTO는 특정 작업을 수행하기 위해 필요한 데이터만 포함한다. EntityDTO를 분리함으로써 관심사를 분리하고 각각의 역할에 집중할 수 있다.

유연성과 확장성
Entity의 값이 변경되는 경우 Repository 클래스의 Entity Managerflush가 호출될 때 DB에 변경값이 반영된다. DTO를 사용하여 Entity의 변경으로 인한 영향을 최소화할 수 있고 클라이언트에게 전달되는 데이터를 쉽게 조정하고 확장할 수 있다.

데이터 은닉
Entity는 데이터베이스와 직접적으로 관련되어 있으므로, 일부 데이터는 애플리케이션의 내부에서만 사용되는 경우가 있을 수 있다. DTO를 사용하면 클라이언트에게 필요한 데이터만을 선택하여 전달하므로, 불필요한 정보 노출을 방지하고 데이터 은닉을 보장할 수 있다.


2. DTO <-> Entity

아래의 코드는 Post Entity에 대한 DTO 클래스이다.
PostRequest는 사용자의 게시글 조회 요청에 대한 DTO이고,
PostResponse는 조회된 게시판을 클라이언트로 리턴할때 사용되는 DTO다.

@Getter
@Builder
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class PostRequest {

    private String title;
    private String content;
    private String writer;

    public Post toEntity() {
        return Post.builder()
                .title(title)
                .content(content)
                .writer(writer)
                .build();
    }
}
@Getter
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class PostResponse {

    private Long id;
    private String title;
    private String content;
    private String writer;

    @Builder
    public PostResponse(Post post) {
        this.id = post.getId();
        this.title = post.getTitle();
        this.content = post.getContent();
        this.writer = post.getWriter();
    }
}

PostRequesttoEntity() 메서드를 통해 Entity로 변환이 가능하다.

PostResponse에서는 Builder 생성자를 통해 EntityDTO로 변환 가능하다.

이러한 DTO들을 작성하고 난 후 Entity - DTO간의 변환 작업을 어느 레이어에서 처리해야 될지에 대한 궁금증이 생겼다.


3. Controller Layer에서의 변환 작업

@PostMapping
public ResponseEntity<Long> create(@RequestBody PostRequest PostRequest) {
	return ResponseEntity.ok().body(postService.create(PostRequest.toEntity()));
}
@GetMapping("/{postId}")
public ResponseEntity<PostResponse> getPost(@PathVariable Long postId) {
	Post post = postService.getPost(postId);
	return ResponseEntity.ok().body(PostResponse.builder().post(post).build());
}

위의 두 코드는 Controller Layer에서 Entity - DTO간 변환 작업을 처리하는 방식이다.

장점

  • Presentation Logic 분리: DTO는 주로 클라이언트에게 데이터를 전달하기 위한 용도로 사용되므로, Controller에서 DTO와의 변환 작업을 수행하면 Presentation LogicController 내부로 격리시킬 수 있다.

  • Controller 레벨에서 유효성 검사: Controller에서 DTO와의 변환 작업을 수행하면, 변환 과정에서 유효성 검사를 수행할 수 있습니다. 이로써 더욱 엄격한 데이터 유효성을 적용할 수 있습니다.

  • Service의 독립성 유지: Service 레이어에서 Entity에만 의존하고 DTO 변환 작업을 수행하지 않으면, Service 레이어의 독립성과 재사용성이 높아진다.

단점

  • 반복적인 작업: DTOEntity 간의 변환 작업은 여러 Controller 메소드에서 반복적으로 수행되어 코드 중복이 발생한다.

  • 비즈니스 로직과 혼재: DTO와의 변환 작업을 Controller에서 수행하면, 비즈니스 로직과 DTO 변환 로직이 혼재되어 코드의 가독성과 유지보수성이 저하된다.


4. Service Layer에서의 변환 작업

 @PostMapping
public ResponseEntity<Long> create(@RequestBody PostRequest PostRequest) {
	return ResponseEntity.ok().body(postService.create(PostRequest));
}
@GetMapping("/{postId}")
public ResponseEntity<PostResponse> getPost(@PathVariable Long postId) {
	return ResponseEntity.ok().body(postService.getPost(postId));
}

위의 두 코드는 Service Layer에서 Entity - DTO간 변환 작업을 처리하는 방식이다.

장점

  • 단일 장소에서 변환 작업 관리: Service는 비즈니스 로직을 관리하는 곳이므로, DTOEntity 간의 변환 작업을 Service 내에서 수행하면 변환 작업을 단일한 장소에서 관리할 수 있다.

  • 코드 중복 감소: Service 내에서 DTOEntity 간의 변환 작업을 수행하면, 여러 Controller 메소드에서 동일한 변환 작업을 반복할 필요가 없어진다.

  • 비즈니스 로직과 분리: DTO와의 변환 작업을 Service에서 수행하면, 비즈니스 로직과 변환 로직을 분리하여 코드를 관리할 수 있다.

단점

  • Service의 복잡도 증가: Service에 변환 작업까지 포함되면 역할이 복잡해질 수 있다.

  • 코드 재사용성 하락: Service에서 특정 DTO에 의존하게 되면 여러 종류의 Controller에서 해당 Service를 이용할 수 없어 코드 재사용성이 떨어지게 된다.


5. 결론

Controller에서 변환 작업을 수행하는 것과 Service에서 변환 작업을 수행하는 것은 각각 장단점이 존재하여 프로젝트의 구조와 성격에 따라 선택을 하는 것이 필요하다.

하지만 일반적으로 Service에서 변환작업을 수행하는 것이 다음과 같은 이점이 존재한다.

  • 레이어 간의 역할 분리: Bussiness LogicPresentation Logic을 분리하여ControllerService간의 역할을 명확히 구분한다.

  • 단일 책임 원칙: Service는 주로 비즈니스 로직을 처리하는 컴포넌트로 간주되며, DTOEntity 간의 변환 작업은 비즈니스 로직과 관련이 있다. 따라서 변환 작업을 Service에서 처리함으로써 단일 책임 원칙을 준수할 수 있다.

profile
개발일지

0개의 댓글