Singleton Bean 이용해서 Step 간의 데이터 공유하기

문지은·2022년 3월 25일
0

이번에 스프링 배치 프로젝트를 하면서 읽기가 두번 일어나는 작업이 필요했다. tasklet 기반으로 해결 가능하지만 chunk 기반으로 reader, processor, writer를 나누면 데이터량이 늘어나도 처리가 가능하고 로직을 분리해서 코딩이 가능하기 때문에 가독성이 높아지는 장점이 있어 나는 chunk 기반으로 배치를 구현하고자 했다. 하지만 CompositeItemWriter처럼 한번에 두개의 쓰기를 할 수 있는 애가 reader에는 존재하지 않았다.. (도대체 왜지)

결국 원래 하나의 step으로 진행하려던 일을 두 개의 step으로 나눠서 일을 진행하고자 했다. 그리고 첫번째 step에서 읽기를 수행한 뒤 해당 데이터를 두번째 step으로 보내주는 방법을 선택했다. 이와 관련해서 Spring Batch 공식 문서를 찾아보다가 아래 문서를 찾을 수 있었다.
Passing Data to Future Steps

It is often useful to pass information from one step to another. This can be done through the ExecutionContext. The catch is that there are two ExecutionContexts: one at the Step level and one at the Job level. The Step ExecutionContext remains only as long as the step, while the Job ExecutionContext remains through the whole Job. On the other hand, the Step ExecutionContext is updated every time the Step commits a chunk, while the Job ExecutionContext is updated only at the end of each Step.

해당 문서를 보면 Execution Context를 사용한다.
1. 첫번째 step에서 얻은 값을 해당 Step Context에 넣어줌
2. ExecutionContextPromotionListener를 이용해서 첫번째 step이 성공할 경우 해당 값 받아옴

하지만 여기서 난 또 문제를 만났다. 데이터를 넣으면서 serialize를 하는데 여기에서 성능 이슈가 발생할 수 있다. (serialize는 성능에 부하를 꽤 크게 주는 일)

그래서 구글링을 하다가 한 블로그에서 또 다른 방법을 알아냈다.

data share bean 이용한 문제 해결법 블로그

공유할 데이터를 빈으로 생성하여 주고 받는 방법이다! 이렇게 하면 java 단에서 깔끔하게 데이터를 주고 받을 수 있다. Spring에서 bean의 scope은 singleton이 디폴트값이므로 map을 멤버 변수로 가지는 bean을 선언하면 된다~

@Component
public class DataShareBean <T> {

    private static Logger logger = LoggerFactory.getLogger(DataShareBean.class);
    private Map<String, T> shareDataMap;

    public DataShareBean () {
    	// thread safe한 concurrentMap 이용
        this.shareDataMap = Maps.newConcurrentMap();
    }

    public void putData(String key, T data) {
        if (shareDataMap ==  null) {
            logger.error("Map is not initialize");
            return;
        }

        shareDataMap.put(key, data);
    }

    public T getData (String key) {

        if (shareDataMap == null) {
            return null;
        }

        return shareDataMap.get(key);
    }

    public int getSize () {
        if (this.shareDataMap == null) {
            logger.error("Map is not initialize");
            return 0;
        }

        return shareDataMap.size();
    }

}

오늘의 결론은...! 공식 문서가 무조건적인 정답은 아니다!
하지만 공식 문서를 먼저 보는 건 맞다 🧐

profile
백엔드 개발자입니다.

0개의 댓글