
UMC 팀프로젝트를 하면서 공공데이터를 활용해야하는 일이 생겼다. 대략 csv 파일 하나에 백만건의 row 들을 저장해야했는데, "Spring batch를 사용하면 bulk insert에 용이하다"라고 추천을 받아 활용해보기로 했다.
우선 프로젝트에 꼭 스프링 배치가 필요한지 확인해보자.
우리 프로젝트에는 건강검진결과 공공데이터를 제공받아 성별 + 연령대 별로 개인의 수치와 비교하는 결과 페이지가 필요하다. 이 과정에서 공공데이터의 평균값이나 개인 수치와 (성별 + 연령대)의 상위/하위 비율 등을 계산하려면 직접 데이터를 받아와야했다. 공공데이터에서 제공하는 단순 api는 조회만 가능하기 때문에 활용으로써는 어려울 것으로 판단된다.
벌크 인서트는 많은 데이터를 처리할 때 데이터 유실의 가능성이 있다. 또한 데이터를 새롭게 추가하거나 업데이트하는데 있어 대용량은 처리하기가 까다롭다.
이러한 점에서 Spring batch는 꽤나 적합한 선택으로 판단된다.
Spring Batch는 로깅/추적, 트랜잭션 관리, 작업 처리 통계, 작업 재시작, 건너뛰기, 리소스 관리 등 대용량 레코드 처리에 필수적인 기능을 제공합니다. 또한 최적화 및 파티셔닝 기술을 통해 대용량 및 고성능 배치 작업을 가능하게 하는 고급 기술 서비스 및 기능을 제공합니다.
Spring Batch에서 배치가 실패하여 작업 재시작을 하게 된다면 처음부터가 아닌 실패한 지점부터 실행을 하게 된다. 또한 중복 실행을 막기 위해 성공한 이력이 있는 Batch는 동일한 Parameters로 실행 시 Exception이 발생하게 된다.
장점은 아래와 같다.
✅ Spring batch 자료(1)
✅ Spring batch 자료(2)
만약, batch를 처음 보거나 구현 먼저 하려고 한다면 용어와 메타 데이터에 대해서는 이해가 안될 수도 있다. 우선 구현을 해보고 다시 뜯어보는 것도 괜찮은 방식이니 추천한다.
✅ Job
Job은 배치처리 과정을 [하나의 단위]로 만들어 놓은 객체이며, 배치 처리 과정 최상단에 위치한다.
✅JobInstance
JobInstance는 Job의 실행의 단위를 나타낸다.
Job을 실행시키게 되면 하나의 JobInstance가 생성된다. 예를들어 A일 실행, B일 실행을 하게 되면 각각의 JobInstance가 생성되며 A일 실행한 JobInstance가 실패하여 다시 실행을 시키더라도 이 JobInstance는 실패한 A일에 대한 데이터만 처리하게 된다.
✅JobParameters
JobParameters는 특정 Job을 실행할 때, 어떤 작업인지를 구별하는 고유한 식별자인 동시에, 그 작업 내부에서 사용할 데이터를 전달하는 매개변수 역할을 한다.
JobInstance = Job 이름 + JobParameters
✅JobExecution
JobExecution은 JobInstance에 대한 실행 시도에 대한 객체이다.
A일에 실행한 JobInstacne가 실패하여 재실행을 하여도 동일한 JobInstance를 실행시키지만 이 2번에 실행에 대한 JobExecution은 개별로 생기게 된다. JobExecution는 이러한 JobInstance 실행에 대한 상태,시작시간, 종료시간, 생성시간 등의 정보를 담고 있다.
✅Step
Step은 Job의 배치처리를 정의하고 순차적인 단계를 캡슐화 한다. Job은 최소한 1개 이상의 Step을 가져야 하며 Job의 실제 일괄 처리를 제어하는 모든 정보가 들어있다. (reader, processor, writer가 step에 포함된다.)
✅StepExecution
StepExecution은 JobExecution과 동일하게 Step 실행 시도에 대한 객체를 나타낸다. 하지만 Job이 여러개의 Step으로 구성되어 있을 경우 이전 단계의 Step이 실패하게 되면 다음 단계가 실행되지 않음으로 실패 이후 StepExecution은 생성되지 않는다.
✅ExecutionContext
ExecutionContext란 Job에서 데이터를 공유 할 수 있는 데이터 저장소이다. Spring Batch에서 제공하느 ExecutionContext는 JobExecutionContext, StepExecutionContext 2가지 종류가 있으나 이 두가지는 지정되는 범위가 다르다. JobExecutionContext의 경우 Commit 시점에 저장되는 반면 StepExecutionContext는 실행 사이에 저장된다. ExecutionContext를 통해 Step간 Data 공유가 가능하며 Job 실패시 ExecutionContext를 통한 마지막 실행 값을 재구성 할 수 있다.
✅JobRepository
JobRepository는 위에서 말한 모든 배치 처리 정보를 담고있다. Job이 실행되게 되면 JobRepository에 JobExecution과 StepExecution을 생성하게 되며 JobRepository에서 Execution 정보들을 저장하고 조회하며 사용하게 된다.
✅JobLauncher
JobLauncher는 Job과 JobParameters를 사용하여 Job을 실행하는 객체이다.
✅ItemReader
ItemReader는 Step에서 Item을 읽어오는 인터페이스.
✅ItemWriter
ItemWriter는 처리 된 Data를 Write 할 때 사용한다. Writer는 처리 결과물에 따라 Insert가 될 수도 Update가 될 수도 Queue를 사용한다면 Send가 될 수도 있다. Writer 또한 Read와 동일하게 다양한 인터페이스가 존재한다. Writer는 기본적으로 Item을 Chunk로 묶어 처리하고 있다.
✅ItemProcessor
Item Processor는 Reader에서 읽어온 Item을 데이터를 처리하는 역할을 하고 있다. Processor는 배치를 처리하는데 필수 요소는 아니며 Reader, Writer, Processor 처리를 분리하여 각각의 역할을 명확하게 구분하고 있다.

spring batch는 데이터를 처리하기 위한 테이블이 별도로 필요하다. 메타데이터들을 남겨서 예외 상황에도 복구할 수 있는 방어 기능을 높인 것으로 보인다. 따라서 단순히 코드만 짠다고 되는 것이 아니다.
필자는 이런 것도 모르고 코드만 작성했었다.
테이블은 위 용어 정리를 토대로 확인하면 된다.
이번 spring batch에서는 데이터의 대용량 입력을 위해 별도의 테이블을 생성하고 있다. 이렇게, 직접적으로 데이터가 사용되지 않더라도 파이프라인을 위해 설계되는점이 인상깊었다.
[caring]이라는 프로젝트에서는 지속적으로 post되는 sensor data를 스케줄링을 통해 계산하도록 파이프라인을 구성해보았다. 두 설계가 약간 유사해서 다시 spring batch 구현 코드를 보니 이해가 확연히 잘되는 것 같다.