국쥐의 개발 일상

Lombok + Mapstruct 사용 시 @Builder와 함께 사용해보기 본문

Java

Lombok + Mapstruct 사용 시 @Builder와 함께 사용해보기

kuckjwi 2021. 5. 23. 17:01

웹 + 데이터베이스 개발을 하다보면, Entity to Dto 및 Dto to Entity를 변환할 케이스가 많이 생긴다.

Mapstruct는 위와같은 변환로직을 Interface로 정의만 하면 autogenerate 하는 아주 유용한 녀석이다!

일반적으로 Mapstruct 사용 시 아래의 코드와 같은 인터페이스 형식으로 선언하게 되어 있다.

@Mapper
public interface JobMapper {
  Job of(JobEntity entity);
}

또한 lombok을 활용하여 아래와같이 Dto Class를 만들었다고 가정해보겠다.

@Data
public class Job {
  private String name;

  private int career;
}

이것을 Mapstruct 녀석이 autogenerate를 해주는데, 위와같이 작성하면 JobMapperImpl.class라는 인터페이스 구현체가 생겼을 것이다! 이것을 열어보면 아래와 같이 코드가 생성이 되어있을 것이다.

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2021-05-23T16:33:11+0900",
    comments = "version: 1.4.2.Final, compiler: IncrementalProcessingEnvironment from gradle-language-java-6.8.3.jar, environment: Java 13.0.1 (Oracle Corporation)"
)
public class JobMapperImpl implements JobMapper {

    @Override
    public Job of(JobEntity entity) {
        if ( entity == null ) {
            return null;
        }

        Job job = new Job();

        job.setName( entity.getName() );
        job.setCareer( entity.getCareer() );

        return job;
    }
}

매우 편리하게 클래스 변환로직이 자동생성 되어있다! 하지만 필자는 dto도 불변으로 만드는것을 좋아한다. (이거슨 취향) 

그래서 아래와 같이 dto 클래스에 대한 lombok 어노테이션 및 final 키워드를 붙여보겠다.

@Getter
@Builder
public class Job {
  private final String name;

  private final int career;
}

그러고서 다시 autogenerate된 impl 파일을 확인해보겠다!

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2021-05-23T16:33:11+0900",
    comments = "version: 1.4.2.Final, compiler: IncrementalProcessingEnvironment from gradle-language-java-6.8.3.jar, environment: Java 13.0.1 (Oracle Corporation)"
)
public class JobMapperImpl implements JobMapper {

    @Override
    public Job of(JobEntity entity) {
        if ( entity == null ) {
            return null;
        }

        Job job = new Job();

        job.setName( entity.getName() );
        job.setCareer( entity.getCareer() );

        return job;
    }
}

??... 변한게없다.. (실제로는 컴파일 에러납니다.)

해결하기 위해서는 아래와같이 디펜던시를 구성해야한다. (필자는 gradle 기반으로 하였음)

annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
annotationProcessor 'org.projectlombok:lombok:1.18.20'
annotationProcessor 'org.projectlombok:lombok-mapstruct-binding:0.2.0'

 

여기서 핵심은 mapstruct processor 이후 lombok processor가 위치하여야한다!

 

이후 다시 생성된 코드를 확인해보겠다.

public class JobMapperImpl implements JobMapper {

    @Override
    public Job of(JobEntity entity) {
        if ( entity == null ) {
            return null;
        }

        JobBuilder job = Job.builder();

        job.name( entity.getName() );
        job.career( entity.getCareer() );

        return job.build();
    }
}

정상적으로 builder 기반으로 동작할 수 있게 코드가 생성되었다!

 

간단한 예제는 여기서 확인이 가능합니다!

'Java' 카테고리의 다른 글

StringBuilder, StringBuffer 차이  (0) 2019.11.14
Java7 try-with-resource  (0) 2019.11.10