spring - MapStruct 사용법

김진욱·2022년 2월 27일
0

study

목록 보기
4/6

Map Struct 란 ?

Dto 와 Entity 의 변환을 쉽게 도와주는 라이브러리 이다.
변환하기 위해 static 메소드를 만들거나 setter 를 사용하는 경우가 대부분이다.
하지만 MapStruct 라이브러리는 선언만 하면 빌드할 때 자동으로 구현을 해주기 때문에 손쉽게Mapping 처리를 할 수 있다.

gradle 설정

dependencies {
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'

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

먼저 Car 와 CarDto 클래스를 보자

public class Car {
 
    private String make;
    private int numberOfSeats;
    private CarType type;
 
    //constructor, getters, setters etc.
}
public class CarDto {
 
    private String make;
    private int seatCount;
    private String type;
 
    //constructor, getters, setters etc.
}

Car 클래스의 numberOfSeats 는 Dto에 seatCount 라는 필드명으로 선언되어있다.
Mapping 인터페이스를 만들어 보자

@Mapper // 1
public interface CarMapper {
 
    CarMapper INSTANCE = Mappers.getMapper( CarMapper.class ); // 2
 
    @Mapping(source = "numberOfSeats", target = "seatCount")
    CarDto carToCarDto(Car car); // 3
}
  1. 매핑 인터페이스임을 명시해준다.
  2. 사용자가 접근할 수 있는 매퍼 객체를 만들어 준다. source:변환할 필드, target:저장될 필드
  3. Car 객체를 CarDto 객체로 매핑 시켜주는 메서드이다

검증

@Test
public void shouldMapCarToDto() {
    //given
    Car car = new Car( "Morris", 5, CarType.SEDAN );
 
    //when
    CarDto carDto = CarMapper.INSTANCE.carToCarDto( car );
 
    //then
    assertThat( carDto ).isNotNull();
    assertThat( carDto.getMake() ).isEqualTo( "Morris" );
    assertThat( carDto.getSeatCount() ).isEqualTo( 5 );
    assertThat( carDto.getType() ).isEqualTo( "SEDAN" );
}

프로젝트에 적용하기
기존의 회원가입 로직에서는 builder 패턴으로 dto -> entity 변환이 이루어지고 있다.

    public Long join(MemberJoinDto member) {
        Role role = roleRepository.findByName(Authority.ADMIN).orElseThrow(() -> new IllegalArgumentException("not found Roll"));

        Member buildMember = Member.builder()
                .email(member.getEmail())
                .password(passwordEncoder.encode(member.getPassword()))
                .roles(Collections.singletonList(role))
                .build();

        return memberRepository.save(buildMember).getId();
    }

Mapper 선언

@Mapper(componentModel = "spring")
public interface MemberMapper {

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

    @Mapping(target = "id", ignore = true) // 1
    @Mapping(source = "password", target = "password", qualifiedByName = "encryptPassword")
    Member toEntity(MemberJoinDto dto);

    @Mapping(source = "email", target = "name") // 3
    MemberDto toDto(Member member);

    @Named("encryptPassword") // 2
    default String encryptPassword(String password) {
        return new BCryptPasswordEncoder().encode(password);
    }
}
-------------------------------------------
@Getter
@Builder
@AllArgsConstructor
public class MemberJoinDto {

    private String email;
    private String password;
}
-------------------------------------------
@Getter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MemberDto {
    private String name;
    private List<Role> roles;
}


1: dto -> entity 로 변환 하는 과정에 id 값은 자동증가 값이므로 ignore 처리한다.
2: password 는 암호화 되어 데이터베이스에 저장되어야 하기 때문에,
이처럼 로직이 들어가는 매핑 룰을 정할 때는 qualifiedByName 이라는 옵션을 사용한다, @Named 어노테이션으로 선언해놓은 값을 사용하겠다는 의미이다
3: entity -> dto 변환할때 entity의 email 필드를 name 필드로 매핑해준다

dto -> entity 검증

    @Override
    public Long join(MemberJoinDto member) {
        Role role = roleRepository.findByName(Authority.ADMIN).orElseThrow(() -> new IllegalArgumentException("not found Roll"));

        Member memberEntity = MemberMapper.INSTANCE.toEntity(member);
        memberEntity.setRoles(Collections.singletonList(role));

        return memberRepository.save(buildMember).getId();
    }

entity -> dto 검증

    @Test
    public void mapperTest() throws Exception {

        Member member = repository.findById(1L).get();

        MemberDto memberDto = MemberMapper.INSTANCE.toDto(member);

        System.out.println(memberDto);
    }

profile
2021.12~ 공부의 기록

0개의 댓글