Dev

[Spring Boot] mapstruct 적용 시 주의점

Ryan Woo 2021. 3. 17. 17:28

최근 객체 맵핑 라이브러리인 ModelMapper의 대안으로 MapStruct가 제법 사용 되는것 같아서 적용 시 문제가 생겼던 부분을 정리해 놓는다.

ModelMapper : modelmapper.org/

 

ModelMapper - Simple, Intelligent, Object Mapping.

Why ModelMapper? The goal of ModelMapper is to make object mapping easy, by automatically determining how one object model maps to another, based on conventions, in the same way that a human would - while providing a simple, refactoring-safe API for handli

modelmapper.org

MapStruct : mapstruct.org/

 

MapStruct – Java bean mappings, the easy way!

Java bean mappings, the easy way! Get started Download

mapstruct.org

* baeldung에 객체 매핑 라이브러리들의 성능을 테스트한 결과 참고 : www.baeldung.com/java-performance-mapping-frameworks

 

Performance of Java Mapping Frameworks | Baeldung

MapStruct comes out on top, followed by JMapper as a close second. The other libraries follow far behind: Orika, ModelMapper, and Dozer.

www.baeldung.com


* 예시는 maven 기준이지만 gradle도 동일합니다.
1. build plugin

<!-- lombok 먼저 선언된 경우 -->
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <configuration>
    <annotationProcessorPaths>
      <path>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok-mapstruct-binding</artifactId>
        <version>0.2.0</version>
      </path>
      <path>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.18</version>
      </path>
      <path>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct-processor</artifactId>
        <version>1.4.2.Final</version>
      </path>
    </annotationProcessorPaths>
  </configuration>
</plugin>


*. lombok 버전이 1.18.16 이상인 경우, annotationProcessorPaths 에 lombok-mapstruct-binding 을 추가해줘야 한다.
*. 순서에 영향 : 위와 같이 mapstruct 가 lombok 뒤에 오는 경우, target class 에 @Builder 가 있어도 무시하고 생성자 + setter 를 사용하므로 정상적으로 Mapper 클래스를 Generation 하기 위해서 @NoArgsConstructor, @Setter 가 필요하다.(최근 Setter 사용을 지양하는 추세라 선호하지 않는다. mapstruct를 먼저 오도록 해서 @Builder 방식으로 Generation 되도록 하는 것을 개인적으로 추천한다.)

<!-- mapstruct 먼저 선언된 경우 -->
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <configuration>
    <annotationProcessorPaths>
      <path>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct-processor</artifactId>
        <version>1.4.2.Final</version>
      </path>
      <path>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok-mapstruct-binding</artifactId>
        <version>0.2.0</version>
      </path>
      <path>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.18</version>
      </path>
    </annotationProcessorPaths>
  </configuration>
</plugin>


2. target class

@Getter
@ToString
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
public class TempUserDTO {
    private String email;
    private String name;
    private String introduce;
    private Timestamp regDt;
    private Timestamp modDt;
}

* @AllArgsConstructor 를 Private 로 설정해서 생성자 방식은 사용하지 못하도록 막고 @Builder 만 사용하도록 해서 전체적으로 코드 패턴을 맞춤.

3. Mapper 작성

@Mapper(componentModel = "spring")
public interface TempUserModelMapper {
    TempUserDTO tempUserVOToTempUserDTO(TempUserVO tempUserVO);
}

* (componentModel = "spring") 이게 없으면 스프링 bean 으로 등록되지 않으므로 명시해 줘야함.