일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- Pinpoint
- CKA
- AWS EKS
- kotlin
- CloudWatch
- 공부
- PETERICA
- kotlin coroutine
- MySQL
- 코틀린 코루틴의 정석
- CKA 기출문제
- 정보처리기사실기 기출문제
- Elasticsearch
- Linux
- APM
- 티스토리챌린지
- 오블완
- AI
- Kubernetes
- mysql 튜닝
- Java
- 정보처리기사 실기 기출문제
- kotlin spring
- 기록으로 실력을 쌓자
- kotlin querydsl
- minikube
- IntelliJ
- Spring
- 정보처리기사 실기
- aws
- Today
- Total
피터의 개발이야기
[Kotlin] 코틀린 MapStruct 사용방법 본문
ㅁ MapStruct란?
ㅇ MapStruct는 Java 객체 간 매핑을 쉽게 할 수 있게 해주는 코드 생성 라이브러리로, 객체 간의 매핑을 컴파일 시점에 자동으로 생성하여 런타임 오버헤드를 줄이고 안전한 매핑을 제공한다.
ㅇ Kotlin에서도 MapStruct를 사용할 수 있지만, 몇 가지 추가 설정이 필요하다.
ㅇ이 글에서는 Kotlin 프로젝트에서 MapStruct를 설정하고 사용하는 방법을 알아본다.
ㅁ MapStruct를 사용하는 이유
ㅇ Service에서 Dto로 많은 객체 변환하는 로직이 여러 곳에 퍼져있어서, 비지니스 로직의 간결성이 떨어진다.
ㅇ 객체 변환 로직이 너무 퍼져 있어서 재사용성이 떨어진다.
ㅇ 구체적으로 보면 Dto와 Entity 사이에 의존성이 크다.
ㅇ MapStruct는 객체 변환 메소드를 자동으로 만들어주고, 필요할 경우 직접 메소드를 만들어 사용할 수 있었다.
ㅇ Dto와 Entity 사이의 의존성을 줄이고, Service에서 변환 로직을 분리하여 MapStruct에 정리할 수 있게 되어, Service 로직의 간결성을 높일 수 있었다.
ㅁ 의존성 추가
//kotlin
plugins {
kotlin("kapt") version "1.5.30"
}
dependencies {
implementation("org.mapstruct:mapstruct:1.5.3.Final")
kapt("org.mapstruct:mapstruct-processor:1.5.3.Final")
}
ㅁ 기본 사용법
import org.mapstruct.Mapper
@Mapper(componentModel = "spring")
interface MyMapper {
fun mapFrom(requestDto: RequestDto): ResponseDto
}
ㅇ RequestDto를 ResponseDto로 매핑하는 기본적인 예제이다.
ㅇ Kotlin에서는 Java와 유사하게 @Mapper 어노테이션을 사용하여 매핑 인터페이스를 정의할 수 있다.
ㅁ 이름이 다른 필드 매핑하기
@Mapper(componentModel = "spring")
interface MyMapper {
@Mapping(source = "key", target = "title")
@Mapping(source = "value", target = "content")
fun mapFrom(mapDto: MapDto): ResponseDto
}
ㅇ 필드 이름이 다른 경우에는 @Mapping 어노테이션을 사용하여 명시적으로 매핑을 지정할 수 있다.
ㅁ 두 객체를 한 객체로 매핑하기
@Mapper(componentModel = "spring")
interface MyMapper {
fun mapFrom(requestDto: RequestDto, mapDto: MapDto): DoubleDto
}
ㅇ 두 개의 객체를 하나의 객체로 매핑할 수도 있다.
ㅇ RequestDto와 MapDto를 DoubleDto로 매핑한다.
ㅁ 한 객체를 다른 객체로 매핑하기
import org.mapstruct.*
@Mapper(componentModel = "spring", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
interface ProductMapper {
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
fun updateProductFromDto(dto: ProductUpdateDto, @MappingTarget entity: Product)
}
ㅇ MapStruct를 사용하여 DTO에서 Entity로 필요한 부분만 매핑하는 매퍼 인터페이스를 작성한다.
ㅇ 기존 entity를 받아서, null값이 아닌 updateDto의 값을 치환한다.
ㅁ 사용자 정의 메서드
@Mapper(componentModel = "spring")
interface MyMapper {
fun mapFrom(requestDto: RequestDto): ResponseDto
default fun mapFrom(requestDto: RequestDto): ResponseDto {
return ResponseDto(requestDto.title, requestDto.content)
}
}
ㅇ 복잡한 로직이 필요한 경우, default 키워드를 사용하여 사용자 정의 메서드를 작성할 수 있다.
ㅁ 심화 사용법
default
@Mapper(componentModel = "spring")
interface MyMapper {
@Mapping(source = "text", target = "content", defaultValue = "content")
fun mapFrom(title: String, text: String): ResponseDto
}
ㅇ 필드의 개수가 다른 객체를 매핑하거나 null 값을 처리할 때 default 값을 설정할 수 있다.
ignore = true
@Mapper(componentModel = "spring")
interface MyMapper {
@Mapping(target = "content", ignore = true)
fun mapFrom(title: String): ResponseDto
}
ㅇ 특정 필드를 매핑하지 않으려면 ignore = true 옵션을 사용한다.
expression
@Mapper(componentModel = "spring")
interface MyMapper {
@Mapping(target = "content", expression = "java(getContent())")
fun mapFrom(title: String): ResponseDto
fun getContent(): String {
return "defaultContent"
}
}
ㅇ 생성된 메서드를 사용하여 값을 매핑할 때는 expression을 사용한다.
ㅇ Source에 없는 값도 expression에서 초기값으로 세팅할 수 있다.
@Named
@Mapper(componentModel = "spring")
interface MyMapper {
@Mapping(source = "num", target = "key", qualifiedByName = "toKey")
fun mapFrom(num: Int, value: String): MapDto
@Named("toKey")
fun toKey(num: Int): String {
return when (num) {
1 -> "key1"
2 -> "key2"
else -> "defaultKey"
}
}
}
ㅇ 반복되는 연산을 처리하기 위해 메서드를 만들고 @Named 어노테이션을 사용하여 매핑할 수 있다.
ㅇ expression과 다르게 없는 필드에 대해서 매핑하지 못한다.
ㅇ mapper에 enum으로 매핑하는 코드는 재사용성이 커서 자주 사용하게 된다.
ㅁ 주의 사항
ㅇ Kotlin에서는 기본적으로 모든 클래스와 메서드가 final이므로, MapStruct가 구현 클래스를 생성할 수 없다.
이를 해결하기 위해 open 키워드를 사용하거나 all-open 플러그인을 사용해야 한다.
ㅇ Kotlin의 데이터 클래스는 기본 생성자가 없으므로, MapStruct가 인스턴스를 생성하는 데 문제가 될 수 있다.
이 경우 @BeanMapping(builder = @Builder(disableBuilder = true)) 어노테이션을 사용하여 빌더 사용을 비활성화할 수 있다.
ㅇ 스프링 부트 3 버전 이후에는 annotationProcessor로 동작하기 때문에 의존성 문제가 발생할 수 있다.
ㄴ [피터의 개발이야기:티스토리] [Kotlin] Spring에 MapStruct와 Lombok을 함께 사용할 때 isCompleted 필드가 null로 넘어오는 문제
ㅁ 마무리
Kotlin에서 MapStruct를 사용하면 객체 간 매핑을 쉽고 효율적으로 처리할 수 있다. 초기 설정에 약간의 추가 작업이 필요하지만, 한 번 설정해 놓으면 Java에서와 마찬가지로 편리하게 사용할 수 있다. MapStruct를 통해 반복적이고 오류가 발생하기 쉬운 매핑 코드 작성을 줄이고, 더 깔끔하고 유지보수가 쉬운 코드를 작성할 수 있다.
ㅁ 함께 보면 좋은 사이트
ㅇ codinghejow - [BackEnd] MapStruct 사용기
ㅇ baeldung - Custom Mapper with MapStruct
ㅇ 편리한 객체 간 매핑을 위한 MapStruct 적용기 (feat. SENS)
ㅇ Mapstruct 공식 github에서 mapstruct-kotlin
'Programming > Kotlin' 카테고리의 다른 글
[Kotlin] MutableList 기능 설명 (0) | 2024.07.10 |
---|---|
[Kotlin] Map 다양한 사용법 (1) | 2024.07.09 |
[Ktor] Kotlin + Ktor + Eposed 환경에서 MySQL 연동하기 (0) | 2024.07.04 |
[Kotlin] ?(물음표)와 !!(느낌표 두개) (0) | 2024.06.28 |
[Kotlin] Scope Functions (let, with, run, apply, also) 정리 (0) | 2024.06.25 |