ABOUT

성능과 운영 안정성을 함께 끌어올리는 개발자입니다.

92% Positional Error Reduction
79% p95 Latency Improvement
90%+ Long Tasks Reduction

2022.02 · 한국장학재단

우수 멘티

한국장학재단 사회 리더 대학생 멘토링 IT

2022.10 · 동작구청

우수 인재상

동작구청 우수 SW 인재

2025.05 · (주) 그랩

프로그래밍 우수상

(주) 그랩 우수 프로그램 개발

2025.05 · AWSKRUG

AWS한국사용자모임 발표

AI agent 스크립트 튜닝 관련 발표

ComputerScience

Development

Engineering

Trouble Shooting

GUESTBOOK

첫 마음부터
함께 나누는 온기

방명록 작성하러 가기

SUBSCRIBE

최신소식을
편하게 만나보세요.

일괄 처리(Batch Processing)

도입

데이터를 즉시 하나씩 처리하는 방식이 아니라, 일정량의 작업을 모아 정해진 시점이나 조건에 따라 자동으로 처리하는 실행 방식이다.

실무 시스템에서는 모든 처리를 실시간으로 할 필요가 없습니다. 주문 결제 승인처럼 즉시 응답이 필요한 작업도 있지만, 정산, 통계 집계, 리포트 생성, 데이터 마이그레이션, 백업, 로그 분석처럼 모아서 처리하는 편이 더 효율적인 작업도 많습니다.

이런 작업은 보통 사용자의 요청 하나에 즉시 반응하기보다, 일정 시간 동안 쌓인 데이터를 대상으로 한 번에 실행됩니다. 이를 일괄 처리 또는 배치 처리라고 부릅니다.

그래서 배치 처리를 이해한다는 것은 단순히 “밤에 도는 작업”을 아는 것이 아니라, 대량 데이터 처리와 재시작성, 장애 복구, 스케줄링, 처리량 최적화를 함께 설계하는 관점을 이해하는 일에 가깝습니다.

필요성

대량의 반복 작업을 안정적으로 처리하려면, 실시간 요청 흐름과 분리된 별도의 실행 단위와 실패 복구 전략이 필요하다

온라인 요청은 보통 짧은 응답 시간을 목표로 합니다. 사용자가 버튼을 누르면 몇 초 안에 결과를 받아야 하므로, 긴 계산이나 대량 데이터 처리를 그 자리에서 수행하면 사용자 경험과 시스템 안정성이 모두 나빠질 수 있습니다.

반면 배치 작업은 처리량과 안정성이 더 중요합니다. 수백만 건의 데이터를 읽고, 검증하고, 변환하고, 저장해야 할 수 있으며, 중간에 실패하면 어디까지 처리했는지 추적하고 재시작할 수 있어야 합니다.

즉 배치 처리는 빠른 단건 응답보다, 대량 반복 작업을 정확하고 재현 가능하게 끝내는 데 초점을 둔 실행 모델입니다.

배치 처리가 특히 중요한 경우
  • 대량 데이터를 정해진 시간에 처리해야 할 때
  • 정산, 집계, 리포트, 통계처럼 반복적인 업무가 있을 때
  • 실시간 요청 흐름과 무거운 처리를 분리하고 싶을 때
  • 실패 후 재시작, 재처리, 중복 방지가 중요한 작업을 설계할 때

정의

일괄 처리는 여러 데이터나 작업을 하나의 묶음으로 모아, 사용자 상호작용 없이 자동으로 실행하는 처리 방식이다

배치 처리에서 핵심 단위는 개별 요청이 아니라 작업 묶음입니다. 예를 들어 하루 동안 쌓인 주문 데이터를 대상으로 정산 파일을 만들거나, 매월 말 고객별 청구서를 생성하거나, 특정 테이블의 데이터를 다른 시스템으로 이관하는 작업이 배치에 해당합니다.

이 작업들은 보통 사용자가 화면에서 기다리는 동안 실행되지 않습니다. 스케줄러, 운영자 명령, 이벤트, 배치 런처 같은 별도 트리거에 의해 실행됩니다.

즉 배치 처리는 “언제 실행할지”, “무엇을 읽을지”, “어떻게 나눠 처리할지”, “실패하면 어떻게 복구할지”까지 포함하는 실행 모델입니다.

핵심 메시지

"배치 처리의 본질은 단순 반복 실행이 아니라

대량 작업을 안정적으로 끝내기 위한 실행 단위, 상태 관리, 복구 전략을 설계하는 데 있습니다."

핵심 원리

배치의 핵심은 데이터를 읽고, 처리하고, 쓰는 흐름을 작은 실행 단위로 나누고, 각 단위의 성공·실패·재시작 상태를 관리하는 데 있다

배치 처리는 보통 Read → Process → Write 흐름으로 이해할 수 있습니다. 먼저 입력 데이터를 읽고, 비즈니스 규칙에 따라 변환하거나 검증한 뒤, 결과를 파일·DB·외부 시스템 등에 기록합니다.

중요한 점은 전체 데이터를 한 번에 메모리에 올려 처리하지 않는다는 것입니다. 대량 데이터를 chunk 또는 page 단위로 나누어 처리하고, 각 단위마다 트랜잭션 경계를 잡는 편이 안정적입니다.

또 배치 작업은 실패 가능성을 전제로 설계해야 합니다. 수십만 건을 처리하다가 중간에 실패했을 때 처음부터 다시 처리할지, 마지막 성공 지점부터 재시작할지, 일부 데이터는 skip 할지, retry 할지 명확한 정책이 필요합니다.

Batch Processing Flow
1) Job 실행
2) Step 시작
3) ItemReader 로 데이터 읽기
4) ItemProcessor 로 검증/변환
5) ItemWriter 로 결과 저장
6) chunk 단위 commit
7) 실행 메타데이터 저장
8) 실패 시 재시작 또는 복구 정책 적용

구성 요소

Spring Batch 기준으로 배치를 이해하려면 Job, Step, ItemReader, ItemProcessor, ItemWriter, JobRepository, JobLauncher 를 함께 봐야 한다
요소 역할 실무 해석
Job 배치 작업 전체 예: 일별 정산 Job, 고객 등급 갱신 Job
Step Job을 이루는 개별 단계 읽기/처리/쓰기 또는 tasklet 단위의 실행 단계
ItemReader 입력 데이터 읽기 DB, 파일, API, 큐 등에서 데이터를 가져옴
ItemProcessor 데이터 검증·변환 비즈니스 규칙 적용, 필터링, DTO 변환
ItemWriter 결과 저장 DB insert/update, 파일 쓰기, 외부 시스템 전송
JobRepository 실행 메타데이터 저장 JobExecution, StepExecution, 재시작성 관리 기반
JobLauncher Job 실행 트리거 스케줄러, API, CLI 등에서 Job을 시작할 때 사용
JobParameters Job 실행 식별과 입력값 날짜, 파일명, 대상 범위 등 실행마다 달라지는 값

실시간 처리와 차이

배치 처리는 즉시성보다 처리량과 안정성에 초점을 두고, 실시간 처리는 사용자 응답과 최신성에 더 큰 비중을 둔다
구분 배치 처리 실시간 처리
처리 단위 여러 건을 묶어서 처리 개별 요청 또는 이벤트를 즉시 처리
중요 기준 처리량, 정확성, 재시작성 응답 시간, 최신성, 지연 최소화
실행 시점 스케줄, 수동 실행, 특정 조건 요청 또는 이벤트 발생 즉시
대표 예시 정산, 리포트, 백업, 데이터 이관 로그인, 결제 승인, 주문 생성
실패 처리 재시작, 재처리, skip/retry 즉시 응답, 보상 트랜잭션, 재시도 이벤트

기본 구조

실무의 배치 시스템은 Job 정의, 실행 트리거, 입력 소스, 처리 로직, 출력 대상, 실행 메타데이터 저장소로 구성되는 경우가 많다
batch-system/
├── job/
│   ├── daily-settlement-job
│   └── customer-grade-update-job
├── step/
│   ├── read-source-data
│   ├── transform-data
│   └── write-result
├── reader/
│   ├── database-reader
│   └── file-reader
├── processor/
│   └── business-rule-processor
├── writer/
│   ├── database-writer
│   └── file-writer
├── repository/
│   └── job-execution-metadata
└── trigger/
    ├── scheduler
    ├── api
    └── cli

기본 구현

Spring Batch에서 가장 기본적인 구조는 Job 하나가 Step 하나 이상을 실행하고, Step 안에서 Reader, Processor, Writer 를 chunk 단위로 연결하는 방식이다
@Configuration
public class CustomerGradeBatchConfig {

    @Bean
    public Job customerGradeJob(
            JobRepository jobRepository,
            Step customerGradeStep
    ) {
        return new JobBuilder("customerGradeJob", jobRepository)
                .start(customerGradeStep)
                .build();
    }

    @Bean
    public Step customerGradeStep(
            JobRepository jobRepository,
            PlatformTransactionManager transactionManager,
            ItemReader<Customer> customerReader,
            ItemProcessor<Customer, CustomerGradeResult> customerProcessor,
            ItemWriter<CustomerGradeResult> customerWriter
    ) {
        return new StepBuilder("customerGradeStep", jobRepository)
                .<Customer, CustomerGradeResult>chunk(1000, transactionManager)
                .reader(customerReader)
                .processor(customerProcessor)
                .writer(customerWriter)
                .build();
    }
}
실전 포인트
chunk 크기는 단순 성능 숫자가 아닙니다. commit 단위, 메모리 사용량, 재처리 범위, DB 부하가 모두 chunk 크기의 영향을 받습니다. 너무 작으면 commit 이 많아지고, 너무 크면 실패 시 재처리 범위와 메모리 부담이 커집니다.

패턴 1. Chunk 기반 처리

대량 데이터를 안정적으로 처리하려면 전체를 한 번에 처리하기보다, 일정 개수 단위로 읽고 처리하고 쓰고 commit 하는 chunk 모델이 가장 기본적이다

Chunk 기반 처리는 배치에서 가장 흔한 구조입니다. 데이터를 하나씩 읽고, 필요한 경우 처리하고, 일정 개수가 모이면 한 번에 기록하고 commit 합니다.

이 방식의 장점은 메모리 사용량과 트랜잭션 경계를 통제할 수 있다는 점입니다. 전체 데이터를 한꺼번에 처리하지 않기 때문에 대량 데이터에도 안정적으로 대응할 수 있습니다.

chunk size = 1000

read 1
process 1
...
read 1000
process 1000
write 1000 items
commit

패턴 2. 스케줄링과 JobParameters

배치 작업은 보통 스케줄러나 외부 트리거로 실행되며, 실행 대상 날짜나 파일명 같은 값은 JobParameters 로 분리해서 관리해야 한다

배치 작업은 매일 새벽, 매월 말, 특정 파일 업로드 후, 운영자 명령 등 다양한 방식으로 실행될 수 있습니다. 이때 실행마다 달라지는 값은 코드에 박아 넣기보다 JobParameters 로 전달하는 편이 좋습니다.

예를 들어 targetDate=2026-05-19, inputFile=/data/orders.csv 같은 값은 같은 Job 정의를 여러 실행에 재사용하게 만들어 줍니다.

Job Name
dailySettlementJob

Job Parameters
targetDate=2026-05-19
region=KR
inputFile=/batch/input/orders-20260519.csv

패턴 3. 재시작성과 멱등성

배치는 실패하지 않는 작업을 만드는 것이 아니라, 실패하더라도 어디서부터 어떻게 다시 처리할지 예측 가능한 작업으로 만드는 것이 핵심이다

배치 작업은 오래 실행되고 많은 데이터를 다루기 때문에 실패 가능성이 항상 있습니다. 네트워크 오류, DB lock, 외부 API 장애, 데이터 오류, 서버 재시작 같은 상황은 언제든 발생할 수 있습니다.

따라서 좋은 배치 설계는 실패를 막는 데서 끝나지 않고, 실패 후 재시작이 가능한지, 이미 처리한 데이터를 다시 처리해도 결과가 깨지지 않는지, 중복 insert나 중복 정산이 생기지 않는지를 함께 봅니다.

이때 중요한 개념이 멱등성(idempotency) 입니다. 같은 데이터를 다시 처리하더라도 최종 결과가 한 번 처리한 것과 같도록 만드는 설계가 필요합니다.

패턴 4. Skip 과 Retry

모든 오류가 같은 의미를 갖는 것은 아니므로, 재시도할 오류와 건너뛸 수 있는 오류와 즉시 실패해야 하는 오류를 분리해야 한다

일부 오류는 일시적입니다. 예를 들어 DB deadlock, 네트워크 지연, 외부 API 일시 장애는 다시 시도하면 성공할 수 있습니다. 이런 경우 retry 정책이 적합합니다.

반대로 특정 입력 데이터 한 건이 잘못된 경우, 그 한 건을 skip 하고 나머지를 처리하는 것이 더 나을 수 있습니다. 다만 금융 정산처럼 한 건의 오류도 전체 결과에 영향을 주는 작업이라면 skip을 허용하면 안 됩니다.

즉 skip과 retry는 기술 설정이 아니라 데이터 의미와 업무 위험도에 따라 결정해야 하는 정책입니다.

오류 처리 분류
- Retry: 일시적 오류, 다시 시도하면 성공 가능
- Skip: 일부 데이터 오류, 업무상 건너뛰기 허용
- Fail: 정확성이 중요해 즉시 중단해야 하는 오류

패턴 5. 확장과 병렬 처리

배치 성능이 부족할 때는 무조건 병렬화하기보다, 단일 스레드 성능을 먼저 측정하고 병목에 맞는 확장 방식을 선택해야 한다

Spring Batch 공식 문서는 많은 배치 문제가 단일 스레드·단일 프로세스 Job으로도 해결될 수 있으므로, 복잡한 병렬 구현을 고려하기 전에 현실적인 Job 성능을 먼저 측정하라고 권장합니다.

성능이 실제로 부족하다면 여러 선택지가 있습니다. 멀티스레드 Step, 병렬 Step, partitioning, remote chunking 같은 방식으로 읽기 범위나 처리 단위를 나눌 수 있습니다.

다만 병렬화는 항상 정답이 아닙니다. DB lock, 외부 API rate limit, Writer thread-safety, 트랜잭션 경합, 순서 보장 문제를 함께 검토해야 합니다.

핵심 포인트
배치 성능 최적화는 “스레드를 늘린다”가 아니라 “병목이 어디인가”를 찾는 작업입니다. Reader가 느린지, Processor가 CPU를 쓰는지, Writer가 DB lock에 걸리는지에 따라 최적화 방향은 완전히 달라집니다.

한계와 주의점

배치 처리는 대량 반복 작업에 강하지만, 즉시성이 필요한 업무나 사용자 응답 흐름을 대신하는 도구로 쓰면 문제가 생긴다

배치는 기본적으로 지연을 허용하는 처리 모델입니다. 따라서 사용자가 즉시 결과를 기대하는 업무를 배치로 밀어내면 사용자 경험이 나빠질 수 있습니다.

또 배치 작업은 대량 데이터를 다루기 때문에 실패 시 영향 범위도 큽니다. 잘못된 조건으로 실행하면 많은 데이터를 한 번에 잘못 수정할 수 있고, 중복 실행 방지나 dry-run 검증이 없으면 복구가 어려울 수 있습니다.

마지막으로 배치는 운영 관점이 매우 중요합니다. 실행 시간, 실패 알림, 재시작 방법, 처리 건수, skip 건수, retry 횟수, 지연 시간 같은 지표가 없다면 장애 대응이 어려워집니다.

주의해야 할 지점
  • 즉시 응답이 필요한 업무를 배치로 처리하면 사용자 경험이 나빠질 수 있음
  • 중복 실행 방지 없이 재시작하면 중복 저장이나 중복 정산이 발생할 수 있음
  • chunk 크기를 잘못 잡으면 DB 부하, 메모리 사용량, 재처리 범위가 커질 수 있음
  • 병렬화는 성능을 높일 수 있지만 lock, 순서, thread-safety 문제를 만들 수 있음
  • 운영 지표와 실패 알림 없이 배치를 운영하면 장애를 늦게 발견할 수 있음

자주 하는 실수

배치 처리를 어렵게 만드는 가장 흔한 원인은 처리 로직만 작성하고, 실행 상태·재시작성·중복 방지·운영 모니터링을 나중 문제로 미루는 데 있다
  • 전체 데이터를 한 번에 메모리에 올려 처리함
  • chunk 크기를 근거 없이 정하고 성능 측정을 하지 않음
  • 실패 후 재시작 시 이미 처리한 데이터가 중복 반영됨
  • JobParameters 없이 실행해 같은 JobInstance 구분이 어려워짐
  • Skip 과 Retry 를 업무 의미 없이 기술 설정으로만 정함
  • 병렬 처리부터 적용하고 DB lock 과 writer thread-safety 를 나중에 발견함
  • 처리 건수, 실패 건수, 실행 시간, 지연 시간 지표를 남기지 않음

실무 루틴

배치를 설계할 때는 처리 로직보다 먼저 실행 단위, 입력 범위, 트랜잭션 경계, 재시작 기준, 중복 방지 기준을 정하는 편이 안전하다
  1. 먼저 이 작업이 실시간 처리인지 배치 처리인지 구분한다.
  2. Job 단위와 Step 단위를 명확히 나눈다.
  3. 입력 범위를 JobParameters 로 받을 수 있게 설계한다.
  4. Reader, Processor, Writer 의 책임을 분리한다.
  5. chunk 크기와 transaction boundary 를 성능 측정 기반으로 정한다.
  6. 재시작 시 중복 반영이 없도록 멱등성을 설계한다.
  7. 실행 시간, 처리 건수, 실패 건수, skip/retry 건수를 운영 지표로 남긴다.

디버깅

배치 문제를 디버깅할 때는 코드 한 줄보다, 어떤 JobInstance 와 JobExecution 과 StepExecution 에서 어디까지 처리됐는지부터 확인해야 한다
1
먼저 실행한 Job 이름과 JobParameters 가 기대한 값인지 확인한다.
2
JobRepository 에 저장된 JobExecution, StepExecution 상태를 확인한다.
3
Reader 가 읽은 건수, Processor 가 필터링한 건수, Writer 가 저장한 건수를 분리해서 본다.
4
실패 지점이 read, process, write 중 어디인지 나눠서 추적한다.
5
재시작 전에는 이미 commit 된 데이터와 재처리될 데이터를 반드시 구분한다.
점검 체크리스트
- JobParameters 가 올바른가
- JobInstance / JobExecution / StepExecution 상태는 무엇인가
- read count / process count / write count 가 기대와 맞는가
- 실패 지점은 Reader, Processor, Writer 중 어디인가
- chunk commit 범위는 어디까지인가
- 재시작 시 중복 반영 가능성은 없는가
- skip / retry 정책이 업무 의미와 맞는가
 
728x90