이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • 어떤 작업을 온라인 요청에서 빼서 batch로 처리해야 하는가?
  • 사용자 응답 경로와 batch 실행 경로가 같은 DB 자원을 경쟁하면 어떤 문제가 생기는가?
  • Java/Spring 서비스에서 요청 transaction과 batch transaction 경계를 어떻게 다르게 잡는가?
  • batch로 분리한 작업의 완료와 재처리는 어떤 기준으로 검증하는가?

개요

온라인 요청은 사용자가 응답을 기다리는 경로다.

batch는 늦게 처리해도 되는 반복 작업을 별도 실행 흐름에서 처리하는 방식이다.

두 경계를 흐리면 API latency, timeout, DB connection 고갈, 중복 처리 문제가 생긴다.

정산, 쿠폰 만료, 리포트 갱신, 외부 시스템 동기화, 대량 알림은 batch 후보가 될 수 있다.

원리

온라인 요청은 짧고 예측 가능해야 한다.

batch는 처리량과 재시작 가능성이 중요하다.

따라서 transaction 크기, timeout, connection pool, retry 전략, 알림 기준이 달라야 한다.

온라인 요청 안에서 대량 작업을 처리하면 사용자는 기다리고, DB는 오래 잠기며, 실패 시 어느 부분까지 처리됐는지 알기 어렵다.

경계 판단

질문온라인 요청Batch
사용자가 기다려야 하는가아니오
처리 범위한 사용자, 한 aggregate날짜, id range, 파일
transaction짧음chunk 단위
실패 처리즉시 응답 오류재시도, 알림, 재실행
검증요청 결과count, sum, target freshness
자원API poolbatch 전용 pool 권장

사용자가 결제 완료 응답을 기다리는 것은 온라인 요청이다.

일별 정산 mart를 만드는 것은 batch다.

나쁜 예

@PostMapping("/settlements/rebuild")
public void rebuild() {
    settlementService.rebuildAll();
}

이 API는 요청 하나가 대량 작업 전체를 붙잡는다.

timeout이 나면 실제로 어디까지 처리됐는지 모를 수 있다.

또한 같은 endpoint를 두 번 누르면 중복 실행될 위험이 있다.

더 나은 방향

@PostMapping("/settlement-jobs")
public JobRunResponse requestSettlementJob(@RequestBody SettlementJobRequest request) {
    Long jobRunId = settlementJobRequestService.create(request.targetDate());
    return new JobRunResponse(jobRunId);
}

API는 job 요청만 만들고 바로 응답한다.

실제 처리는 scheduler나 worker가 job_run_id를 기준으로 수행한다.

사용자는 job 상태 조회 API로 진행 상황을 확인한다.

Batch 실행 예시

@Scheduled(cron = "0 */5 * * * *", zone = "Asia/Seoul")
public void runPendingSettlementJobs() {
    Optional<JobRun> jobRun = jobRunRepository.findNextPending("settlement");
    if (jobRun.isEmpty()) {
        return;
    }
    settlementBatchService.run(jobRun.get().id(), 1000);
}

이 코드는 trigger일 뿐이다.

진짜 batch 설계는 run 내부의 chunk, checkpoint, lock, idempotent writer에 있다.

처리 이력

CREATE TABLE batch_job_run (
    job_run_id bigserial PRIMARY KEY,
    job_name varchar(100) NOT NULL,
    target_date date NOT NULL,
    status varchar(20) NOT NULL,
    last_processed_id bigint NULL,
    processed_count bigint NOT NULL DEFAULT 0,
    started_at timestamp NULL,
    finished_at timestamp NULL,
    error_message text NULL
);

batch로 분리했다면 실행 이력이 필요하다.

이 table은 실패 후 어디서 다시 시작할지와 운영자가 무엇을 확인해야 하는지를 알려 준다.

자원 격리

batch가 API와 같은 DB connection pool을 쓰면 peak time에 장애가 전파될 수 있다.

가능하면 batch 전용 pool, 낮은 concurrency, query timeout, rate limit을 둔다.

운영 DB를 직접 읽는 batch는 chunk size와 sleep interval을 조절해 API 영향을 줄인다.

대량 write는 lock 범위와 transaction 시간을 짧게 유지한다.

판단 축

  • 사용자가 결과를 즉시 알아야 하면 온라인 요청에 둔다.
  • 지연을 허용하고 반복 처리할 수 있으면 batch 후보로 본다.
  • 대량 처리라면 API transaction과 다른 chunk transaction으로 나눈다.
  • batch로 분리하면 job run id와 상태 조회 경로를 만든다.
  • 같은 작업이 두 번 실행돼도 target이 안전한지 검증한다.

실패 모델

  • 온라인 API가 외부 API 1만 건 동기화를 직접 수행한다.
  • batch 작업이 API connection pool을 모두 점유한다.
  • 요청 timeout 후에도 서버 작업은 계속되어 중복 재시도가 발생한다.
  • batch 실패 후 마지막 처리 위치를 몰라 전체를 다시 돌린다.
  • 관리자 수동 실행 API에 lock이 없어 같은 작업이 동시에 돈다.

운영 지표

  • api_latency_p95: batch가 API 응답에 영향을 주는지 본다.
  • batch_active_count: 동시에 실행 중인 batch 수다.
  • batch_job_duration: 예상 window를 넘는지 확인한다.
  • batch_last_success_time: 마지막 성공 시각이다.
  • batch_processed_count: source 범위 대비 처리 건수다.
  • api_pool_usage_during_batch: 자원 경쟁 여부다.

완료 검증

batch 완료는 exit code만으로 판단하지 않는다.

SELECT target_date, status, processed_count, error_message
FROM batch_job_run
WHERE job_name = 'settlement'
ORDER BY job_run_id DESC
LIMIT 5;
SELECT COUNT(*) AS settlement_count, SUM(amount) AS settlement_amount
FROM settlement_daily
WHERE settlement_date = DATE '2026-06-30';

실행 이력과 target 검증을 함께 봐야 한다.

개인 프로젝트 기준

개인 프로젝트에서는 Spring Scheduler와 간단한 job table이면 충분하다.

다만 온라인 요청에서 대량 작업을 직접 수행하지 않고, job 요청과 실행을 분리한다.

실패 메시지와 마지막 처리 id만 남겨도 재실행 신뢰성이 크게 올라간다.

기업 운영 기준

기업에서는 batch window와 API peak time을 분리한다.

DBA나 인프라 팀에는 예상 row 수, chunk size, lock 범위, 실행 시간을 공유한다.

장애 알림에는 job name, target date, job run id, 마지막 처리 위치, 재실행 방법이 포함되어야 한다.

실전 팁

  • controller에서 loop로 대량 처리하는 코드를 보면 batch 후보인지 검토한다.
  • 사용자가 기다리지 않아도 되는 작업은 job request로 분리한다.
  • batch writer는 upsert나 처리 이력으로 idempotent하게 만든다.
  • batch 전용 timeout과 connection pool을 검토한다.
  • 완료 알림에는 처리 건수와 검증 결과를 같이 넣는다.

위험 신호!

  • 관리자 API 호출 하나가 수분 이상 실행된다.
  • timeout 후 재호출하면 같은 데이터가 두 번 처리된다.
  • batch가 peak time에 API pool을 점유한다.
  • job 실행 이력이 로그 파일에만 있다.
  • 성공 알림은 있지만 target count 검증이 없다.

확인 질문

확인 질문

  • 어떤 작업을 온라인 요청에서 batch로 빼야 하는가?
    • 사용자가 즉시 결과를 기다릴 필요 없고, 대량 또는 반복 처리이며, 실패 후 재실행 가능한 작업이다.
  • batch로 분리했을 때 반드시 남겨야 하는 정보는 무엇인가?
    • job run id, target range, status, last processed key, processed count, error message다.
  • API와 batch가 같은 DB 자원을 경쟁하면 어떤 문제가 생기는가?
    • API latency 증가, connection pool 고갈, lock 대기, timeout, 장애 전파가 생길 수 있다.

참고 문서