이번 글을 통해 배워 갈 내용
- 자바 맵핑
- MapStruct에 대한 소개
- MapStruct 성능 소개
- MapStruct 사용법
자바 맵핑
Spring 서버에서 Object 맵핑을 하기 위해서
BeansUtil을 사용하거나 손으로 하나하나 Setter와 Getter로 설정을 하는 경우
매우 힘이 드는 일이다.
성능 저하를 최대한 줄이고
Object Mapping을 할 때 하나하나 다 막일을 안 하려면
어떻게 해야 될까?
라고 고민을 한다면
MapStruct를 권장한다.
MapStruct에 대한 소개
MapStruct는 Annotation 기반으로 type-safe bean mapping 클래스를 제공하는 기능이다.
Mapping 되는 내용을 하나하나 다 적는 것보다 시간 절약이 되며
다른 동적 맵핑보다 시간 절약성, 컴파일 시간 안전성, 오류 내용 확인성 등에서 우월하다.
MapStruct 성능 소개
순수하게 get/set으로 진행한 경우와 MapStruct를 사용한 경우 둘 다 속도 측정을 해본 결과
비슷하게 나왔다.
BeansUtil보다는 한 객체를 변경하는 경우보다 100ms 정도 더 빨랐다.
따라서
MapStruct는 매우 빠르다
MapStruct 사용법
Maven에 MapStruct를 사용한다면
Maven Configuration을 아래와 같이 설정합니다.
...
<properties>
<org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
</properties>
...
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
</dependencies>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
...
Gradle에 MapStruct를 사용한다면
Gradle Configuration을 아래와 같이 설정합니다.
...
plugins {
...
id "com.diffplug.eclipse.apt" version "3.26.0" // 이클립스라면 추가
}
dependencies {
...
implementation "org.mapstruct:mapstruct:${mapstructVersion}"
annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
// If you are using mapstruct in test code
testAnnotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
}
...
Lombok을 MapStruct와 같이 사용한다면
Maven 은 아래와 같이 세팅해줍니다.
...
<properties>
<org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
<org.projectlombok.version>1.18.16</org.projectlombok.version>
</properties>
...
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<!-- lombok dependency should not end up on classpath -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
</path>
<!-- additional annotation processor required as of Lombok 1.18.16 -->
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.1.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
...
Gradle은 아래와 같이 세팅해줍니다.
dependencies {
implementation "org.mapstruct:mapstruct:${mapstructVersion}"
implementation "org.projectlombok:lombok:1.18.16"
annotationProcessor "org.projectlombok:lombok-mapstruct-binding:0.1.0"
annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
annotationProcessor "org.projectlombok:lombok:1.18.16"
}
ExampleDto, ExampleEntity를 만들고
ExampleMapper를 이용해서 테스트해봤습니다.
import lombok.*;
@Getter
@Setter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ExampleDto {
private String name;
private String description01;
private String description02;
}
import lombok.*;
@Getter
@Setter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ExampleEntity {
private String name;
private String description01;
private String description02;
}
import org.mapstruct.Mapper;
@Mapper
public interface ExampleMapper {
ExampleDto entityToDto(ExampleEntity exampleDto);
ExampleEntity dtoToEntity(ExampleDto exampleDto);
}
테스트
@RestController
@RequestMapping("api/example")
public class ExampleController {
private ExampleMapper exampleMapper;
public ExampleController() {
this.exampleMapper = Mappers.getMapper(ExampleMapper.class);
}
@GetMapping(path = "")
public void getTest(){
// dto -> entity
ExampleDto exampleDto = ExampleDto.builder()
.name("hello")
.description01("설명1")
.description02("설명2")
.build();
System.out.println("전 " + exampleDto);
ExampleEntity exampleEntity = exampleMapper.dtoToEntity(exampleDto);
System.out.println("후 " + exampleEntity);
// entity -> dto
ExampleEntity exampleEntity2 = ExampleEntity.builder()
.name("hello2")
.description01("설명21")
.description02("설명22")
.build();
System.out.println("전 " + exampleEntity2);
ExampleDto exampleDto2 = exampleMapper.entityToDto(exampleEntity2);
System.out.println("후 " + exampleDto2);
}
}
잘 작동합니다.
설명을 추가하자면
ObjectMapper는 내부에서 아래와 같은 클래스를 만들어줍니다.
public class ExampleMapperImpl implements ExampleMapper{
@Override
public ExampleDto entityToDto(ExampleEntity exampleEntity){
if(exampleEntity == null){
return null;
}
ExampleDto exampleDto = new ExampleDto();
exampleDto.setName(exampleEntity.getName());
exampleDto.setDescription01(exampleEntity.getDescription01());
exampleDto.setDescription02(exampleEntity.getDescription02());
return exampleDto;
}
@Override
public ExampleEntity dtoToEntity(ExampleDto exampleDto){
if(exampleDto == null){
return null;
}
ExampleEntity exampleEntity = new ExampleEntity();
exampleEntity.setName(exampleDto.getName());
exampleEntity.setDescription01(exampleDto.getDescription01());
exampleEntity.setDescription02(exampleDto.getDescription02());
return exampleEntity;
}
}
결론
오늘 알아가야 하는 한 가지는
MapStruct가 빠르고 Stateless, Thread-safe하니까 자주 애용하자 :)
참조 및 인용
https://mapstruct.org/documentation/stable/reference/html/#Preface
블로그 추천 포스트
https://codemasterkimc.tistory.com/50
오늘도 즐거운 코딩 하시길 바랍니다 ~ :)
'Spring' 카테고리의 다른 글
Spring 초간단 Custom ORM을 만들며 배운 한가지 (0) | 2022.05.29 |
---|---|
h2-console "mem:testdb" not found 문제 해결하는 한가지 방법 (0) | 2022.04.09 |
@PathVariable, @RequestParam, @RequestBody에 대한 한가지 생각 (0) | 2022.02.17 |
스프링 부트 시작시간 줄이는 5가지 방법 (1) | 2022.02.09 |
Maven Multi-Module Project Azure Deployment Study (0) | 2021.12.04 |