배치 작업의 실패 재시도(Retry) 횟수 초과 시 데이터 처리 누락

증상 확인: 배치 작업이 실패 후 재시도 없이 중단되는 문제
주기적으로 실행되는 배치 작업(예: 데이터 동기화, 리포트 생성, 대량 메일 발송)이 실패한 후, 설정한 재시도(Retry) 횟수를 초과하여 최종적으로 중단되는 현상입니다. 그래서 특정 시간대의 데이터가 처리되지 않고 누락되며, 시스템 간 데이터 불일치, 재무 보고서 오류, 고객 알림 누락 등 심각한 업무 장애로 이어집니다. 사용자는 “작업이 실패했다”는 로그만 확인할 뿐, 누락된 데이터를 파악하고 복구하는 데 상당한 시간을 소모하게 됩니다.
원인 분석: 재시도 메커니즘의 설계 및 운영상 결함
이 문제는 단순한 코드 버그가 아닌, 시스템 아키텍처와 운영 체계의 결합된 허점에서 발생합니다. 가장 흔한 원인은 다음과 같습니다.
- 일시적 장애와 영구적 장애의 구분 실패: 네트워크 타임아웃이나 데이터베이스 잠금(Lock) 같은 일시적 오류와, 참조 키 무결성 위반 같은 영구적 오류를 동일한 재시도 정책으로 처리합니다. 영구적 오류는 재시도해도 성공할 수 없어, 허비된 재시도 횟수만 소모한 후 작업이 중단됩니다.
- 부실한 오류 처리(Error Handling): 예외(Exception)를 포괄적으로 캐치(`catch (Exception e)`)한 후, 오류의 종류와 심각도를 구분하지 않고 무조건 재시도합니다. 이는 오류의 근본 원인을 로그에서 흐리게 만듭니다.
- 외부 의존성 관리 미비: 배치 작업이 의존하는 API, 파일 서버, 메시지 큐(Message Queue)가 장기간 불안정한 상태에서도 작업이 재시도만 반복하며, 결국 리소스(스레드, 메모리)를 고갈시킵니다.
- 재시도 간격(Backoff) 전략 부재: 실패 후 즉시 재시도를 반복하면, 과부하 상태의 대상 시스템(예: DB)을 악화시켜 성공 가능성을 더욱 낮춥니다. 지수 백오프(Exponential Backoff) 같은 지능적인 대기 전략이 필요합니다.

해결 방법 1: 기본적인 진단 및 즉시 조치
문제가 발생했을 때, 시스템을 정상 상태로 빠르게 복구하고 데이터 누락을 최소화하는 응급 조치입니다.
주의사항: 다음 조치를 수행하기 전에, 현재 실행 중인 동일한 배치 작업 인스턴스를 중지하십시오. 동시에 여러 작업이 실행되면 데이터 중복 또는 손상을 초래할 수 있습니다.
- 로그의 심층 분석: 배치 작업 로그에서 마지막 성공 지점과 첫 실패 지점을 정확히 찾습니다. 단순히 “ERROR” 로그가 아닌, 예외 스택 트레이스(Stack Trace) 전체를 확인하여 근본 오류 메시지를 파악하십시오.
- 수동 데이터 보정 실행: 누락된 데이터 구간(예: 2023년 10월 27일 02:00~04:00)을 대상으로, 배치 작업의 핵심 로직만을 포함한 임시 스크립트를 작성하여 수동 실행합니다. 이때 원본 데이터 소스의 무결성을 반드시 확인 필수.
- 의존 서비스 상태 점검: 배치 작업이 연결하는 모든 외부 서비스(데이터베이스, API 엔드포인트, FTP 서버)의 현재 상태와 응답 시간을 점검합니다. 네트워크 연결 테스트는 `telnet` 또는 `nc`(netcat) 명령어로 수행.
해결 방법 2: 재시도 메커니즘의 강화 및 설계 변경
근본적인 해결을 위해 애플리케이션 코드 수준에서 재시도 로직을 재설계합니다.
2.1. 지능형 재시도 정책 구현
모든 오류를 동일하게 재시도하지 말고, 오류 유형에 따라 분기 처리합니다.
- 재시도 가능 오류 정의: 네트워크 예외(`SocketTimeoutException`, `ConnectException`), 데이터베이스 교착 상태(Deadlock) 오류, HTTP 5xx 상태 코드 등은 재시도 대상으로 분류.
- 재시도 불가 오류 정의: 비즈니스 로직 오류(잘못된 입력값), 데이터 무결성 오류(`ConstraintViolationException`), 인증 실패 오류 등은 즉시 실패 처리하고 관리자 알림 발송.
- 지수 백오프 전략 도입: 재시도 간격을 점진적으로 증가시킵니다. 예: 첫 재시도는 2초 후, 두 번째는 4초 후, 세 번째는 8초 후. 이는 라이브러리(Spring Retry, Resilience4j)를 활용해 쉽게 구현 가능.
2.2. 패턴 설계: 회로 차단기(Circuit Breaker) 패턴 적용
외부 서비스 연속 실패 시, 일정 시간 동안 요청을 차단하여 시스템 자원 소모를 방지합니다.
- 상태 모니터링: 특정 API 호출 실패율이 임계치(예: 50%)를 초과하면, 회로 차단기를 “열림(OPEN)” 상태로 전환.
- 실패 우회(Fail Fast): “열림” 상태에서는 모든 요청을 즉시 실패 처리(예외 발생)하여 불필요한 대기 시간과 리소스 낭비를 방지.
- 반열림(HALF-OPEN) 상태 시험: 설정한 시간 이후, 단일 요청을 보내 성공하면 회로를 “닫힘(CLOSED)” 상태로 복구, 실패하면 다시 “열림” 상태 유지.
해결 방법 3: 운영 체계 구축 및 모니터링 강화
코드 변경 외에, 운영 인프라를 개선하여 문제를 사전에 예방하고 신속히 감지합니다.
- 데드 레터 큐(Dead Letter Queue, DLQ) 도입: 모든 재시도를 마친 후에도 실패한 작업 메시지나 작업 정보를 별도의 저장소(DLQ)에 보관합니다, 이는 데이터 누락을 방지하는 최후의 안전장치 역할을 하며, 운영자가 dlq를 주기적으로 점검하여 수동 복구할 수 있는 기회를 제공합니다.
- 종합 모니터링 대시보드 구축: 배치 작업의 성공/실패 횟수, 평균 실행 시간, 재시도 횟수 추이를 실시간으로 모니터링합니다. Grafana 대시보드에 해당 메트릭을 시각화하고, 실패율이 증가할 때 Slack, 이메일, SMS로 즉시 알림 발송.
- 작업 스케줄러의 고가용성 보장: 단일 서버에서 크론(Cron) 작업을 실행하는 방식은 서버 장애 시 모든 배치 작업이 중단됩니다. Kubernetes CronJob, Apache Airflow, Quartz 클러스터링 같은 분산되고 장애 조치(Failover)가 가능한 스케줄러로 이전을 검토.

주의사항 및 장기적 예방책
위 해결책을 적용할 때 반드시 고려해야 할 운영상의 세부 사항입니다.
- 멱등성(Idempotency) 보장: 재시도로 인해 동일한 작업이 여러 번 실행될 수 있습니다. 작업 로직은 여러 번 실행되어도 동일한 결과를 내는 멱등성을 갖춰야 합니다. 데이터베이스 업데이트 시 조건 체크 또는 유니크 키 활용 필수.
- 로그 추적성 강화: 각 배치 작업 실행마다 고유한 추적 ID(Correlation ID)를 부여하여, 분산 시스템 환경에서도 하나의 작업 흐름을 시작부터 끝까지 로그로 추적 가능해야 함.
- 정기적인 장애 대응 훈련: 배치 작업 실패는 언제든 발생할 수 있습니다. 정기적으로 주요 배치 작업을 의도적으로 실패시키고(카오스 엔지니어링), 정의된 복구 절차(수동 스크립트 실행, DLQ 처리)가 효과적으로 작동하는지 훈련과 검증을 반복 수행.
전문가 팁: 재시도 정책의 중앙 관리
각 애플리케이션 코드마다 재시도 로직을 산발적으로 구현하면 관리가 어렵습니다. 재시도 정책(최대 횟수, 백오프 전략, 예외 분류)을 설정 파일(예: YAML)이나 환경 변수로 외부화하십시오. 더 뿐만 아니라, 서비스 메시(Service Mesh)나 API 게이트웨이 수준에서 글로벌 재시도 정책을 적용하면, 애플리케이션 코드 변경 없이도 일관된 정책을 모든 서비스에 적용할 수 있습니다. 이는 마이크로서비스 환경에서 특히 효과적인 전략입니다.



