본문 바로가기
[JAVA]/JAVA 기본

DTO 클래스 ↔︎ Entity 클래스 Mapping에 대한 정리(ModelMapper, Mapstruct, 수동매핑)

by 황원용 2023. 5. 8.
728x90

DTO(Data Transfer Object)란?

  • 데이터를 전송하기 위한 객체이다.
  • 클라이언트에서 서버 쪽으로 요청하는 요청 데이터와 서버에서 클라이언트 쪽으로 전송하는 응답 데이터 사이에서 사용된다.
  • 어떠한 비즈니스 로직도 가져서는 안 되며 저장, 검색, 직렬화, 역직렬화 로직만 가져야 한다.

 

DTO 클래스를 활용할 때의 장점

  • 요청 및 응답 데이터를 하나의 객체로 전달하거나 받음으로써 코드를 간결하게 유지할 수 있다.
    • HTTP 요청 및 응답을 받는 핸들러 메서드는 말 그대로 요청 및 응답을 받는 것이 주목적이기 때문에 최대한 간결하게 작성되어야 한다.
  • 데이터의 유효성(Validation) 검증을 단순화할 수 있다.
    • DTO 클래스를 사용하면 유효성 검증 로직을 DTO 클래스로 빼내어 핸들러 메서드의 간결함을 유지할 수 있게 도와준다.

 

역직렬화와 직렬화

역직렬화(Deserialization)

  • JSON 형식의 데이터를 DTO 같은 자바의 객체로 변환하는 것을 말한다.

 

직렬화(Serialization)

  • 자바의 객체를 JSON 형식으로 변환하는 것을 말한다.

 

 

Entity 클래스

  • 실제 데이터베이스 테이블과 매핑되는 핵심 클래스이다.
  • 데이터베이스의 테이블에 존재하는 칼럼들을 필드로 가지는 객체이다.
  • 데이터베이스의 테이블과 1:1 매핑이 되므로 테이블이 가지고 있지 않는 칼럼을 필드로 가져서는 안 된다.
  • Entity 클래스는 데이터베이스의 영속성을 목적으로 사용하는 객체이기 때문에 요청, 응답 등에 사용되어서는 안 된다.
    • DB로부터 조회된 Entity를 그대로 View로 넘기게 된다면 불필요한 정보가 노출되는 등 코드가 지저분해질 수 있기 때문이다.
  • setter 메서드의 사용을 지양해야 한다. 변경되지 않는 인스턴스에 대해서도 setter 메서드를 사용하면 접근이 가능하기 때문에 객체의 무결성, 일관성 등 변조되지 않음을 보장할 수 없게 된다. 따리서 생성자를 이용해 초기화하여 불변 객체로 사용하는 것이 좋다.
    • Builder를 사용하면 멤버 변수가 많아지더라도 어떤 값을 어떤 필드에 넣는지 코드를 통해 육안으로 확인 가능할 뿐만 아니라 필요한 값만을 넣는 것이 가능하다.

 

 

정리

  •  DTO 클래스를 사용하면 코드를 간결화하고 직관적으로 바꿀 수 있음과 동시에 요청 및 응답 데이터와 Entity 클래스 등이 여러 곳에서 사용되는 지저분한 상황을 막을 수 있다.

 

 

DTO 클래스와 Entity 클래스의 대표적인 매핑 방법

1. 자동 매핑

  • ModelMapper
  • Mapstruct

2. 수동 매핑

  • 생성자 메서드
  • Builder 패턴

 

자동 매핑(ModelMapper, Mapstruct)

반복 작업의 큰 부분을 차지하는 객체 간 매핑을 자동으로 가능하게 한다.

 

ModelMapper

  • 간결한 코드 작성이 가능하다.(자동 매핑의 장점)
  • 런타임 시에 객체를 분석하고 매핑하므로 Lombok 라이브러리와 충돌 없이 사용 가능하다.
  • Reflection API를 사용하여 객체 필드의 정보를 추출하고 Map을 만들어 그 정보를 기준으로 매핑하는 방식이기 때문에 다른 방식보다 오버헤드가 많아 성능이 좋지 않다.

 

MapStruct

  • 간결한 코드 작성이 가능하다.(자동 매핑의 장점)
  • 객체 필드의 변경 사항이 다른 로직에 영향을 주지 않는다.
  • 컴파일 시점에 코드를 생성하면서 타입이나 매핑이 불가능한 상태 등의 문제가 발생한 경우 컴파일 에러를 발생시킨다.
  • 전혀 다른 형태의 필드 매핑을 시도하는 경우 해결은 가능하나 매핑 로직이 매우 복잡해진다.

 

두 기술의 간단한 차이점

  • 두 기술 중 Mapstruct의 인기가 압도적으로 높다. 왜?
  • Mapstruct는 코드 생성방식이며, ModelMapper는 Reflection 기반으로 동작한다.
  • 코드 생성 방식은 생성된 코드를 통해 매핑 로직을 쉽게 파악할 수 있다.
  • Reflection 기반의 동작은 매핑 로직을 쉽게 파악하기 힘들다.
  • Mapstruct는 컴파일 시점에 애너테이션을 읽어 구현체를 만들어내기 때문에 Reflection이 발생하지 않는다.
  • ModelMapper는 매핑이 일어날 때 Reflection이 발생한다.

 

Reflection이란?

  • 클래스의 타입을 구체적으로 알지 못하더라도 그 클래스의 메서드, 타입, 변수에 접근할 수 있게 해주는 자바 API를 말한다.

 

 

ModelMapper와 비교했을 때 Mapstruct의 장점

  • Mapstruct의 처리속도가 압도적으로 빠르다.
  • Mapstruct는 컴파일 시 오류를 확인할 수 있다.
  • Mapstruct는 디버깅이 쉽다.
  • Mapstruct는 생성된 매핑 코드를 눈으로 직접 확인할 수 있다.

 

 

수동 매핑

  • 객체 변환을 위한 별도의 과정을 거치지 않고 메서드 호출만으로 매핑이 되기 때문에 성능에 영향을 주지 않는다.
  • 이름이 다른 필드 간 매핑도 Getter 등을 작성하여 매핑이 가능하다.
  • 매핑하는 필드 타입이 다른 경우 컴파일 타임에 이를 식별할 수 있다.
  • 변경 사항이 있을 시 매번 수정해줘야 한다.
  • 필드가 너무 많거나 조합하는 형태의 데이터가 많다면 휴먼 에러가 발생할 가능성이 높다.(잘못된 매핑, 데이터 누락 등)

 

생성자 사용

@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class OrderDto {
    private Long id;
    private String name;
    private Long quantity; 
    private OrderStatus orderStatus;
    private LocalDateTime createAt;

    private MemberDto memberDto;
}

OrderDto orderDto = new OrderDto(1, "computer",
 2, OrderStatus.CHECKED, new LocalDateTime(), new MemberDto("10", "kim", "seoul")
  • 코드가 간결하지만 필드가 많으면 매우 지저분해보인다.
  • 어떤 자리에 어떤 값을 넣어줘야 할지 알고 있어야 한다.
  • 필드가 Setter 메서드로 인해 변경될 가능성이 있다.

 

 

Builder 패턴 사용

OrderDto orderDto = OrderDto.builder()
        .id(1)
        .name("computer")
        .quantity(2)
        .orderStatus(OrderStatus.CHECKED)
        .createAt(LocalDateTime.now())
        
        .memberDto(
                MemberDto.builder()
                		.id(10)
                        .name("kim")
                        .address("seoul")
                        .build())
        .build()
  • 필요한 데이터만 설정할 수 있다.
  • 유연성을 확보할 수 있다.
  • 가독성을 높일 수 있다.
  • 불변성을 확보할 수 있다.

 

 

 

 

 

참고

https://yeoooo.github.io/java/BuilderPattern/

https://velog.io/@sally_devv/Builder-%ED%8C%A8%ED%84%B4-Dto

https://velog.io/@backtony/Spring-Mapper-Mapstruct-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

https://dev-splin.github.io/spring/Spring-ModelMapper,MapStruct/#modelmapper--mapstruct

https://wildeveloperetrain.tistory.com/101

 

728x90