[Spring] MapStruct란?

김진회·2022년 11월 24일
0

jpa

목록 보기
4/5

1. MapStruct란?

DTO, Entity간의 매핑을 간편하게 할 수 있도록 지원해주는 라이브러리다.
@Mapper를 사용하고 @Mapping으로 컬럼에 대한 추가적인 설정을 해주면 컴파일 시, MapperImpl을 생성해준다.

  • 장점
    • 컴파일 시 오류를 확인할 수 있다.
    • 생산성이 향상된다.
    • 생성된 매핑 코드를 눈으로 직접 확인할 수 있다.
    • 리플렉션(Reflection)을 사용하지 않아 매핑 속도가 빠르다.
      리플렉션: 구체적인 클래스 타입을 알지 못해도, 그 클랙스의 메소드, 타입, 변수들에 접근할 수 있도록 해주는 자바 API

2. 기존의 Mapping 방법

MapStruct를 자세히 알아보기 전에 기존의 Mapping 방법을 알아보자.
기존 DTO와 Entity간의 매핑 방법은 객체 생성 후, setter사용, @Builder사용, ModelMapper가 있다. 본인은 그 중 @Builder를 이용했다.

//DTO/Entity에 @Builder 선언 후,

RecordDto.builder().
	.id(Record.getId())
    .name(Record.getName())
    .myTreeId(Record.getMyItemId())
    .build();

위 방법은 필드가 많아진다면 꽤나 코드가 길어지고 그만큼 실수할 가능성이 생긴다.


3. MapStruct를 이용한 방법

1) 환경설정

Gradle에 mapstruct에 대한 dependencies를 설정한다

dependencies {
    // lombok
    implementation 'org.projectlombok:lombok:1.18.22'
    annotationProcessor 'org.projectlombok:lombok:1.18.22'
    annotationProcessor 'org.projectlombok:lombok-mapstruct-binding:0.2.0' // v1.18.16+ 부터

    // mapstruct
    implementation 'org.mapstruct:mapstruct:1.4.2.Final'
    annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
}

2) EntityMapper 인터페이스 구현

Generic을 이용해서 MapStruct를 위한 인터페이스를 구현한다.
본인은 보통 이런 파일은 /global/common/mapper 폴더를 생성해서 여기에 작성한다.

public interface EntityMapper<D, E> {
    E toEntity(final D dto);
    D toDto(final E entity);
}

3) Mapper 작성

Product에 대한 Mapper을 작성해보겠다.
예시) RecordDto는 필드로 id, name, itemId, myItemid을 갖는다. MyItem은 itemId를 필드로 갖는다.

@Mapper
public interface RecordMapper extends EntityMapper<RecordDto, Record> {
    RecordMapper mapper = Mappers.getMapper(RecordMapper.class);

    @Override
    @Mapping(target = "userId", ignore = true)
    @Mapping(source = "myItem.item.id", target = "itemId")
    @Mapping(source = "myItem.id", target = "myItemId")
    RecordDto toDto(final Record entity);

    @Override
    @Mapping(source = "itemId", target = "myItem.item.id")
    @Mapping(source = "myItemId", target = "myItem.id")
    Record toEntity(final RecordDto dto);
}
  • 작성 방법
    • @Mapper: 해당 인터페이스의 구현체를 생성한다.
    • @Mapping: 매핑 과정에서 특정 필드를 ignore하거나 따로 설정한다.
    • 정책
      • unmappedSourcePolicy
        • IGNORE, WARN, ERROR
        • 매핑시 Sourece.aFiled가 사용되지 않는다면 컴파일 오류
        • ERROR로 설정시 매핑되지 않았다면 컴파일 오류가 발생
      • unmappedTargetPolicy
        • IGNORE, WARN, ERROR
        • 매핑시 Target.aFiled가 사용되지 않는다면 컴파일 오류
      • typeConversionPolicy
        • IGNORE, WARN, ERROR
        • 타입 변환시 유실이 발생할 수 있을 때 정책
        • long -> int로 값을 넘길 때 유실이 발생, 이런 경우에 정책을 설정할 수 있다.
    • 전략
      • nullValueMappingStrategy
        • RETURN_NULL(deafult), RETURN_DEFAULT
        • Source가 null일 때 정책이다.
      • nullValuePropertyMappingStrategy
        • SET_TO_NULL(default), SET_TO_DEFAULT, IGNORE
        • Source의 필드가 null일 때 정책

4) 컴파일 및 사용

컴파일 시, build폴더에 MapperImpl이 생성된다. ServiceImpl에서 해당 MapperImpl을 사용하면 된다.
여기서 @Mapping 설정에 따라 Entity->DTO 변환 시, userId는 null로 들어간다.
* userId와 같이 보안에 신경써야 하는 필드는 ignore설정을 해주는 것이 좋다.

//DTO(recordDto) <-> Entity(record)
RecordDto recordDto= RecordMapper.mapper.toDto(record);
Record record= RecordMapper.mapper.toEntity(recordDto);

4. 주의사항

정책을 ERROR로 설정하지 않았으면 컴파일 시, 에러코드가 출력되지만 에러가 발생하는 필드를 제외하고 MapperImpl이 생성된다. 따라서 후에 MapperImpl이 존재하기 때문에 프로그램이 중단되지 않고 그대로 작동된다. 이 MapperImpl은 정상적인 작동을 하는 클래스가 아니니 이를 인지하고 있도록 하자

이를 방지하기 위해 정책을 Error로 설정해 오류가 있으면 파일 생성을 중단시킬 수 있다.


참조

https://meetup.toast.com/posts/213

https://joojimin.tistory.com/19

profile
SSAFY 7기. HMG. 협업, 소통, 사용자중심

0개의 댓글