3 min read

Part 6. 수동 배치 전략: REST 트리거, Admin UI, 파라미터 재처리, 롤백

수동 배치를 안전하게 운영하기 위한 API 설계, 권한 통제, 감사 추적, 재처리 및 롤백 전략을 정리한다.

Series: Spring Boot 배치 전략 완전 정복

12편 구성. 현재 6편을 보고 있습니다.

썸네일 - 운영자 모니터링
썸네일 - 운영자 모니터링

출처: Pexels - Security control room team

버전 기준

  • Java 21
  • Spring Boot 3.3.x
  • Spring Batch 5.2.x
  • Quartz 2.3.x
  • PostgreSQL 15
  • OpenSearch 2.x

1) 문제 제기

자동 배치만으로는 모든 운영 요구를 해결할 수 없다. 실제 운영에서는 다음 요청이 반복된다.

  • "특정 고객 데이터만 다시 처리해 주세요."
  • "어제 02:00~03:00 구간만 재실행해 주세요."
  • "배치 결과가 잘못돼서 바로 롤백이 필요합니다."

이때 임시 SQL로 처리하면 빠르지만, 재현성과 감사 추적을 잃는다. 수동 배치의 목표는 편의성보다 안전한 통제다.

2) 핵심 개념 정리

수동 배치는 최소한 다음 4가지를 갖춰야 한다.

  1. 실행 권한 분리: 조회 권한과 실행 권한 분리.
  2. 입력 파라미터 검증: 날짜 범위, 대상 ID 개수, 최대 건수 제한.
  3. 실행 이력/감사 로그: 누가, 언제, 어떤 파라미터로 실행했는지 저장.
  4. 롤백 경로: 결과 테이블 기준으로 역적용 가능.

트랜잭션 격리 수준은 수동 재처리에서 특히 중요하다. 운영자 재처리 중에는 대상 집합이 흔들리면 안 되므로 REPEATABLE READ 스냅샷 읽기를 사용하고, 쓰기 구간은 짧은 chunk 커밋으로 쪼개는 것이 일반적이다.

수동 배치 실행 흐름

Mermaid diagram rendering...

본문 이미지 - 경고등(리스크 관리)
본문 이미지 - 경고등(리스크 관리)

출처: Pexels - Warning Red Beacon

3) 코드 예시

예시 A: 수동 실행 API

@RestController
@RequestMapping("/admin/batches")
@RequiredArgsConstructor
public class ManualBatchController {

    private final ManualBatchService manualBatchService;

    @PostMapping("/reprocess-orders")
    public ResponseEntity<ManualBatchResponse> reprocess(@RequestBody ReprocessRequest request,
                                                         @AuthenticationPrincipal AdminUser adminUser) {
        ManualBatchResponse response = manualBatchService.startReprocess(request, adminUser.getEmail());
        return ResponseEntity.accepted().body(response);
    }
}

예시 B: 파라미터 검증 + 실행 등록

@Transactional
public ManualBatchResponse startReprocess(ReprocessRequest req, String requestedBy) {
    if (req.startDate().isAfter(req.endDate())) {
        throw new IllegalArgumentException("startDate must be <= endDate");
    }
    if (ChronoUnit.DAYS.between(req.startDate(), req.endDate()) > 7) {
        throw new IllegalArgumentException("max range is 7 days");
    }

    String requestHash = DigestUtils.sha256Hex(req.toString());
    Long executionId = manualBatchExecutionRepository.insertPending("reprocess-orders", requestedBy, requestHash, req);

    jobLauncher.run(reprocessJob, new JobParametersBuilder()
        .addLong("executionId", executionId)
        .addString("requestHash", requestHash)
        .toJobParameters());

    return new ManualBatchResponse(executionId, "PENDING");
}

예시 C: 감사 로그 테이블 SQL

CREATE TABLE manual_batch_execution (
    id BIGSERIAL PRIMARY KEY,
    job_name VARCHAR(100) NOT NULL,
    requested_by VARCHAR(120) NOT NULL,
    request_hash VARCHAR(64) NOT NULL,
    request_payload JSONB NOT NULL,
    status VARCHAR(20) NOT NULL,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    started_at TIMESTAMP NULL,
    ended_at TIMESTAMP NULL
);

CREATE UNIQUE INDEX uk_manual_batch_job_hash ON manual_batch_execution (job_name, request_hash);
CREATE INDEX idx_manual_batch_status_created ON manual_batch_execution (status, created_at DESC);

예시 D: 재처리 대상 Keyset 조회

SELECT id, order_id, error_code
FROM order_sync_failures
WHERE id > :last_id
  AND occurred_at BETWEEN :start_at AND :end_at
ORDER BY id ASC
LIMIT 1000;

4) 실제 장애/운영 시나리오

상황: 운영자가 같은 재처리 요청을 브라우저 새로고침으로 3번 전송했다. 시스템은 각 요청을 별도 배치로 받아 중복 처리했고, 고객에게 중복 알림이 발송됐다.

원인:

  • 요청 멱등 키가 없었다.
  • UI에서 "실행 중" 상태 잠금을 하지 않았다.
  • 수동 배치 결과가 외부 발송 시스템으로 즉시 전파되었다.

개선:

  1. request_hash 유니크 인덱스로 중복 실행 차단.
  2. UI에서 동일 파라미터 재전송 금지 및 진행 상태 Polling.
  3. 외부 발송은 outbox로 분리하고 승인 단계 추가.

5) 설계 체크리스트

  • 수동 배치 실행 권한이 RBAC로 분리되어 있는가?
  • 실행 파라미터 최대 범위/건수 제한이 있는가?
  • 요청 멱등 키(request_hash)로 중복 실행을 차단하는가?
  • 감사 로그에 요청자/파라미터/결과가 저장되는가?
  • 롤백 경로(역적용 SQL 또는 보정 배치)가 문서화되어 있는가?
  • 수동 실행 중 DB 락 경합을 줄이기 위한 chunk/격리 수준을 정했는가?

6) 요약

수동 배치는 "운영 편의 기능"이 아니라 통제 시스템이다. API 엔드포인트를 여는 것만으로는 불충분하며, 권한·멱등성·감사·롤백이 함께 설계되어야 실제 운영에서 안전하다.

7) 다음 편 예고

다음 편에서는 DB 대량 조회 전략을 다룬다. OFFSET/LIMIT의 한계, Keyset Pagination, ID Range Batch, Covering Index, SKIP LOCKED, 스냅샷 읽기 전략을 수치 중심으로 비교한다.

참고 링크

시리즈 네비게이션

댓글