MapStruct 소개

김대니·2022년 12월 13일
0
post-thumbnail

Multi-layered 로 구성되어 있는 어플리케이션을 개발하다 보면 데이터 모델 간 변환이 수시로 발생합니다.

예를 들어, DB Entity 인 Computer 라는 모델과 DTO 인 ComputerDto 가 있습니다.

public class Computer {
	private String computerId;
    private boolean isMacOs;
    private Long numberOfCpus;
}

public class ComputerDto {
	private String id;
    private boolean isMacOs;
    private Long numberOfCpus;
}

각 계층에서 데이터 모델을 사용하기 위해서는 위 두 모델은 동일한 데이터를 가지고 있지만 꼭 필요한 모델입니다. (가짜 중복)

위와 같이 데이터 모델을 계층 간 이동을 할 때에는 별도 변환 클래스를 생성하여 사용하게 됩니다. 보통 xxxConverter.java, yyyGenerator.java, zzzMapper.java 등으로 네이밍을 할 수 있습니다.

public class ComputerDtoConverter {
	public ComputerDto(Computer source) {
    	return ComputerDto(
        	source.getComputerId(),
            source.isMacOs(),
            source.getNumberOfCpus()
        )
    }
}

만약 필드가 많아지고, 이동해야 할 계층이 많아진다면 작성해야 할 Converter 코드는 훨씬 더 많아지게 되고 휴먼 에러가 발생할 가능성도 높아집니다.

이를 효과적으로 하기 위해 사용하는 오픈소스가 MapStruct 입니다.

https://mapstruct.org/

MapStruct

공식 사이트에서 MapStruct 를 아래와 같이 소개하고 있습니다.

MapStruct is a code generator that greatly simplifies the implementation of mappings between Java bean types based on a convention over configuration approach.
MapStruct 는 흔히 알고 있는 코드 작성 컨벤션에 기반하여 두 자바 빈의 매핑을 쉽게 구현해주는 코드 생성기입니다.

적용하기

코드에 적용한 예제를 살펴보겠습니다.

공식 사이트를 참고하셔도 됩니다.

https://mapstruct.org/documentation/installation/

불러오기

Gradle 에서 MapStruct 를 불러와보겠습니다.
현재 기준, 최신 버전은 1.5.3 입니다.

dependencies {
    ...
    implementation 'org.mapstruct:mapstruct:1.5.3.Final'
    annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.3.Final'
}

Mapper 작성하기

아래는 MapStruct 를 활용하여 구현한 Converter 클래스 입니다.

@Mapper // 1.
public interface ComputerDtoConverter { // 2.
	@Mapping(source = "computerId", target = "id") // 3.
    @Mapping(source = "macOs", target = "isMacOs")
    ComputerDto convert(Computer source)
}

위 코드에 대한 설명을 드리면:

  1. @Mapper 어노테이션이 부여된 Interface 를 대상으로 매핑 구현체를 자동으로 생성하게 됩니다. 따라서 Interface@Mapper 어노테이션을 부여해야 합니다.

  2. 따라서 Class 가 아닌 Interface 로 작성해야 합니다.

  3. 매핑의 출처가 되는 원본 클래스를 Source, 대상 클래스를 Target 이라고 합니다. 변환하고자 하는 두 모델에서 다른 이름으로 변수명이 작성되어 있다면 @Mapping 어노테이션을 통해 두 변수를 매핑해줄 수 있습니다.

build.gradle 실행하기

프로젝트 build 를 한 뒤에 작성한 Interface 의 구현체가 자동으로 생성된 것을 확인하실 수 있습니다.

주의할 점

🤦‍♂️ 1. boolean 변수를 조심하자

직접 사용하다가 겪은 후기입니다.

클래스 변수 중에 boolean 변수가 isXyz 형태로 네이밍이 되어 있었습니다. 변환하고자 하는 두 모델 모두 동일한 변수명을 사용하고 있었기에 잘 변환이 되겠거니 별도 @Mapping 을 통해 직접 매핑을 해주지 않았습니다.

프로젝트 빌드도 잘 되고 매핑 인터페이스에 대한 구현체도 잘 생성되었습니다.

하지만 테스트를 할 때 isXyz 변수에 값이 무조건 false 로 입력되는 것이었습니다.

찾아보니 is... 형식의 변수는 직접 매핑을 해주어야 MapStruct 의 코드 생성기에서 문제가 없이 잘 생성되는 것이었습니다.

따라서, @Mapping(source = "xyz", target = "isXyz") 처럼 직접 매핑을 해주어야 기대처럼 잘 작동하게 됩니다.

profile
?=!, 물음표를 느낌표로

0개의 댓글