@Mapper(componentModel = "spring")에 의한 에러

hye_·2025년 9월 4일
0

1. 문제 현상

MSA 구조인 프로젝트에서 별도의 branch로 개발 중인 서비스를 실행하려다 공유하고 있는 모듈 중 다른 branch에 있고, 개발중인 서비스에서는 사용도 하지 않고, 참조도 하지 않는 파일에 대한 FileNotFoundException으로 애플리케이션 구동이 실패하였다.

Caused by: java.io.FileNotFoundException: SomethingMapper.class

2. 원인 분석

해당 파일에는 인터페이스에 다음과 같은 선언이 있었다.

@Mapper(componentModel = "spring")
public interface SomethingMapper { ... }

componentModel = "spring" 때문에 MapStruct가 생성한 구현체를 스프링 Bean으로 등록하려고 시도,
→ 하지만 현재 브랜치/서비스에는 SomethingMapper.class가 존재하지 않음.
→ 스프링이 빈 스캐닝 과정에서 해당 클래스를 찾지 못해 FileNotFoundException 발생.

즉, 공용 라이브러리에 선언된 MapStruct Mapper가 모든 서비스에서 스프링 Bean으로 등록되려 한 것이 원인으로 추정된다.

3. 해결 방법

1) ./gradlew clean build 와 Invalidated Caches를 진행
-> 해결 안됨

2) 원인 파일의 빈 주입 방식 변경 -> 해결

Before
componentModel = "spring" 사용 → 스프링 빈 자동 등록
서비스 코드에서 @Autowired / private final 방식으로 의존성 주입

After
componentModel = "spring" 제거 → 스프링 Bean 등록 안 됨
MapStruct의 정적 팩토리 메서드 방식 사용:

@Mapper
public interface SomethingMapper {
    SomethingMapper INSTANCE = 
        Mappers.getMapper(SomethingMapper.class);
}

서비스 코드에서 직접 호출:

@Service
@RequiredArgsConstructor
public class CallingService {
...
private final SomethingMapper mapper = 
    SomethingMapper.INSTANCE;
}

해당 파일을 수정하여 배포 후 에러가 나던 서비스의 Invalidated Caches를 진행하고 원래의 branch에서 Application Run을 해보고, branch를 바꿔 Application Run을 하니 제대로 실행되었다.

정리

@Mapper(componentModel = "spring") → MapStruct 구현체가 자동으로 Spring Bean이 됨.
이 경우, classpath에 존재하는 모든 Mapper 구현체가 스프링의 스캔 대상이 되며, 멀티 브랜치/멀티 모듈 환경에서는 공용 라이브러리 JAR까지 스캔돼서 예상치 못한 Bean 로딩 문제가 생길 수 있음.
반대로 Mappers.getMapper() 방식을 쓰면 Spring Bean 관리 대상이 아니므로, 다른 브랜치에 있는 클래스까지 영향을 주지 않음.

0개의 댓글