관리 메뉴

피터의 개발이야기

[Kotlin] Spring에 MapStruct와 Lombok을 함께 사용할 때 isCompleted 필드가 null로 넘어오는 문제 본문

Programming/Kotlin

[Kotlin] Spring에 MapStruct와 Lombok을 함께 사용할 때 isCompleted 필드가 null로 넘어오는 문제

기록하는 백앤드개발자 2024. 7. 19. 10:10
반응형

ㅁ 들어가며

ㅇ Kotlin Spring 프로젝트에서 MapStruct와 Lombok을 사용할 때 isCompleted가 null로 넘어오는 문제가 발생하였다.

ㅇ 이를 해결하면서 Lombok과 MapStruct의 충돌에 대해서 알게 되어 이를 정리하였다.

ㅇ 우선 나의 문제와 해결방법을 정리하고, 원인을 분석하는 과정에 다른 글에서 null일 수 있는 경우도 정리하였다.

관련글: [Kotlin] 코틀린 MapStruct

 

ㅁ 문제상황

ㅇ reqDto.isComplated가 swagger에 보이지 않음.

MapStruct 구현부에서 isComplated가 entity로부터 값을 set받지 못하여 null.

ㅇ resDto.isComplated이 결과 JSON에서 아예 키까지 빠져있음.

// RequestDto:  Swagger에서 나타나지 않음
val isComplated: String

// entity: snake case에서 camelcase 형태로 선언되었고, Y/N으로 값이 입력된다.
@Column(name = "is_complated")
val isComplated: String

// QueryDSL 데이터 조회
val products = 
 jpaQueryFactory.selectFrom(product)
            .where(
                product.name.like("p%"),
            )
            .fetch()
    
// mapstruct로 변환
products.map {
    productsMapper.entityToResponseDto(it)
}
            
// ResponseDto: null로 세팅된다.
val isComplated: String

ㅇ 처음 문제점을 인식한건 isComplated가 ResDto에 결과가 null이 아닌 key 자체가 없는 점이었다.

ㅇ 누락 또는 오타로 문제점을 파악했지만, 아키덱트 내부의 문제점으로 파악해 나아갔다.

ㅇ 더욱이 ReqDto에서도 isComplated가 Swagger에서 표출되지도 않았다.

ㅇ 무엇인가 구조상 버그가 틀림없어 보였다.

 

ㅁ 해결 방법

// entity 수정
@Column(name = "is_complated")
val complatedYn: String

// req, res도 수정
val complatedYn: String

ㅇ isComplated는 Boolean타입으로 오인할 수 있는 형태여서, 명확한 complatedYn으로 변경하였다.

ㅇ 문제가 되었던 reqDto, resDto에 값이 잘 전달되었다.

ㅇ 원인이 무엇인지 Searching을 하였다.

 

ㅁ 분석을 위한 Searching과 내용정리

 Lombok의 동작 방식 <+++ 나의 경우 핵심 원인

For boolean fields that start with is immediately followed by a title-case letter, nothing is prefixed to generate the getter name.
Any variation on boolean will not result in using the is prefix instead of the get prefix; for example, returning java.lang.Boolean results in a get prefix, not an is prefix.

- Lombok의 공식 문서의 내용이다. 

- Lombok은 boolean 타입 필드에 대해 'is' 접두사를 사용한 getter를 생성한다. 

- 예를 들어, isCompleted()와 같은 형태다.

 

Jackson의 동작 방식
Jackson은 기본적으로 getter 메서드의 이름을 기반으로 JSON 필드 이름을 결정한다. boolean 타입의 경우, 'is' 접두사를 제거하고 첫 글자를 소문자로 바꾼다.

참조 블로그 다른 내용 요약
- Spring 의 Json Message ConverterJackson라이브러리를 사용
- lombok 의 Getter 는 필드명 맨 앞을 항상 대문자로 만듬
- Jackson 라이브러리는 Getter 의 맨 앞 두글자가 전부 대문자인 경우 필드명과 Json key 값이 달라짐
- aCount 라는 필드명을 lombok 을 사용해서 Getter 를 만들면 getACount() 가 되기 때문에 이슈가 발생

[참조] Spring Request DTO 에 null 값이 들어가는 이유 (Jackson, Lombok)

 

 

MapStruct와 Lombok의 충돌

- Lombok 라이브러리에 먼저 dependency (의존성) 추가가 되어있어야 한다.

- MapStruct는 Lombok의 getter, setter, builder를 이용하여 생성된다.

- 그래서 MapStruct와 Lombok은 빌드과정 중에 생성 순서에 따라 MapStruct가 "Unknown property" 오류가 발생할 수 있다.

- 이를 방지 하기 위해서는 MapStruct와 Lombok을 함께 사용할 때는 버전 호환성, 처리 순서, 그리고 필요한 추가 의존성을 고려해야 한다.

ㄴ Maven의 annotationProcessorPaths에 Lombok 추가
ㄴ MapStruct와 Lombok의 처리 순서 조정 (Lombok을 먼저 처리)
ㄴ lombok-mapstruct-binding 의존성 추가 (Lombok 1.18.16 이상 버전에서 필요)
ㄴ MapStruct와 Lombok 버전 호환성 확인

[참조] MapStruct + Lombok together not compiling: unknown property in result type

[참조] 편리한 객체 간 매핑을 위한 MapStruct 적용기 (feat. SENS)

ㅁ 마무리

isCompleted가 null로 넘어오는 문제는 주로 Lombok, MapStruct, 그리고 Jackson의 상호작용으로 인해 발생한다. 이를 해결하면서 각 라이브러리의 동작 방식을 이해하고, 적절한 어노테이션이나 설정 방법을 잘 이해하고 사용해야 함을 배우게 되었다. 

 

ㅁ 함께 보면 좋은 사이트

편리한 객체 간 매핑을 위한 MapStruct 적용기 (feat. SENS)

MapStruct + Lombok together not compiling: unknown property in result type

[Java] Lombok에서 boolean타입 프로퍼티에 대한 get,set

Kotlin Code Generation and Empty Constructor Preference

Spring Request DTO 에 null 값이 들어가는 이유 (Jackson, Lombok)

반응형
Comments