웹개발/Web

편리한 객체 간 매핑을 위한 MapStruct 사용법

slown 2023. 8. 28. 00:43

MapStruct는 Java bean 유형 간의 매핑 구현을 단순화하는 코드 생성기입니다.

 

MapStruct 의 장점

 

컴파일시 설정된 압식으로 오류를 확인할 수 있다.(코드 생성시)

리플렉션을 사용하지 않기 때문에 매핑 속도가 빠르다.

디버깅이 쉽다.

생성된 매핑코드를 눈으로 직접 확인할 수 있다.

 

MapStruct의 사용법

 

1. gradle을 통해서 라이브러리를 주입한다.

    //map struct
    implementation 'org.mapstruct:mapstruct:1.4.2.Final'
    annotationProcessor "org.mapstruct:mapstruct-processor:1.4.2.Final"
 

 

2. 엔티티에서 데이터를 옮길 dto클래스를 작성한다.

@Getter
@Setter
@Builder
@AllArgsConstructor
public class MemberRequest {
    @NotBlank(message = "회원 이름을 입력해주세요.")
    private String userName;
    @NotBlank(message = "패스워드를 입력해주세요.")
    private String password;
    @NotBlank(message = "회원 나이를 입력해주세요.")
    private Integer userAge;
    @NotBlank(message = "회원 이메일을 입력해주세요.")
    private String userEmail;
    private Role role;
}

@Getter
@ToString
@Builder
@AllArgsConstructor
public class MemberResponse {
    private Long id;
    private String userName;
    private String password;
    private Integer userAge;
    private String userEmail;
    private Role role;
    private LocalDateTime createdTime;
    private LocalDateTime updatedTime;
}

 

3. Mapper 인터페이스를 작성해서 Entity -> Dto 그리고 반대되는 경우의 메소드를 작성을 한다.

//1.componentModel은 map struct에 자동으로 주입
@Mapper(componentModel = "spring")
public interface MemberMapper {

    MemberMapper INSTANCE = Mappers.getMapper(MemberMapper.class);

    //entity -> dto
    MemberResponse entityToDto(Member member);

    //dto -> entity
    //맵핑을 하는 과정에서 제외를 하고 싶은 컬럼은 ignore를 true
    @Mapping(target = "id",ignore = true)
    @Mapping(target = "createdTime",ignore = true)
    @Mapping(target = "updatedTime",ignore = true)
    Member dtoToEntity(MemberRequest memberRequest);
}

Mapper 인터페이스에 @Mapper 어노테이션을 붙이면 MapStruct가 자동으로 MemberMapper의 구현체를 생성해줍니다.

위에서 사용된 MemberMapper INSTANCE = Mappers.getMapper(MemberMapper.class) 는 매퍼 클래스에서 MemberMapper를 찾을 수 있도록 하는 방법입니다. 매퍼 interface에서 위와 같이 Instance를 선언해주면 매퍼에 대한 접근이 가능합니다. 매핑하려는 객체는 필드값이 동일하기 때문에, 구현 코드를 작성 또는 수정하지 않고 쉽게 매핑할 수 있습니다.

 

그 후에 gradle로 빌드를 하고 나면 자동으로 해당 메소드에 관련된 객체가 구현이 됩니다.

@Component
public class MemberMapperImpl implements MemberMapper {
    public MemberMapperImpl() {
    }

    public MemberResponse entityToDto(Member member) {
        if (member == null) {
            return null;
        } else {
            MemberResponse.MemberResponseBuilder memberResponse = MemberResponse.builder();
            memberResponse.id(member.getId());
            memberResponse.userName(member.getUserName());
            memberResponse.password(member.getPassword());
            memberResponse.userAge(member.getUserAge());
            memberResponse.userEmail(member.getUserEmail());
            memberResponse.role(member.getRole());
            memberResponse.createdTime(member.getCreatedTime());
            memberResponse.updatedTime(member.getUpdatedTime());
            return memberResponse.build();
        }
    }

    public Member dtoToEntity(MemberRequest memberRequest) {
        if (memberRequest == null) {
            return null;
        } else {
            Member.MemberBuilder member = Member.builder();
            member.userName(memberRequest.getUserName());
            member.password(memberRequest.getPassword());
            member.userAge(memberRequest.getUserAge());
            member.userEmail(memberRequest.getUserEmail());
            member.role(memberRequest.getRole());
            return member.build();
        }
    }
}

그리고 위의 코드가 제대로 작동을 하는지를 확인하기 위해서 테스트 코드를 작성을 하면 이러하다.

 

@Test
    @DisplayName("Map Struct Entity->Dto")
    public void EntityToDto(){
        Member member = MemberFactory.member();

        MemberResponse response = MemberMapper.INSTANCE.entityToDto(member);

        assertThat(response).isNotNull();
        assertThat(response.getId()).isEqualTo(member.getId());
        assertThat(response.getUserName()).isEqualTo(member.getUserName());
        assertThat(response.getPassword()).isEqualTo(member.getPassword());
        assertThat(response.getUserEmail()).isEqualTo(member.getUserEmail());
        assertThat(response.getUserAge()).isEqualTo(member.getUserAge());
        assertThat(response.getCreatedTime()).isEqualTo(member.getCreatedTime());
        assertThat(response.getUpdatedTime()).isEqualTo(member.getUpdatedTime());
    }

    @Test
    @DisplayName("Map Struct Dto->Entity")
    public void DtoToEntity(){
        MemberRequest memberRequest = MemberFactory.request();
        Member member = MemberMapper.INSTANCE.dtoToEntity(memberRequest);

        assertThat(member).isNotNull();
        assertThat(member.getUserName()).isEqualTo(memberRequest.getUserName());
        assertThat(memberRequest.getPassword()).isEqualTo(member.getPassword());
        assertThat(memberRequest.getUserAge()).isEqualTo(member.getUserAge());

    }

[참고]