본문 바로가기
Back-End/Spring MVC

MapStruct 구현체의 분석과 DTO에서 필요한 Lombok 애너테이션

by 달의 조각 2022. 10. 20.

Mapper는 DTO 클래스와 엔티티 클래스를 서로 변환해 주는 변환자이다. 계층간 역할 분리를 위해 사용한다. Mapper의 한 종류인 MapStruct는 컴파일 타임에 매핑 구현체를 모두 생성한다.

DTO를 Inner class로 관리하던 중에 각 Inner class에 어떤 Lombok 애너테이션을 붙여야 하는지 판단이 되지 않아서 DTO 클래스와 MapStruct가 생성하는 구현체 코드를 분석하여 정리했다.

🪄  MapStruct가 매핑을 정상적으로 하기 위한 우선 순위 조건

  1. Builder 패턴이 적용된 경우
  2. 모든 필드를 파라미터로 가지는 생성자가 있을 경우
    👉 이때 기본 생성자가 포함되어 있으면 제 역할을 하지 못한다.
  3. setter 메서드가 있는 경우

 


 

1. RequestBody → Member

RequestBody의 데이터를 가져와서 Member에 넣는다.

🪄 @Getter

MemberDto 클래스의 이너 클래스인 Post 클래스와 이에 상응하는 MapperImple의 코드이다. @Getter를 통해 Post 객체의 필드를 읽고, 이를 Member 엔티티 클래스의 @Setter를 통해 값을 넣는다.

public class MemberDto {

    @Getter // 아래 필드를 읽어서(Getter) Member에 쓴다(setter)
    public static class Post {

        @NotBlank(message = "이름을 입력하세요.")
        private String name;

        @NotBlank
        private String password;

        @NotBlank
        @Email
        private String email;
    }
    
    ...
}
@Component
public class MemberMapperImpl implements MemberMapper {

    @Override
    public Member memberPostDtoToMember(Post requestBody) {
        if ( requestBody == null ) {
            return null;
        }

        Member member = new Member();

        member.setName( requestBody.getName() );
        member.setPassword( requestBody.getPassword() );
        member.setEmail( requestBody.getEmail() );

        return member;
    }
    
    ...
}

 

2. Member → Response

 Member의 값을 가져와서 Response에 넣는다.

MemberDto 클래스의 이너 클래스인 Response 클래스와 이에 상응하는 MapperImple의 코드이다. 여기서는 Response 객체의 데이터를 사용할 것이 아니라 Member 엔티티의 값을 가져와서 Response에 넣고 응답으로 반환한다.

Response에 값을 넣는 방법은 여러가지이다. 클래스에 사용한 애너테이션의 종류에 따라 MapperImple은 구현을 다르게 한다.

@Getter를 붙이지 않으면 DB에 데이터 저장은 되지만 Not Acceptable 에러가 발생한다. DTO를 return 하여 클라이언트에게 전달하려면 JSON으로 변환해야 하는데, Getter 메서드가 없으면 private인 프로퍼티에 접근할 수가 없다! @JsonProperty를 활용할 수도 있다.

🪄 @Getter + @AllArgsConstructor

@Getter
@AllArgsConstructor
public static class Response {

    private long memberId;
    private String name;
    private String email;
}
@Override
public Response memberToMemberResponse(Member member) {
    if ( member == null ) {
        return null;
    }

    long memberId = 0L;
    String name = null;
    String email = null;

    memberId = member.getMemberId();
    name = member.getName();
    email = member.getEmail();

    Response response = new Response( memberId, name, email );

    return response;
}

 

🪄 @Getter + @Setter

@Getter @Setter
public static class Response {

    private long memberId;
    private String name;
    private String email;
}
@Override
public Response memberToMemberResponse(Member member) {
    if ( member == null ) {
        return null;
    }

    Response response = new Response();

    response.setMemberId( member.getMemberId() );
    response.setName( member.getName() );
    response.setEmail( member.getEmail() );

    return response;
}

 

🪄 @Getter + @Builder

@Builder
@Getter
public static class Response {

    private long memberId;
    private String name;
    private String email;
}
@Override
public Response memberToMemberResponse(Member member) {
    if ( member == null ) {
        return null;
    }

    ResponseBuilder response = Response.builder();

    response.memberId( member.getMemberId() );
    response.name( member.getName() );
    response.email( member.getEmail() );

    return response.build();
}

댓글