[Trouble shooting] Required a single bean, but N were found error

정의정·2024년 4월 4일
0

Trouble Shooting

목록 보기
3/3
post-thumbnail

💥 문제 상황


Boocam 프로젝트에 Spring Batch로 주기적인 데이터 업데이트를 구현한 이후 Application을 실행시켰더니 위에 첨부한 사진처럼 APPLICATION FAILED TO START 메시지가 뜨며 종료되는 문제를 발견했습니다.


💡 원인

가장 중요한 에러 메시지를 읽어봤습니다.

Parameter 1 of method deleteOldReviewsJob in com.fasttime.global.config.BatchConfig required a single bean, but 7 were found:

BatchConfigdeleteOldReviewsJob이라는 메서드 첫번째 파라미터에 하나의 빈이 요구되는데, 7개가 발견되었다는 내용이었습니다.

문제가 발생한 BatchConfigdeleteOldReviewsJob 메서드를 살펴봤습니다.

    @Bean
    public Job deleteOldReviewsJob(JobRepository jobRepository, Step deleteOldReviewsStep) {
        return new JobBuilder("deleteOldReviewsJob", jobRepository)
            .start(deleteOldReviewsStep)
            .build();
    }

해당 메서드는 2번째 파라미터에서 StepdeleteOldReviewsStep이라는 이름으로 받아오고 있습니다.

그리고 그 아래, 다음처럼 Step 스프링 빈 객체를 생성하는 여러개의 메서드가 정의되어 있었습니다.

	~ 생략 ~
    
    @Bean
    @Primary
    public Step deleteOldReviewsStep(JobRepository jobRepository,
        PlatformTransactionManager transactionManager, DeleteOldReviewsTasklet tasklet) {
        return new StepBuilder("deleteOldReviewsStep", jobRepository)
            .tasklet(tasklet, transactionManager)
            .build();
    }

	~ 생략 ~

    @Bean
    public Step updateActivityStatusStep(JobRepository jobRepository,
        PlatformTransactionManager transactionManager, UpdateActivityStatusTasklet tasklet){
        return new StepBuilder("updateActivityStatusStep", jobRepository)
            .tasklet(tasklet,transactionManager)
            .build();
    }

	~ 생략 ~

deleteOldReviewsStep, updateActivityStatusStep 그리고 그 외 5개의 메서드가 Step을 반환하도록 정의되어 있었습니다.

Step 타입의 빈이 여럿 있으니, 그 중에 어떤 빈을 주입해야 하는지 정확하게 한정할 수 없어서 발생한 문제였습니다.


🪄 해결 방안

어떤 빈을 주입해야 하는지 한정할 수 없기 때문에 발생한 문제라면, 해결 방법도 자연스레 어떤 빈을 주입해야 하는 지 한정시키는 것으로 좁혀집니다.

의존 객체를 선택할 수 있도록 한정시키는 방법은 3가지가 있었습니다.

  1. List로 받아 인덱스로 get()하여 사용하는 방법
  2. @Primary를 붙여 해당 애노테이션을 붙인 메서드로 생성한 빈을 우선적으로 주입하도록 한다.
  3. @Qualifier로 한정 값을 설정해 주입할 빈을 한정할 수 있도록 한다.

저는 세 번째 방법인 @Qualifier를 사용하는 방법을 채택했습니다. '

첫 번째 방법은 메서드 이름에 따라 인덱스가 변하기 때문에 메서드 이름 변경만으로도 문제를 초래할 수 있기 때문에 빠르게 넘어갔고, 두 번째 방법은 각 메서드마다 한정해야 할 빈이 다르기 때문에 내 상황에 알맞는 해결책은 아니라고 판단했습니다.

    @Bean
    public Job deleteOldReviewsJob(JobRepository jobRepository,
        @Qualifier("deleteOldReviewsStep") Step deleteOldReviewsStep) {
        return new JobBuilder("deleteOldReviewsJob", jobRepository)
            .start(deleteOldReviewsStep)
            .build();
    }

위처럼 @QualifierdeleteOldReviewsStep이라는 한정 값을 설정하기만 하면 됩니다. 각 빈의 기본 한정자는 빈을 생성한 메서드명이기 때문에, 이렇게 지정해두면 주입할 빈을 한정 값과 일치하는 한정자를 가진 빈 객체로 한정할 수 있게 됩니다.


💭 느낀 점

스프링 설정 클래스에서 같은 타입의 빈 객체가 여러 개인 경우 @Primary@Qualifier 애노테이션을 적절히 사용하여 한정할 수 있게 되었습니다.
이론상으로만 배운 것을 실제로 문제로 마주해 문제 해결에 사용하면서 체득할 수 있었던 것 같습니다.

profile
배움 기록

0개의 댓글