MapStruct를 활용해서 손쉽게 매핑하기
Spring Project에서 레이어 간 이동을 할 때, DTO를 생성해서 데이터를 이동시키곤 합니다. DTO 내부에 필드의 갯수가 정말 많다면, 단지 데이터를 이동시키는 코드만 작성하는데에도 정말 많은 시간이 소요되는데, 이러한 노가다성 작업은 MapStruct를 활용하여 쉽게 처리할 수 있습니다. 앞으로 몇 개의 게시글을 통해 MapStruct를 활용하는 법에 대한 내용을 작성할 예정입니다. 전반적인 내용은 MapStruct 공식 문서를 기반으로 작성되었습니다.
1. MapStruct란?
MapStruct는 configuration 접근 방식의 규칙을 기반으로 한 Java 빈 타입의 매핑 구현을 단순화해주는 코드 생성기입니다. 생성된 매핑 코드는 일반 메서드 호출을 사용하므로, 빠르고 type-safe하며 이해하기 쉽습니다.
스프링에서 코드 작업을 하다 보면, 레이어 간 이동에서 다양한 DTO가 생기게 됩니다. Controller에서 Service로, Service에서 Repository로 데이터가 이동하는 과정에서, 필드 간 차이가 많지 않다면 매핑 과정에서 노가다성 코드만 늘어나게 되는데, 이러한 상황에서 MapStruct를 통해 객체 내 많은 필드에 대해서 쉽게 매핑을 할 수 있습니다.
2. MapStruct 설정하기
MapStruct를 활용하기 위해서는 아래와 같이 dependency를 설정해야 합니다.
implementation "org.mapstruct:mapstruct:1.5.2.Final"
annotationProcessor "org.mapstruct:mapstruct-processor:1.5.2.Final"
testAnnotationProcessor "org.mapstruct:mapstruct-processor:1.5.2.Final"
implementation 'org.projectlombok:lombok-mapstruct-binding:0.2.0'
annotationProcessor 'org.projectlombok:lombok-mapstruct-binding:0.2.0'
Lombok과 MapsStruct을 동시에 사용하는 경우, Lombok이 제대로 작동하지 않는 문제가 발생할 수 있는데, 이러한 상황에서 lombok-mapstruct-binding을 dependency에 추가하여 문제를 해결할 수 있습니다.(dependency 순서에 따라 이 문제는 발생하지 않을 수 있습니다.
3. Mapper 생성하기
인터페이스 또는 추상클래스에 @Mapper 어노테이션을 붙임으로써, 손쉽게 Mapper를 만들 수 있습니다. Mapper를 만들게 되면, 자동으로 MapperImpl 이 생성되었음을 확인할 수 있습니다.
- 사용자가 생성한 Mapper
@Mapper
public interface CarMapper {
@Mapping(target = "manufacturer", source = "make")
@Mapping(target = "seatCount", source = "numberOfSeats")
CarDto carToCarDto(Car car);
@Mapping(target = "fullName", source = "name")
PersonDto personToPersonDto(Person person);
}
- 생성된 MapperImpl (generated 패키지에 생성됨)
// GENERATED CODE
public class CarMapperImpl implements CarMapper {
@Override
public CarDto carToCarDto(Car car) {
if ( car == null ) {
return null;
}
CarDto carDto = new CarDto();
if ( car.getFeatures() != null ) {
carDto.setFeatures( new ArrayList<String>( car.getFeatures() ) );
}
carDto.setManufacturer( car.getMake() );
carDto.setSeatCount( car.getNumberOfSeats() );
carDto.setDriver( personToPersonDto( car.getDriver() ) );
carDto.setPrice( String.valueOf( car.getPrice() ) );
if ( car.getCategory() != null ) {
carDto.setCategory( car.getCategory().toString() );
}
carDto.setEngine( engineToEngineDto( car.getEngine() ) );
return carDto;
}
@Override
public PersonDto personToPersonDto(Person person) {
//...
}
private EngineDto engineToEngineDto(Engine engine) {
if ( engine == null ) {
return null;
}
EngineDto engineDto = new EngineDto();
engineDto.setHorsePower(engine.getHorsePower());
engineDto.setFuel(engine.getFuel());
return engineDto;
}
}
※ MapStruct를 활용해서 매핑을 처리하다 보면, 잘못된 설정으로 인해 의도하지 않은 필드에 매핑되는 경우가 가끔 있습니다. 매핑 메서드를 생성한 이후에는 생성된 MapperImpl을 꼭 확인해서 의도한대로 모든 필드들이 잘 매핑되었는지 확인하는 것이 좋습니다.
3.1. Mapper를 Bean으로 생성하고자 하는 경우
Mapper를 생성할 때 Bean으로 지정하고자 한다면, @Mapper 어노테이션 지정 시 componentModel을 spring으로 지정하면 됩니다.
@Mapper(componentModel = "spring")
3.2. Mapper에서 다른 클래스 내의 메서드 또는 Mapper를 사용하고자 하는 경우
Mapper에서 매핑 시 Mapper 외부의 메서드를 사용하고자 하는 경우, @Mapper 의 property 중 하나인 uses를 활용하여 다른 클래스들을 지정할 수 있습니다. 여러 클래스를 활용하고자 한다면, 중괄호{}를 통해 지정해야 합니다.
@Mapper(uses=DateMapper.class)
public interface CarMapper {
CarDto carToCarDto(Car car);
}
2023.01.07 - [Spring] - [Java/Spring] (1) MapStruct를 활용해서 손쉽게 매핑하기 ✅
2023.01.08 - [Spring] - [Java/Spring] (2) MapStruct - Mapping 필드 정의하기
2023.01.13 - [Spring] - [Java/Spring] (3) MapStruct - Mapping 필드 정의하기 2
2023.01.13 - [Spring] - [Java/Spring] (4) MapStruct - Mapper 세부 설정
'Java, Kotlin, Spring' 카테고리의 다른 글
[Java/Spring] Resilience4j - Circuit Breaker (1). 정의 (0) | 2023.02.11 |
---|---|
[Java/Spring] (4) MapStruct - Mapper 세부 설정 (0) | 2023.01.13 |
[Java/Spring] (3) MapStruct - Mapping 필드 정의하기 2 (0) | 2023.01.13 |
[Java/Spring] (2) MapStruct - Mapping 필드 정의하기 (0) | 2023.01.08 |
[SpringBoot 3.0] SpringBoot 3.0 버전 업그레이드 방법 (0) | 2023.01.05 |