사용자가 입력한 데이터가 담긴 DTO를 DB에 저장할 Entity로 바꾸기 위한, mapstruct 사용법에 대해 정리해보자.
MapStruct 란 ?
프로젝트를 할 때, DTO 와 Entity 를 매핑해야하는 경우가 많은데 이러한 매핑 코드를 작성하는 것은 귀찮고 오류가 발생하기 쉽다. MapStruct 는 이를 자동화하여 작업을 단순화할 수 있게 한다.
자세한 내용은, 아래 공식 문서를 통해 확인할 수 있다.
MapStruct – Java bean mappings, the easy way!
Java bean mappings, the easy way! Get started Download
mapstruct.org
MapStruct 사용하기
1. dependency 추가 및 주의사항( Gradle )
dependencies {
..
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
implementation 'org.mapstruct:mapstruct:1.5.5.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final'
}
그냥 dependency 를 추가하면 되는데 주의할 점은 lombok 과 같이 사용하는 경우이다.
MapStruct가 Lombok 빌더 또는 Lombok Setter를 사용하려는 경우 Lombok이 아직 실행되지 않은 경우 컴파일이 실패하여 문제가 발생할 수 있다.
간단한 방법은 위와 같이 lombok -> mapstruct 순서로 나열하는 것이다. 혹은, MapStruct 의 github을 참조할 수 있다.
2. DTO 및 Entity 생성
- Member
@Getter
public class Member {
private Long id;
private String email;
private String passwd;
private String name;
private String delYN;
@Builder
public Member(String email, String passwd, String name) {
this.email = email;
this.passwd = passwd;
this.name = name;
}
}
- MemberDTO
@Getter
public class MemberDTO {
private String email;
private String passwd;
private String name;
@Builder
public MemberDTO(String email, String passwd, String name) {
this.email = email;
this.passwd = passwd;
this.name = name;
}
}
필수적인 요소만 남겨뒀다.
인터페이스를 생성하여 원하는 기능을 구현할텐데, MapStruct에서 구현체를 자동으로 만드는데 Getter 및 Builder 를 사용하기 때문에 해당 내용까지 적어뒀다. 필요에 따라 validation 등의 어노테이션을 추가할 수 있다.
3. Mapper 인터페이스 구현
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface MemberMapper {
MemberDTO toDto(Member member);
Member toEntity(MemberDTO memberDTO);
List<MemberDTO> toDtoList(List<Member> members);
List<Member> toEntityList(List<MemberDTO> memberDTOS);
}
필수 요소는,
- 인터페이스에 @Mapper를 사용하여 MapStruct를 사용할 수 있도록 한다.
- 바꾸고 싶은 결과를 return 값으로, 바꾸기 전 대상을 매개변수로 넣어 위와 같이 사용할 수 있다.
메서드명에 제약은 없으나 표준 및 가독성을 고려하여 자유롭게 구현하면 된다.
@Mapper 의 componentModel 옵션을 "spring" 으로 한 이유는, 구현체를 spring이 관리할 수 있도록 하기 위함이고,
unmappedTargetPolicy 옵션에는 IGNORE, WARN, ERROR 세 가지가 있는데 매핑되지 않는 속성값이 있을 때 어떻게 처리할 지에 대한 옵션이다.
각 DTO와 Entity 에서 필드 값은 다르지만 매핑시켜야 하는 경우에는, 아래와 같이 @Mapping 을 사용한다.
@Mapping(source = "user_id", target = "email")
MemberDTO toDto(Member member);
source 가 매개변수로 들어간 Member, target이 결과값인 MemberDTO 의 속성 값이다.
다른 경우로는, 필드 값이 같은데 매핑시키고 싶지 않은 경우가 있을 것이다.
@Mapping(source = "email",ignore = true)
MemberDTO toDto(Member member);
그럴 때는 source에 필드명을 적어주고 ignore 옵션을 true로 설정하면 된다.
4. 구현체 생성
이제, build 를 해서 구현체만 확인하면 된다.
- 실행 결과
그러면 build > generated > .. > mapper 하위에 구현체가 만들어져 있는 것을 확인할 수 있다.
5. 테스트 코드 작성 및 검증
그러면 이제 마지막으로 테스트를 통해 매핑이 잘 되는 지 검증한다.
- 테스트 코드
@ExtendWith({SpringExtension.class, MockitoExtension.class})
class MemberMapperTest {
@InjectMocks
MemberMapperImpl memberMapperImpl;
@Test
void then_Mapper_thenSuccess(){
Member user = Member.builder()
.email("test@test.com")
.passwd("test1")
.name("홍길동")
.build();
MemberDTO memberDto = memberMapperImpl.toDto(user); // entity -> dto 변환
Assertions.assertThat(memberDto.getEmail()).isEqualTo("test@test.com");
}
}
위의 코드는 Entity → DTO 로 매핑하는 toDTO 에 대한 테스트이다.
결과 값으로 반환받은 memberDto의 이메일과 user의 이메일을 비교하여 같은 지 확인한다.
- 실행 결과
테스트는 성공으로, 변환이 성공적으로 되었음을 알 수 있다.
정리
변환해야 하는 경우가 많아질수록 실수가 늘거나 귀찮아질 것 같을 때 MapStruct를 사용하여 익숙해져 보는 것이 좋을 것 같다.
참고
https://springframework.guru/using-mapstruct-with-project-lombok/
'Web' 카테고리의 다른 글
[Spring] XSS Prevention (2) | 2023.07.31 |
---|---|
[Spring] Unit Tests with Mockito ( in Service Layer ) (0) | 2023.07.25 |
[Spring] 라이브러리 버전 관리하기 ( Gradle ) (0) | 2023.07.06 |
[Spring] Exception Handling 에 대해서 (3) | 2023.07.03 |
[Spring] @Builder 와 Builder 패턴 (0) | 2023.06.25 |