TIL_MapStruct Mapping 오류_250102

2025. 1. 3. 04:06·Spring 단기심화 2기

문제 상황

호텔 검색 기능 개발을 완료한 후, HTTP 요청을 보내니 필드 값이 모두 null로 반환되었다.

{
  "content": [
    {
      "hotelId": null,
      "name": null,
      "country": null,
      "city": null,
      "address": null,
      "description": null,
      "charge": 0
    }
  ],
  "page": {
    "size": 10,
    "number": 0,
    "totalElements": 1,
    "totalPages": 1
  }
}

 

처음에는 검색 조건으로 받는 요청 파라미터나 DB에서 불러오는 데이터에 문제가 있다고 생각하고 로그를 확인해봤으나, 요청 파라미터와 엔티티 값은 모두 제대로 작성되어 있었다.

ResponseDto에 null 값이 들어간 원인

원인은 빌드 과정에서 Mapper 인터페이스의 정보를 기반으로 HotelMapperImpl을 생성하는 과정에서, 타겟 클래스의 프로퍼티를 매핑할 수 없다는 에러가 발생한 것이었습니다.

// MapStruct Mapper 클래스
@Mapper
public interface HotelMapper {

    HotelMapper HOTEL_MAPPER = Mappers.getMapper(HotelMapper.class);

    @Mapping(target = "hotelId", source = "hotelId")
    HotelResponse hotelToResponse(Hotel hotel);

    List<HotelResponse> hotelToResponses(List<Hotel> hotels);

}
// 에러 로그
warning: Unmapped target properties: "hotelId, name, city, country, address, description". Mapping from Collection element "Hotel hotel" to "HotelResponse hotelResponse".           ^
1 warning

 

Property를 찾을 수 없어서 자동 생성된 구현체가 null로 초기화된 후 값을 제대로 넣지 못해 변환 과정에서 응답 객체의 필드가 모두 null로 채워지게 되었습니다.

// 자동 생성된 Mapper 클래스 구현체
import com.marriot.hotel.infrastructure.persistence.entity.Hotel;
import com.marriot.hotel.presentation.rest.dto.response.HotelResponse;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.processing.Generated;

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2025-01-02T10:08:22+0900",
    comments = "version: 1.6.3, compiler: IncrementalProcessingEnvironment from gradle-language-java-8.11.1.jar, environment: Java 17.0.7 (Azul Systems, Inc.)"
)
public class HotelMapperImpl implements HotelMapper {

    @Override
    public List<HotelResponse> hotelToResponses(List<Hotel> hotels) {
        if ( hotels == null ) {
            return null;
        }

        List<HotelResponse> list = new ArrayList<HotelResponse>( hotels.size() );
        for ( Hotel hotel : hotels ) {
            list.add( hotelToHotelResponse( hotel ) );
        }

        return list;
    }

    protected HotelResponse hotelToHotelResponse(Hotel hotel) {
        if ( hotel == null ) {
            return null;
        }
				
// null로 초기화되고 아무 값도 안들어 감
        Long hotelId = null;
        String name = null;
        String country = null;
        String city = null;
        String address = null;
        String description = null;
        int charge = 0;

        HotelResponse hotelResponse = new HotelResponse( hotelId, name, country, city, address, description, charge );

        return hotelResponse;
    }
}

왜 MapStruct는 프로퍼티값을 찾지 못한 것일까? 

바로 본론부터 이야기 하자면 build.gradle의 annotationProcessor 의존성 순서 문제였다. mapstruct는 lombok을 이용하기 때문에 Lombok을 명시해주고 mapStruct를 명시해주어야 한다.

dependencies {
	// MapStruct
	implementation 'org.mapstruct:mapstruct:1.6.3'
	annotationProcessor 'org.mapstruct:mapstruct-processor:1.6.3'

	// QueryDSL
	implementation "com.querydsl:querydsl-jpa:${querydslVersion}:jakarta"
	annotationProcessor "com.querydsl:querydsl-apt:${querydslVersion}:jakarta"
	annotationProcessor "jakarta.annotation:jakarta.annotation-api"
	annotationProcessor "jakarta.persistence:jakarta.persistence-api"

	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-validation'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'com.mysql:mysql-connector-j'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

문제가 생겼을 당시의 build.gradle

dependencies {
	implementation 'org.mapstruct:mapstruct:1.6.3'
	implementation "com.querydsl:querydsl-jpa:${querydslVersion}:jakarta"
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-validation'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'com.mysql:mysql-connector-j'

	// annotationProcessor 순서 유지 필요
	annotationProcessor 'org.projectlombok:lombok'
	annotationProcessor 'org.mapstruct:mapstruct-processor:1.6.3'
	annotationProcessor "com.querydsl:querydsl-apt:${querydslVersion}:jakarta"
	annotationProcessor "jakarta.annotation:jakarta.annotation-api"
	annotationProcessor "jakarta.persistence:jakarta.persistence-api"


	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

build.gradle annotationProcessor의 순서 조정 이후 해당 이슈 해결

 

(추가) Lombok 버전 1.18.16 이상을 사용하는 경우  lombok-mapstruct-binding 의존성을 추가하게 되면 순서와 상관없이 사용할 수 있게 된다.

 

저작자표시 비영리 변경금지 (새창열림)

'Spring 단기심화 2기' 카테고리의 다른 글

TIL_Annotation_241221  (1) 2024.12.21
TIL_MapStruct_241219  (1) 2024.12.20
TIL_JVM 구조와 동작 원리_241208  (3) 2024.12.09
TIL_HTTP 메서드의 멱등성_241205  (0) 2024.12.05
TIL_대규모 스트림 처리에서의 데이터 일관성 유지_241205  (0) 2024.12.05
'Spring 단기심화 2기' 카테고리의 다른 글
  • TIL_Annotation_241221
  • TIL_MapStruct_241219
  • TIL_JVM 구조와 동작 원리_241208
  • TIL_HTTP 메서드의 멱등성_241205
l'avenirJun
l'avenirJun
  • l'avenirJun
    오늘도 꾸준히 개발
    l'avenirJun
  • 전체
    오늘
    어제
    • 분류 전체보기 N
      • 📚 개발자의 서재 N
        • 객체지향의 사실과 오해
        • Good Code, Bad Code
        • 도메인 주도 개발 시작하기 N
      • 🔧 트러블 슈팅
      • Java
      • Spring
      • 운영체제
        • 공룡책 학습
      • 알고리즘
      • GIT
      • 면접 지식
      • Spring 단기심화 2기
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    애그리거트
    추상화
    객체지향의 사실과 오해
    캡슐화
    specification
    협력
    책임-주도 설계
    애그리거트 루트
    오블완
    good code bad code
    역할
    인터페이스
    DIP
    메시지
    코딩트리조별과제
    티스토리챌린지
    도메인 모델
    리포지터리
    표현 영역
    책임
    가독성
    매핑 구현
    코드트리
    도메인 주도 개발 시작하기
    모듈화
    객체
    타입
    코딩테스트
    유스케이스
    코드 계약
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
l'avenirJun
TIL_MapStruct Mapping 오류_250102
상단으로

티스토리툴바