on
[Mapper] MapStruct로 Dto/Entity 매핑하기(vs ModelMapper)
[Mapper] MapStruct로 Dto/Entity 매핑하기(vs ModelMapper)
스터디원 중에 한 분이 엔티티매퍼 알려주셔서 와 신세계다 이러고 쓰고 있었는데 예전 프로젝트들 까보니까 익숙하게 ModelMapper 쓰고 있었음...... 스프링 너무 간만인데다 어디 기록도 안 해두니까 다 까먹는다.
그런데 기존에 사용하던 ModelMapper는 modelMapper.map(ENTITY, DTO.Class) 형태로 사용할 때 리플렉션이 일어나서 MapStruct보다 성능이 떨어진다고 한다. 그래서 이번 프로젝트부터는 MapStruct를 사용하기로 했다. 알려주셔서 감사합니다.
mapstruct는 프로젝트를 빌드하면 mapstruct의 @Mapper가 달린 interface의 구현클래스를 자동으로 생성해 준다.
pom.xml
1.4.2.Final 0.2.0 org.mapstruct mapstruct ${org.mapstruct.version} install org.apache.maven.plugins maven-compiler-plugin org.projectlombok lombok ${org.projectlombok.version} org.mapstruct mapstruct-processor ${org.mapstruct.version} org.projectlombok lombok-mapstruct-binding ${org.projectlombok.ombok-mapstruct-binding.version}
properties로 mapstruct 버전 잡아주고, dependency 추가해주고, lombok과 같이 사용하기 위해 build plugin을 저 순서로 설정해줬다.
버전 업그레이드 되면서 이제는 둘이 호환이 잘 된다고 하는데 그래도 mapstruct와 lombok은 쉽게 충돌할 수 있는 친구들이니까 따로 신경 써 줘야 한다. 롬복 @Builder를 사용할 거라 lombok-mapstruct-bind도 넣어준다.
bind dependency를 넣어주지 않으면 mapstruct가 냅다 build만 가져다 써버려서 Impl클래스에 setter가 하나도 안 붙는다.
-> 빈 객체를 리턴하게 됨
mapstruct는 프로젝트를 빌드하면 mapstruct의 @Mapper가 달린 interface의 구현클래스를 자동으로 생성해 준다.
maven에서 빌드를 진행하기 위해 defaultGoal도 설정해 줬다.
이클립스 STS의 경우 클래스 생성위치를 메이븐이 못 잡는다. 때문에 위의 pom.xml에서 build>configuration 안에 따로 패스를 작성해줘야 한다. gradle의 경우 sourceSets로 설정해 주면 된다.
${project.build.directory}/generated-sources/annotations
Dto/Entity 준비
Dto를 Entity로 바꿔주는 함수를 작성할 건데 내 경우는 각각 대응되는 필드명이 같다.
MemberDtoMapper.java
import org.mapstruct.Mapper; import org.mapstruct.Mapping; @Mapper public interface MemberDtoMapper{ MemberDtoMapper INSTANCE = Mappers.getMapper(MemberDtoMapper.class); Member toEntity(MemberSaveRequestDto requestDto); }
이미 마이바티스 매퍼를 사용하고 있어서 bean이름이 겹치는 걸 방지하려고 MemberDtoMapper로 네이밍했다.
(MemberRepository는 JpaRepository가 이미 쓰고 있음...)
다음과 같이 본인클래스를 인스턴스로 설정해주고, Dto를 파라미터로 받아 Entity를 리턴하는 toEntity()를 선언해준다.
만일 파라미터와 반환타입에서 대응되는 필드 이름이 다르다면 메소드 위에 @Mapping(source = "dtoname", target = "entityname") 처럼 어노테이션을 달아주면 된다. (toEntity의 경우)
Generic Interface를 선언해서 각각 상속받게 하면 조금 더 편하게 쓸 수 있기는 한데 일단은 환경만 구성해 놓는 거라 일단 그냥 작성해줬다.
MemberDtoMapperImpl.java
Maven update-Maven Clean-Maven Build
빌드하면 이클립스 STS 기준 target/generated-sources/${본인패키지구조}/에 Impl클래스가 생긴다. 인텔리제이는 메이븐에서 인식해주는 경로로 잘 잡힌다.
MemberDtoMapperTest.java
@Slf4j public class MemberDtoMapperTest { @Test public void toEntity() { MemberSaveRequestDto requestDto = MemberSaveRequestDto.builder() .name("test").tel("test").address("test").email("test").password("test") .build(); Member member = MemberDtoMapper.INSTANCE.toEntity(requestDto); assertEquals(member.getEmail(), "test"); } }
toEntity()가 리턴한 Member객체에 Dto값이 제대로 들어가 있다.
만약 Impl이 만들어졌는데도 못 찾는다는 에러가 뜨면 pom.xml에 있는 source 경로 다시 확인해보고 업데이트클린빌드새로고침 반복하기......
잘 되던 게 어느날 갑자기 에러 뜨면 업데이트클린빌드새로고침하기......
이클립스는 메이븐이랑 SourceSet이 다른 거 같다던데 진짜 규정만 아니었어도 갈아탔다
from http://butfound.tistory.com/25 by ccl(A) rewrite - 2021-11-07 20:27:47