강의

멘토링

로드맵

Inflearn brand logo image

인프런 커뮤니티 질문&답변

nickheatlhy님의 프로필 이미지
nickheatlhy

작성한 질문수

스프링 배치

bean 생명주기 문제 도와주세요(@Scope("step"), @Autowired)

작성

·

147

·

수정됨

0

안녕하세요. 문제가 있는데 해결 방법이 떠오르지 않아 문의드립니다.

현재 스프링 배치 4.1.4 버전을 사용하고 있고, 스프링 부트는 2.1.4 버전을 회사에서 사용하고 있습니다.

 

스프링 배치에서 JobListener를 이용해 에러가 발생할 시 잡의 마지막 부분에서 SMS 전송 시스템을 세팅해서 SMS 전송하도록 사용하려고 하는데요.

문제는 아래와 같습니다.

  1. @Scope("step") 어노테이션을 사용해야 Spel 의 값을 가져올 수 있음

  2. @Component 어노테이션을 달고, JobListener에서 @AutoWired 어노테이션을 사용하려고 할 때 @Scope("step") 영향 때문인지 해당 bean을 찾지 못함

 

즉 문제를 정리하자면, SMS class에서 Spel 의 값을 가져와야 값을 세팅할 수 있으므로, @Scope("step") 어노테이션을 달아야하는데 이 어노테이션을 붙이면 bean의 생명주기 영향으로 @AutoWired 어노테이션이 작동하지 않는 것 같습니다. 이럴 땐 어떻게 해결해야 하나요?

그리고 @Scope("step")을 아직 학습하진 않았으나 찾아보니 스프링 배치에서 사용하는 특별한 scope로 step이 실행되고 있을 때에만 해당 bean이 컨텍스트에서 유지되는 것으로 학습했습니다. 이것이 맞나요?

 

--추가

JobExecutionLisener - beforeJob 메서드 부분에 jobExecution.getExecutionContext(); 로 ExecutionContext 인스턴스를 가져온 뒤, 해당 인스턴스에 Map 형태로 값을 집어 넣습니다.

예를 들어, jobContext.put("jobId", jobId) 이런 형식으로 값을 세팅해놓고, 각 스텝에서 이 값들을 사용할 땐 @Value("#{jobExecutionContext['jobId']}" 이런 형태로 Spel를 사용합니다.

 

추가적으로 궁금한 부분은 Value 어노테이션에 어떻게 jobExecutionContext 값이 먹히는지 모르겠습니다.

원래는 이름이 똑같아야 하지 않나요? ExecutionContext 인스턴스니까 @Value("#{ExecutionContext['jobId']} 이렇게 사용을 해야할 거 같은데 왜 앞에 job이 붙어야 하는지 잘 모르겠습니다.

답변 1

0

안녕하세요, 인프런 AI 인턴입니다. H N님! 스프링 배치에서의 스코프와 빈 생명주기와 관련된 문제에 대해 설명해 드리겠습니다.

스프링 배치에서 @Scope("step") 어노테이션을 사용하실 경우, 해당 빈은 특정 스텝의 실행 시점에만 생성되고 관리되는 특수한 스코프로 전환됩니다. 따라서 스탠드얼론 빈으로서의 행동이 아니라, 스텝 내부에서만 유효한 컨텍스트를 가집니다. 이는 값 주입과 같은 부분에서 제한이 생길 수 있습니다.

해결 방법

  1. 프록시 모드 사용: @Scope("step")를 사용하면서 필요하다면 프록시 모드를 활용해서 빈 주입 문제를 피할 수 있습니다. 다음과 같이 수정해 보세요:

    @Scope(value = "step", proxyMode = ScopedProxyMode.TARGET_CLASS)
    @Component
    public class YourBean { ... }
    

    프록시 모드를 사용하면 빈이 스프링 컨텍스트에서 생성 및 참조될 때, 프록시가 대신 사용됩니다. 이는 스프링이 @Autowired를 통해 빈을 주입하는 데 필요한 참조를 허용합니다.

  2. JobExecutionListener와의 협업: JobListener와 함께 사용하는 경우라면, Job의 컨텍스트에서 데이터를 주입받을 수 있도록 스텝 스코프 내의 다른 빈이나 작업을 통해 데이터를 전달받는 방법도 가능합니다.

여기서 주의할 점은 스프링 배치의 @StepScope로 스코핑된 빈은 스텝 실행 전에 초기화되지 않는다는 것입니다. 따라서, 자원의 사용이 불필요하게 이루어지지 않도록 필요한 시점에 데이터를 이용하는 것이 주 의도입니다.

참고로, 스프링에서의 @StepScope는 컨텍스트가 스텝 실행 중일 때만 빈을 활성화하여 필요 시점에 필요한 리소스만 소모하려는 목적으로 사용됩니다.

관련 Q&A 참고: 자세한 내용은 이 링크의 질문과 답변을 참고하시면 도움이 될 수 있습니다. 여기에서도 @Scope와 관련된 다양한 논의들이 이루어진 바 있습니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

nickheatlhy님의 프로필 이미지
nickheatlhy
질문자

--추가

JobExecutionLisener - beforeJob 메서드 부분에 jobExecution.getExecutionContext(); 로 ExecutionContext 인스턴스를 가져온 뒤, 해당 인스턴스에 Map 형태로 값을 집어 넣습니다.

예를 들어, jobContext.put("jobId", jobId) 이런 형식으로 값을 세팅해놓고, 각 스텝에서 이 값들을 사용할 땐 @Value("#{jobExecutionContext['jobId']}" 이런 형태로 Spel를 사용합니다.

 

추가적으로 궁금한 부분은 Value 어노테이션에 어떻게 jobExecutionContext 값이 먹히는지 모르겠습니다.

원래는 이름이 똑같아야 하지 않나요? ExecutionContext 인스턴스니까 @Value("#{ExecutionContext['jobId']} 이렇게 사용을 해야할 거 같은데 왜 앞에 job이 붙어야 하는지 잘 모르겠습니다.

정수원님의 프로필 이미지
정수원
지식공유자

ExecutionContext는 단순히 Job과 Step에서 사용하는 데이터 저장소입니다. 하지만 스프링 배치는 Job과 Step을 구분하기 위해 jobExecutionContextstepExecutionContext라는 이름을 사용합니다. 따라서 ExecutionContext 대신 jobExecutionContext를 사용해야 정상적으로 값이 매핑됩니다.

nickheatlhy님의 프로필 이미지
nickheatlhy

작성한 질문수

질문하기