[ 바로가기 목차 ]
[ 들어가기 ]
여러 테이블 혹은 DB에서 데이터를 가져와서 원하는 작업을 하는 방법에는 여러가지가 있습니다. 가져온 데이터를 원하는 형식이나 여러 객체를 하나의 객체로 합치는 일은 매우 흔한 일입니다.
이때
이러한 매핑 작업을 직접 개발자가 하게 된다면 문제가 발생할 수 있습니다.
- 코드의 중복
- 생산성 저하
- 실수로 인한 데이터 누락
- 복잡한 로직까지 추가된다면 코드 가독성 저하
이러한 문제를 해결하기 위해 스프링(Spring)에서는 라이브러리를 지원합니다.
- Mapstruct
- ModelMapper
맵 스트럭트(MapStruct) 와 모델 매퍼(ModelMapper)의 차이점을 간단하게 설명하자면 객체의 생성 방식에 조금의 차이를 가지고 있습니다.
모델 매퍼(ModelMapper)는 reflection으로 모델을 매핑하는데, 리플렉션은 런타임(runtime)시점에 동적으로 클래스의 정보를 추출해서 사용하기에, 성능이 그렇게 좋지 않다. (런타임 시점에 리플렉션으로 매핑이 결정되기에 실제 매핑 로직을 파악하기 어려움
맵 스트럭트(MapStruct)는 Annotation Processor를 이용해서 컴파일(compile)시점에 매핑 클래스를 생성해 만든 구현체를 런타임에 사용하기에 성능상의 이슈가 없다.
[ 알아보기 ]
프로젝트의 버전 및 상황에 따라 변경될 수요가 있는 코드로 예시 및 참고로만 보시길 권장합니다.
[ 의존성 설정 ]
gradle
buildscript {
ext {
mapstructVersion = '1.3.1.Final'
}
}
...
// Mapstruct
implementation "org.mapstruct:mapstruct:${mapstructVersion}"
annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
testAnnotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
...
compileJava {
options.compilerArgs = [
'-Amapstruct.suppressGeneratorTimestamp=true',
'-Amapstruct.suppressGeneratorVersionInfoComment=true'
]
}
[ 출처 : https://coding-start.tistory.com/349]
implementation 'org.mapstruct:mapstruct:1.4.2.Final'
annotationProcessor "org.mapstruct:mapstruct-processor:1.4.2.Final"
annotationProcessor(
'org.projectlombok:lombok',
'org.projectlombok:lombok-mapstruct-binding:0.1.0',
'org.mapstruct:mapstruct-processor:1.4.2.Final'
)
// 테스트에서 사용할 경우
//testAnnotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
[ 출처 : https://velog.io/@backtony/Spring-Mapper-Mapstruct-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0]
(롬복이 추가된 이유는 Dto 필드의 get method가 있어야 field와 매칭이 될 수 있다. setter는 선택적 )
Sample Code
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface CarMapper {
CarResponse toDto(Car car);
@Mappings({
@Mapping(target = "loginId", source = "createDto.loginId"),
@Mapping(target = "id", ignore = true)
})
Car toEntity(CarRequestDto createDto, IdentityVerificationResponse identityVerification);
}
윗 코드에서 의존성 등록이 끝났으면 MapStruct를 사용할 수 있다.
[ 어노테이션 간단 설명 ]
@Mapper : MapStruct를 사용하기 위한 인터페이스 정의
빌드 시 해당 어노테이션을 찾아 xxImpl 형태로 구현체를 모두 만들고, componentModel의 속성을 spring으로 주면 Impl은 스프링의 싱글톤으로 관리 (@Component)
@Mappings : 여러 조건을 묶기 위한 어노테이션
@Mapping : 매핑에 관한 조건 설정
- ignore : 소스에는 있지만 타겟 무시
- source : 변수 명이 다를 경우 매핑 설정
윗 코드를 빌드하게 되면 Impl 형태의 코드가 generated 폴더에 생성되는데, 이렇게 세팅을 해주면 맵스트럭트를 이용해서 객체를 간편하게 매핑 가능하다.
( 코드는 임의로 작성한 것이라 신경쓰지 않으시면 됩니다.)
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2022-05-08T13:01:47+0900",
comments = "version: 1.4.2.Final, compiler: javac, environment: Java 11.0.11 (AdoptOpenJDK)"
)
@Component
public class CarMapperImpl implements CarMapper {
@Override
public Car to(CarDto carDto) {
if ( carDto == null ) {
return null;
}
Car.CarBuilder car = Car.builder();
if ( createDto != null ) {
member.loginId( createDto.getLoginId() );
}
if ( identityVerification != null ) {
car.name( identityVerification.getPhoneNumber() );
car.color( identityVerification.getName() );
}
return car.build();
}
// ... 생략
}
[ 마무리 ]
이런 형태로 간단하게 객체를 매핑할 수 있는 장점이 있습니다.
덕분에 개발자의 생산성도 올라가고 잘못된 데이터를 넣을 위험도 많이 줄어들었습니다. 위에 정리한 내용 외에 다양한 속성을 지원해주니 필요에 의한 속성 사용이 권장됩니다.
참고 레퍼런스 https://meetup.nhncloud.com/posts/213
https://coding-start.tistory.com/349
https://velog.io/@backtony/Spring-Mapper-Mapstruct-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0
'스프링' 카테고리의 다른 글
[#2] Spring Securiy Context Holder (0) | 2023.01.03 |
---|---|
[#1] Spring Security - 시큐리티는 무엇이고, Filter는 무엇인지 (0) | 2022.12.29 |
[10분 테코톡] - @JDK Dynamic Proxy & CGLIB (0) | 2022.12.22 |
[10분 테코톡] - @Transactional (0) | 2022.12.21 |
Spring- Lombok의 이해와 @Annotation (계속 추가합니다.) (0) | 2022.12.14 |