Search

섹션 1. 배치 처리와 스프링 배치

생성일
2025/10/04 07:21
태그

배치 처리 이해하기

배치 처리란?

배치(Batch) 처리는 대량의 데이터를 동시에 또는 정해진 시간에 일괄적으로 처리하고 결과를 출력하는 작업을 말한다. 우리가 따로 작업을 수행하지 않더라도, 시스템이 자동으로 효율적으로 데이터를 처리하게 만드는 작업이다.
대형 게임 회사의 서버 개발자를 예시로 들면, 매일 새벽 4시에 서버 점검 시간을 두고 그 사이에 전날 유저의 로그를 분석하고, 랭킹을 업데이트하고, 불법 프로그램 사용자를 검출하는 등의 작업을 하기 위해서는 정말 많은 개발자가 많은 시간을 들여야 될 것이고 이는 사실상 불가능하다. 배치 처리를 사용하면 정해진 시간(새벽 4시)에 우리가 원하는 작업(로그 분석, 랭킹 갱신 등)을 자동으로 처리될 수 있게 된다.

배치 처리의 특성

배치가 무엇인지 어렴풋이 알았으니, 배치 처리의 특성에 대해 더 자세히 알아보자. 배치 처리의 특성은 다음과 같다.
대용량 데이터 처리 : 배치는 대량의 데이터를 효율적으로 처리한다. 웹 애플리케이션이 특정 ID의 데이터 하나를 조회하거나 수정한다면, 배치는 수천 수만 개의 데이터를 한 번에 처리한다.
정기적 실행 : 매일, 매주, 매달 등 스케줄에 따라 정해진 시간에 실행된다.
자동화 : 한 번 만들고 설정해두면, 사람이 지켜보지 않더라도 설정에 따라 자동으로 처리된다.
유한한 양의 데이터 처리 : 무한의 데이터를 처리하는 것은 불가능하므로, 배치는 오늘의 거래 데이터, 이번 주의 로그, 지난 달의 매출 등 특정 기간의 유한한 양의 데이터를 처리한다. 데이터의 양이 정해져있기 때문에, 배치 처리의 결과와 시간을 정확하게 예측할 수 있다.
가용 자원 부하 예측 : CPU, 메모리, 디스크 용량 등 시스템에서 감당할 수 없는 데이터 처리는 시스템이 다운된다. 때문에 시스템의 처리 능력에 맞게 배치를 설계해야한다.

실무 배치 사례 살펴보기

실전에서 배치를 활용해 어떤 데이터들을 처리하는지 살펴보자.
일일 정산 배치 : 매일 밤 자정, 하루 동안의 거래 내역과 결제 정보, 사용자 활동 로그를 모두 정리하고 집계한다.
대용량 데이터 마이그레이션 : 시스템 업그레이드나 DB 변경 시 수행되는 배치로, 수백만, 수천만 건의 데이터를 새로운 형태의 데이터로 변경하거나 새로운 저장소로 옮기는 작업을 수행한다.
리포트 생성 배치 : 데이터를 주기적으로 모아 리포트를 만들어내는 배치로, 생성 후 이메일이나 알람 메세지 형태로 전달되어 중요한 결정을 내리는데 필요한 정보를 제공한다.
데이터 정제 배치 : 중복된 데이터를 제거하거나 형식을 통일하여 데이터 품질을 개선하는 작업이다. 오래되어 사용되지 않거나 정합성이 맞지않거나, 부정확한 데이터를 정리한다.
백업 배치 : 정기적으로 데이터를 백업해 시스템 장애나 데이터 손실에 대비하는 작업이다. 예기치 못한 문제 발생 시 데이터를 복구하고 서비스를 빠르게 정상화하기 위한 안정망 역할을 한다.
데이터 통합 배치 : 여러 데이터 저장소에서 데이터를 수집하여 하나의 일관된 데이터 상태로 만드는 작업이다. 이 배치 작업은 데이터의 정합성을 유지하고, 다양한 서비스에서 통합된 최신 데이터를 활용하기 위한 기반 작업이 된다.
데이터 통합 전략
고객 데이터 통합 : CRM, 주문 관리, 마케팅 시스템 등 여러 소스에 흩어진 고객 데이터를 통합해, 고객의 모든 정보를 한눈에 볼 수 있는 뷰를 구착하여 맞춤형 마케팅과 개인화 서비스를 제공한다.
사용자 로그 통합(행동 패턴 분석) : 웹 사이트, 모바일 앱, API 등에서 생성된 사용자 활동 데이터를 통합하여 사용자 행동 패턴을 분석한다.
운영 데이터 통합(시스템 진단 및 최적화) : 서버 로그, 애플리케이션 모니터링 데이터, 시스템 리소스 사용량 등을 통합하여 성능 지표와 모니터링 데이터를 중앙에서 관리한다.
데이터 웨어하우스 구축 : ERP, CRM, 외부 API에서 데이터를 끌어와 중앙 데이터 웨어하우스에 저장한다. 이렇게 통합된 데이터는 비즈니스 분석과 의사결정의 기반으로 활용된다.
검색 엔진 동기화(정보 최신화) : 상품 정보나 리뷰, 키워드 데이터를 통합하여 검색 엔진 인덱스를 최신 상태로 유지한다.

웹과 배치의 차이점

대부분의 개발자들은 RESTful API를 만들고, 데이터베이스랑 통신하고, 요청 처리하는 등의 웹 애플리케이션 중심으로만 다룬다. 하지만 배치는 대규모 데이터 처리와 자동화를 통해 시스템의 기반이 되는 데이터를 만드는 작업이다. 우리는 웹 애플리케이션과 배치 처리의 차이를 확실히 이해하고, 잘 활용할 필요가 있다.

실시간 대화 vs 일방적 전달

웹 애플리케이션 : 사용자가 요청하면, 즉각적으로 서버가 요청을 받아 JSON, HTML 등의 결과로 돌려준다. 이러한 실시간 반응이 웹 애플리케이션의 본질이다.
배치 처리 : 배치는 사용자의 요청을 기다리지 않고, 정해진 스케줄에 따라 미리 설정된 작업이 자동으로 실행된다. 특정 시점에 대량으로 데이터를 처리하거나 주기적으로 실행되는 작업에서 강점이 있다.

결과의 속도

웹 애플리케이션 : 사용자 요청에 즉각적으로 응답하는 것이 중요하다. 페이지 로딩이 1초 이상 걸리면 대부분의 사용자가 떠난다.
배치 처리 : 배치 처리는 결과의 속도보다 정확성과 완결성이 우선된다. 데이터 처리에 시간이 걸리더라도 대량의 데이터를 완벽하게 처리하는 것이 중요하다.

처리량

웹 애플리케이션 : 한 번에 한 사용자의 요청을 처리한다.
배치 처리 : 대량의 데이터를 효율적으로 처리한다.

오류 처리

웹 애플리케이션 : 잘못된 요청이 오면 에러 코드를 반환하여 사용자에게 문제를 알리고 종료한다.
배치 처리 : 문제가 발생하면 재시도한 후, 실패가 반복되면 중단한 뒤 로그와 알림으로 관리자를 호출하여, 발생한 문제가 해결된 뒤 실패한 작업을 재시작하도록 만드는 것이 일반적이다.

리소스 사용

웹 애플리케이션 : 웹 서버는 상시 실행 중으로, 사용자 요청이 없을 때도 대기 상태로 유지하여 CPU와 메모리를 차지한다.
배치 처리 : 배치는 필요할 때 실행되어, 작업이 끝나면 리소스를 반환하고 종료한다.

차이점 요약

기준
웹 애플리케이션
배치 처리
실행 방식
사용자 요청 기반(실시간)
스케줄 기반(스케줄링)
처리 우선순위
응답 시간 최적화(즉각적 피드백)
처리량 및 신뢰성(정확성, 완결성) 최적화
데이터 처리량
단일 요청 단위
대량의 데이터(수백, 수천만 건)
오류 처리
즉시 에러 응답 반환
재시도 및 복구, 알림
리소스 사용
상시 사용
필요 시에만 사용

웹 애플리케이션과 배치 처리 공존

위의 차이점에서 알 수 있듯, 웹 애플리케이션은 사용자와의 연결을 담당하여 실시간 요청을 처리하고 배치는 시스템의 후방에서 대규모 데이터를 처리하는 것을 담당한다. 웹 애플리케이션만으로도 운영은 가능하지만, 배치와 함께 사용할 때 시스템이 더 완벽해진다.

스프링 배치

배치 처리의 중요성에 대해서 이해했으니, 이제 스프링 배치에 대해 알아보자. 스프링 배치는 스프링 프레임워크를 기반으로 만들어진 배치 처리 프레임워크로, 우리에게 익숙한 스프링 기술로 배치 처리의 일반적인 패턴을 손쉽게 구현할 수 있게 도와준다. 이제 스프링 배치가 제공하는 핵심 기능들을 살펴보자.

유지보수성

웹 애플리케이션은 비즈니스 요구사항에 따라 자주 수정되지만, 배치 처리는 한 번 정리된 업무 프로세스를 반복적으로 수행하기 때문에 상대적으로 변경이 적다. 따라서 잘못 만든 배치는 시스템 전체 장애를 만들 수 있기 때문에, 배치는 만들 때 신중하게 잘 만들어야한다.
스프링 배치는 스프링의 장점을 그대로 계승하여, 익숙한 스프링 기술로 유지보수하기 쉬운 배치 처리 시스템을 만들 수 있다.
의존성 주입(DI) : 컴포너느 간 결합을 낮추고, 코드 변경 시 전체 시스템에 영향을 주지 않게 만든다.
관점 지향 프로그래밍(AOP) : 횡단 관심사를 분리하여 코드의 모듈화를 극대화한다.
추상화 계층 : 추상화 계층을 통해 특정 기술에 대한 종속성을 차단할 수 있다.
이외에도 스프링 배치는 일반적인 배치 처리의 패턴을 추상화한 컴포넌트들을 제공한다. 웹 애플리케이션에서 컨트롤러, 서비스, 레포지토리 패턴을 가지는 것처럼, 배치 처리에도 읽고, 가공하고, 저장하는 일반적인 패턴이 있다. 스프링 배치는 이런 패턴을 다음과 같은 표준화된 방식으로 제공한다.
Job : 하나의 완전한 배치 처리 작업
Step : Job의 세부 실행 단계(하나의 Job은 여러 Step으로 구성될 수 있다)
ItemReader : 저장되어 있는 데이터를 읽어오는 컴포넌트
ItemProcessor : 읽어온 데이터를 가공하는 컴포넌트
ItemWriter : 데이터를 저장하는 컴포넌트
스프링 배치의 표준 컴포넌트들로 시간이 지나도 유지보수하기 쉬운 시스템을 만들 수 있다.

유연한 데이터 처리

배치 처리의 핵심은 다양한 데이터 소스를 다루어 가공하는 것이다. 스프링 배치에서는 ItemReader와 ItemWriter라는 표준화된 인터페이스를 통해 다양한 데이터 소스를 일관된 방식으로 처리할 수 있다. 또한 스프링 배치에서는 이미 대부분의 데이터 소스에 대한 ItemReader/Writer 구현체를 제공하고 있어, 구현체의 설정만으로 원하는 데이터 소스를 쉽게 다룰 수 있다.
플랫 파일(CSV, TXT) : FlatFileItemReader, FlatFileItemWriter 제공
XML / JSON : StaxEventItemReader, JsonItemReader, JsonFileItemWriter 제공
RDB : JdbcCursorItemReader, JdbcBatchItemWriter (JPA와 같은 ORM 프레임워크도 지원)
NoSQL : MongoDB, Redis 등을 위한 전용 Reader/Writer 제공
메세징 시스템 : KAFKA, AMQP 등 메세지 큐 시스템과의 연동 지원
스프링 배치의 강력한 점은 이 모든 데이터 소스들을 추상화된 인터페이스를 통해 일관된 방식으로 다룰 수 있어 유연하다는 것이다. 파일에서 DB, DB에서 NoSQL로 데이터를 옮기는 작업도 단순히 ItemReader와 ItemWriter 구현체만 바꾸면 해결된다.

견고한 트랜잭션 관리

스프링 배치는 다음과 같은 트랜잭션 관리 기능도 제공한다.
체크포인트 : 긴 배치 작업 중간에 안전 지점을 설정하고, 문제 발생 시 처음이 아닌 마지막 체크포인트부터 재시작할 수 있다.
트랜잭션 범위 설정 : 처리할 데이터 양과 특성에 따라 트랜잭션 범위를 조절할 수 있다. 이를 통해 작은 단위의 커밋으로 메모리 사용량 조절하는 것도 가능하다.
배치 작업 역시 트랜잭션을 두는 것이 중요하며, 이러한 견고한 트랜잭션 관리 기능을 제공하기 때문에 대용량 데이터를 안전하게 처리할 수 있다.

재시작 기능과 유연한 실행 제어

스르핑 배치는 작업 실행을 세밀하게 제어할 수 있도록 기능을 제공한다.
재시작 기능
오류 발생 시 마지막으로 성공한 Step부터 재시작 가능
Step 내에서 마지막 처리 항목 이후부터 작업 재개 가능
유연한 실행 제어
특정 Step만 선택적으로 실행 가능
파라미터를 통한 동적 작업 제어
이전 Step의 결과에 따라 다음 작업 결정 가능(Step의 조건부 실행)
작업 상태 추적
모든 Job과 Step의 실행 상태를 메타데이터로 관리
작업의 실행 시점과 결과를 정확하게 추적 가능

대용량 데이터 처리와 확장성

스프링 배치는 대용량 데이터를 효율적으로 처리하면서도 다양한 규모의 작업에 유연하게 대응할 수 있다.
Chunk 지향 처리
대량의 데이터를 지정된 크기로 나누어 순차적으로 처리
한 번에 처리하는 데이터 양을 제한하여 메모리 사용 최적화
효율적인 데이터 읽기
페이징 방식 - 일정 크기만큼 데이터를 조회하여 처리
커서 방식 - 데이터베이스 커서를 사용한 효율적인 데이터 스트리밍
확장성 기법(Scalability Techniques)
멀티스레드 Step - 하나의 Step을 여러 스레드로 처리
병렬 Step - 여러 Step을 동시에 실행
분산 처리 - 여러 서버에서 배치 처리 분산 실행

테스트 용이성

스프링 배치는 배치 애플리케이션을 쉽게 테스트할 수 있는 다양한 도구들을 제공한다.
전용 테스트 도구
배치 작업의 실행과 결과를 손쉽게 검증할 수 있는 도구들 제공
테스트 환경에서 실행하고 검증 가능
스프링의 테스트 지원
우리에게 익숙한 스프링의 테스트 도구들을 배치에서도 그대로 사용
독립된 테스트 환경 제공
단위 테스트
복잡한 배치 작업을 작은 단위로 나누어 테스트 가능
이러한 테스트 지원 기능들을 통해 복잡한 배치 프로세스도 쉽게 검증할 수 있다.

배치의 전체적인 시스템 이해하기

스프링 배치는 단순히 배치 처리를 위한 도구가 아니라, 수많은 컴포넌트들이 엮인 거대한 시스템이다. 이 복잡한 시스템을 다루기 위해서는 전체적인 맥락을 이해할 필요가 있다.

Job과 Step

스프링 배치에서 가장 많이 듣게 될 두 가지 개념이다.

Job

Job은 배치 처리의 가장 큰 단위로, 하나의 완전한 배치 처리를 의미한다. 실제 우리가 흔히 접하는 배치 작업들은 전부 Job으로 표현된다.
매일 밤 12시에 수행되는 ‘일일 매출 집계
매주 일요일마다 처리되는 ‘휴면 회원 정리
매월 1일에 실행되는 ‘정기 결제
필요 시점에 실행할 수 있는 ‘대용량 데이터 마이그레이션

Step

Step은 Job을 구성하는 실행 단위로, 하나의 Job은 하나 이상의 Step으로 구성된다. 예시로 ‘일일 매출 집계’ Job은 다음과 같은 Step들로 이루어질 수 있다.
매출 집계 Step
전일 주문 데이터를 읽기(Read)
결제 완료된 것만 필터링(process)
상품별/카테고리별로 집계하여 저장(Write)
알림 발송 Step
집계 요약 정보를 생성하여 관리자에게 전달
캐시 갱신 Step
집계된 데이터로 캐시 정보 업데이트
이처럼 하나의 Job이 여러 개의 Step으로 구성되어 있으며, 각 Step이 순차적으로 실행되는 구조를 나타낸다. 모든 Step이 정상적으로 완료되어야 Job도 성공적으로 완료된다.

전체 시스템을 보기 전에…

스프링 배치는 Job을 만들기 위해 필요한 대부분의 컴포넌트와 실행 흐름을 제공하고 있다. 개발자는 이 위에 스프링 배치가 제공하는 컴포넌트들을 @Configuration을 통해 적절히 구성하기만 하면된다.
위 말을 이해하면, 전체를 크게 스프링 배치가 제공하는 영역개발자가 제어하는 영역으로 나우어 바라보면 이해하기 쉬울 것이다.

스프링 배치가 제공하는 영역

스프링 배치 프레임워크에서 제공하는 핵심 컴포넌트들을 살펴보자.
Job / Step : 앞서 살펴본 이 두 요소가 스프링 배치의 핵심 컴포넌트이다.
JobLauncher : Job을 실행하고 실행에 필요한 파라미터를 전달하는 역할을 수행한다.(배치 작업의 시작점)
JobRepository : 배치 처리의 모든 메타데이터를 저장하고 관리하는 핵심 저장소로, Job과 Step의 실행 정보(시작/종료 시간, 상태, 결과 등)를 저장한다. 이렇게 저장된 정보들로 배치 작업을 모니터링하거나 문제 발생 시 재실행에 사용한다.
ExecutionContext : Job과 Step 실행 중 상태 정보를 key-value 형태로 담는 객체다. Job과 Step 간의 데이터 공유나 Job 재시작 시 상태 복원에 사용된다.
ItemReader 구현체 : JdbcCursorItemReader, JpaPagingItemReader, MongoCursorItemReader 등 다양한 데이터 소스로부터 데이터를 읽어올 수 있다.
ItemWriter 구현체 : JdbcBatchItemWriter, JpaItemWriter, MongoItemWriter 등을 통해 처리된 데이터를 저장할 수 있다.

개발자가 제어하는 영역

Job/Step 구성

@Configuration을 사용해 Job과 Step의 실행 흐름을 정의한다.
각 Step의 실행 순서와 조건을 설정하고, Spring 컨테이너에 등록해 배치 잡의 동작을 구성한다.
Spring의 DI(의존성 주입)를 활용해 ItemReader, ItemProcessor, ItemWriter 등 배치 작업에 필요한 컴포넌트들을 조합하고 배치 플로우를 완성한다.
@Bean fun dataTermationJob(terminateStep: Step): Job { return JobBuilder("dataTerminationJob", jobRepostiroy) .start(terminateStep) .build(); } @Bean fun terminateStep(reader: ItemReader<String>, writer: ItemWriter<String>): Step { return StepBuilder("terminateStep", jobRepository) .<String, String>chunk(10, transactionManager) .reader(reader) .writer(writer) } @Bean fun itemReader(): ItemReader<String> { // return ItemReaderImpl } @Bean fun itemWriter(): ItemWriter<String> { // return ItemWriterImpl }
Kotlin
복사

데이터 처리 컴포넌트

스프링 배치는 ItemReader, ItemWriter 같은 데이터 처리 컴포넌트 구현체를 제공하지만, 파일 포맷이나 SQL 쿼리 조건 등 세부 로직은 개발자가 직접 지정해야한다.
예를 들어, FlatFileItemReader로 CSV 파일을 읽을 경우, CSV의 각 컬럼을 자바 객체의 프로퍼티와 매핑하는 방식은 개발자가 직접 지정해야한다.

단순 작업 처리

모든 배치 Job이 데이터를 읽고-처리하고-쓰는 방식으로만 구성되지는 않는다. 때로는 파일 복사나 디렉토리 이동, 알림 방송 등 단순 작업이 필요할 때도 있다. 다행히도 스프링 배치는 이러한 작업들을 직접 구현할 수 있는 포인트를 제공하고 있고, 여기에 개발자가 자유롭게 작성하여 단순 작업을 처리할 수 있다.

커스텀 데이터 처리 포인트

스프링 배치는 다양한 데이터 소스를 다룰 수 있는 기본 구현체들을 지원하지만, 모든 데이터베이스나 포맷을 커버하지는 못한다. 과거에는 MongoDb Cursor 기반의 ItemReader가 제공되지 않았고, Redis에 데이터를 읽고 쓰는 ItemReader와 ItemWriter 구현체는 Spring Batch 5.1이 되어서야 공식적으로 제공되었다.
이처럼 스프링 배치가 제공하지 않는 데이터 소스를 사용하고자 한다면, ItemReader와 ItemWriter를 개발자가 직접 구현해야한다. 또한 ItemProcessor는 비즈니스 로직의 핵심을 담당하므로, 당연하게도 개발자가 직접 구현해야한다.

간단한 스프링 배치 만들어보기

Spring Boot를 통해 아주 간단한 배치 작업을 만들어보자.
먼저 start.spring.io에 가서 batch와 h2 라이브러리 의존성만 받은 후 프로젝트를 생성한다.
@Configuration class HelloBatchConfig( private val jobRepository: JobRepository, private val transactionManager: PlatformTransactionManager, ) { @Bean fun helloJob(): Job { return JobBuilder("helloJob", jobRepository) .start(firstStep()) .next(secondStep()) .next(lastStep()) .build() } @Bean fun firstStep(): Step { return StepBuilder("firstStep", jobRepository) .tasklet({ contribution, chunkContext -> println("This is my first step") RepeatStatus.FINISHED }, transactionManager) .build() } @Bean fun secondStep(): Step { return StepBuilder("secondStep", jobRepository) .tasklet({ contribution, chunkContext -> println("This is my second step.") RepeatStatus.FINISHED }, transactionManager) .build() } @Bean fun lastStep(): Step { return StepBuilder("lastStep", jobRepository) .tasklet({ contribution, chunkContext -> println("This is my last step") RepeatStatus.FINISHED }, transactionManager) .build() } }
Kotlin
복사
그 후 위와 같이 우리가 필요한 배치를 작성해주면 된다. Spring boot가 의존성 주입, 데이터베이스 소스 설정 등 나머지 모든 작업을 알아서 처리해준다.