김지팡의 저장소
article thumbnail
728x90

🚨 [에러 발생]

동영상에 대한 통계와 정산 작업, 광고에 대한 통계와 정산 작업을 배치를 통해 수행하기 위해 다음과 같이 설정하였다.

 

@Component
@RequiredArgsConstructor
public class BatchJobRunner {

    private final JobLauncher jobLauncher;
    private final Job calculateDailyVideo;
    private final Job calculateDailyAdvertisement;

    @Scheduled(cron = "*/10 * * * * ?") //
    public void runCalculateDailyVideoJob() throws Exception {
        System.out.println("Executing calculateDailyVideoJob");
        JobParameters jobParameters = new JobParametersBuilder()
                .addLong("time", System.currentTimeMillis()) // 고유한 매개변수 추가
                .toJobParameters();
        jobLauncher.run(calculateDailyVideo, jobParameters);
    }

    @Scheduled(cron = "*/10 * * * * ?") //
    public void runCalculateDailyAdJob() throws Exception {
        System.out.println("Executing calculateDailyAdvertisementJob");
        JobParameters jobParameters = new JobParametersBuilder()
                .addLong("time", System.currentTimeMillis()) // 고유한 매개변수 추가
                .toJobParameters();
        jobLauncher.run(calculateDailyAdvertisement, jobParameters);
    }
}
@Configuration
public class JobConfig {

    @Autowired
    private StepConfig stepConfig;

    @Bean
    public Job calculateDailyVideo(JobRepository jobRepository) {
        return new JobBuilder("calculateDailyVideo", jobRepository)
                .start(stepConfig.calculateVideoStat())
                .next(stepConfig.calculateVideoAdjustment())
                .build();

    }

    @Bean
    public Job calculateDailyAdvertisement(JobRepository jobRepository) {
        System.out.println("광고 정산 및 통계");
        return new JobBuilder("calculateDailyAdvertisement", jobRepository)
                .start(stepConfig.calculateAdStat())
                .next(stepConfig.calculateAdAdjustment())
                .build();
    }
}
@Configuration
@RequiredArgsConstructor
public class StepConfig {

    @Autowired
    private JobRepository jobRepository;
    @Autowired
    private PlatformTransactionManager transactionManager;
    @Autowired
    private VideoStatisticsTasklet videoStatisticsTasklet;
    @Autowired
    private VideoItemReader videoItemReader;
    @Autowired
    private VideoAdjustmentProcessor videoAdjustmentProcessor;
    @Autowired
    private VideoAdjustmentWriter videoAdjustmentWriter;
    @Autowired
    private AdStatisticsTasklet adStatisticsTasklet;
    @Autowired
    private AdItemReader adItemReader;
    @Autowired
    private AdAdjustmentProcessor adAdjustmentProcessor;
    @Autowired
    private AdAdjustmentWriter adAdjustmentWriter;

    @Bean
    @JobScope
    public Step calculateVideoStat() {
        return new StepBuilder("calculateVideoStat", jobRepository)
                .tasklet(videoStatisticsTasklet, transactionManager)
                .build();
    }

    @Bean
    public Step calculateVideoAdjustment() {
        return new StepBuilder("calculateVideoAdjustment", jobRepository)
                .<Video, VideoAdjustment>chunk(10, transactionManager)
                .reader(videoItemReader)
                .processor(videoAdjustmentProcessor)
                .writer(videoAdjustmentWriter)
                .build();

    }

    @Bean
    public Step calculateAdStat() {
        return new StepBuilder("calculateAdStat", jobRepository)
                .tasklet(adStatisticsTasklet, transactionManager)
                .build();
    }

    @Bean
    public Step calculateAdAdjustment() {
        return new StepBuilder("calculateAdAdjustment", jobRepository)
                .<VideoAd, AdAdjustment>chunk(10, transactionManager)
                .reader(adItemReader)
                .processor(adAdjustmentProcessor)
                .writer(adAdjustmentWriter)
                .build();
    }
}

 

 

이렇게 작성을 하고 실행을 하니 다음과 같은 에러가 발생하였다.

 

Parameter 1 of constructor in com.example.settlement_batch.batch.config.BatchJobRunner required a single bean, but 2 were found:

 

 

💡 [해결 방법 1]

찾아보니 해당 에러는 BatchJobRunner 클래스에서 의존성을 주입할 객체를 탐색할 때, 같은 타입의 객체가 2개 이상 존재해서 발생하는 에러였다.

이때에는, 어떤 객체를 주입할 것인지 명시적으로 지정해 줘야 된다고 하는데, 이는 @Qualifier 어노테이션을 활용해서 할 수 있다.

 

@Component
@RequiredArgsConstructor
public class BatchJobRunner {

    private final JobLauncher jobLauncher;
    @Qualifier("calculateDailyVideo")
    private final Job calculateDailyVideo;
    @Qualifier("calculateDailyAdvertisement")
    private final Job calculateDailyAdvertisement;

    @Scheduled(cron = "*/10 * * * * ?") //
    public void runCalculateDailyVideoJob() throws Exception {
        System.out.println("Executing calculateDailyVideoJob");
        JobParameters jobParameters = new JobParametersBuilder()
                .addLong("time", System.currentTimeMillis()) // 고유한 매개변수 추가
                .toJobParameters();
        jobLauncher.run(calculateDailyVideo, jobParameters);
    }

    @Scheduled(cron = "*/10 * * * * ?") //
    public void runCalculateDailyAdJob() throws Exception {
        System.out.println("Executing calculateDailyAdvertisementJob");
        JobParameters jobParameters = new JobParametersBuilder()
                .addLong("time", System.currentTimeMillis()) // 고유한 매개변수 추가
                .toJobParameters();
        jobLauncher.run(calculateDailyAdvertisement, jobParameters);
    }
}

 

 

그렇지만, 이렇게 해도 여전히 동일한 에러가 발생하고 있었다. 🥹

 

 

 

💡 [해결 방법 2]

@Qualifier 어노테이션에 위와 같은 문구가 뜨는 것을 알 수 있었다. Lombok이 Qualifier를 생성자로 복사하지 못한다는 것이었다.

 

@RequiredArgsConstructor는 Lombok에서 제공하는 것인데, 의존성 주입 시 필요한 생성자를 자동으로 만들어주는 역할을 한다. 하지만, @RequiredArgsConstructor는 기본적으로 필드에 대해서만 파라미터로 포함을 시켜 생성자를 만들어주는 것이었다.

 

때문에, @Qualifier에 대해서는 @RequiredArgsConstructor가 복사하지 못해 같은 타입의 객체를 주입해야 하는 상황에서 어떠한 것을 주입해야 할지 알 수 없어 에러가 발생한 것이었다.

 

이를 해결하기 위해서는 직접 생성자를 만들어 주어 생성자 주입 방식으로 해결을 해야 했다.

@Component
public class BatchJobRunner {

    private final JobLauncher jobLauncher;
    private final Job calculateDailyVideo;
    private final Job calculateDailyAdvertisement;

    public BatchJobRunner(JobLauncher jobLauncher, @Qualifier("calculateDailyVideo") Job calculateDailyVideo, @Qualifier("calculateDailyAdvertisement") Job calculateDailyAdvertisement) {
        this.jobLauncher = jobLauncher;
        this.calculateDailyVideo = calculateDailyVideo;
        this.calculateDailyAdvertisement = calculateDailyAdvertisement;
    }

    @Scheduled(cron = "*/10 * * * * ?") //
    public void runCalculateDailyVideoJob() throws Exception {
        System.out.println("Executing calculateDailyVideoJob");
        JobParameters jobParameters = new JobParametersBuilder()
                .addLong("time", System.currentTimeMillis()) // 고유한 매개변수 추가
                .toJobParameters();
        jobLauncher.run(calculateDailyVideo, jobParameters);
    }

    @Scheduled(cron = "*/10 * * * * ?") //
    public void runCalculateDailyAdJob() throws Exception {
        System.out.println("Executing calculateDailyAdvertisementJob");
        JobParameters jobParameters = new JobParametersBuilder()
                .addLong("time", System.currentTimeMillis()) // 고유한 매개변수 추가
                .toJobParameters();
        jobLauncher.run(calculateDailyAdvertisement, jobParameters);
    }
}

 

 

이렇게 하니 에러 없이 정상적으로 배치 작업을 수행할 수 있었다. 😚😚

 

 

 

 

🧑‍💻

[회고]

Intellij에서는 컴파일 오류나 문법 오류 등 여러 오류에 대해서 코드 아래에 빨간 줄로 표현해 준다. 덕분에, 해당 카테고리류 에러에 대한 것을 쉽게 잡을 수 있다. 

 

노란 줄로도 표현을 하는데, 이것은 보통 경고 정도이다. 실행은 되지만 개선 사항이 필요할 때 주로 노란 줄로 표현을 해주는 것이다.

 

여태 노란 줄에 대한 코드를 수정하지 않아도 실행에는 문제가 없었다. 하지만, 이번을 계기로 노란 줄로 표현된 코드들에 대해서 한 번씩 살펴보아야겠다는 생각을 하게 됐다. 

728x90
profile

김지팡의 저장소

@김지팡

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!