## 들어가며
배치 프로세스가 갑자기 100% 실패율을 기록한다면 어떻게 대응하시나요? 최근 논문 PDF 자동 다운로드 시스템에서 1,090건을 처리했지만 단 한 건도 성공하지 못한 장애를 경험했습니다. 이 글에서는 3가지 근본 원인을 찾아낸 과정과 해결 방법, 그리고 얻은 교훈을 공유합니다.
## 장애 현황
모니터링 알림을 통해 확인한 상황은 다음과 같았습니다:
- 처리 대상: 1,090건
- 성공: 0건 (100% 실패)
- PM2로 관리되는 Node.js 프로세스가 반복적으로 재시작
## 발견한 3가지 근본 원인
### 1. Node.js 런타임 환경 불일치 (긴급)
**문제**: 알림 전송 모듈에서 `fetch()` API를 사용했으나, 배포 환경의 Node.js 버전에서는 해당 API가 지원되지 않았습니다.
```javascript
// 문제가 된 코드 (Node.js v18 미만 환경)
await fetch(telegramApiUrl, {
method: 'POST',
body: JSON.stringify(message)
});
```
**영향**: 다운로드 배치가 완료될 때마다 알림 함수 호출 시 크래시가 발생하여 PM2가 프로세스를 21회 반복 재시작했습니다.
**해결책**: 환경에 독립적인 HTTP 클라이언트 라이브러리로 교체했습니다.
```javascript
// 수정된 코드
import axios from 'axios';
await axios.post(telegramApiUrl, message);
```
**교훈**: Node.js 18 이상에서만 `fetch()`가 글로벌 API로 제공됩니다. 하위 호환성이 필요하다면 axios, node-fetch 등의 라이브러리를 사용하세요.
### 2. 외부 리소스 버전 관리 문제
**문제**: 데이터베이스에 저장된 PDF URL에 버전 정보가 포함되어 있었으나(예: `arxiv.org/pdf/2601.06899v2`), 해당 논문이 최신 버전으로 업데이트되면서 이전 버전 URL이 404 오류를 반환했습니다.
**해결책**: 데이터베이스의 1,090개 URL에서 버전 접미사를 제거하여 항상 최신 버전을 가리키도록 수정했습니다.
```sql
-- 버전 접미사 제거 예시
UPDATE papers
SET pdf_url = REGEXP_REPLACE(pdf_url, 'v[0-9]+$', '')
WHERE pdf_url LIKE '%v[0-9]%';
```
**교훈**: 외부 리소스를 참조할 때는 버전 관리 정책을 확인하세요. 버전별 URL과 최신 버전 URL을 구분하여 사용 목적에 맞게 선택해야 합니다.
### 3. Cron 스크립트 간 의존성 충돌
**문제**: rsync로 파일을 동기화한 후 빈 디렉토리를 삭제하는 스크립트가 10분마다 실행되었습니다. 그런데 오래된 논문들은 특정 카테고리 디렉토리 구조(`quant-ph/`, `cs/` 등)를 필요로 했고, 이 디렉토리들이 계속 삭제되어 다운로드가 실패했습니다.
```bash
# 문제가 된 스크립트
rsync -avz --remove-source-files source/ dest/
find dest/ -type d -empty -delete # 필요한 디렉토리까지 삭제
```
**해결책**: 빈 디렉토리 삭제 로직을 제거하고, 필요한 25개 카테고리 디렉토리를 미리 생성했습니다.
```bash
# 수정된 스크립트
rsync -avz --remove-source-files source/ dest/
# find 삭제 명령 제거
# 필요한 디렉토리 사전 생성
mkdir -p dest/{cs,math,physics,quant-ph,...}
```
**교훈**: rsync 후 정리 작업은 신중하게 설계해야 합니다. `--remove-source-files` 옵션만으로도 동기화된 파일은 제거되므로, 디렉토리 구조를 함부로 삭제하면 다른 프로세스에 영향을 줄 수 있습니다.
## 해결 후 결과
3가지 이슈를 모두 수정한 후 프로세스를 재시작했고, 정상적으로 PDF 다운로드가 진행되었습니다.
## 핵심 교훈
1. **런타임 환경 검증**: 로컬과 배포 환경의 Node.js 버전 차이를 CI/CD 단계에서 사전 검증하세요.
2. **외부 리소스 변동성 대비**: 버전이 있는 URL은 최신 버전 정책을 확인하고, 필요시 URL 정규화 로직을 추가하세요.
3. **스크립트 간 의존성 관리**: Cron 스크립트가 다른 프로세스의 디렉토리 구조를 삭제하지 않도록 주의하세요. 특히 `find -empty -delete`는 예상치 못한 부작용을 일으킬 수 있습니다.
## 마치며
100% 실패율이라는 극단적인 상황은 오히려 명확한 원인이 존재함을 의미합니다. 로그 분석, 프로세스 재시작 패턴 파악, 스크립트 실행 순서 추적을 통해 복합적인 원인을 하나씩 제거할 수 있었습니다. 여러분의 배치 프로세스에서도 비슷한 이슈가 발생한다면 이 사례가 도움이 되길 바랍니다.
development
PDF 자동 다운로더 장애 분석: 100% 실패율을 해결한 3가지 원인과 교훈
Node.js 환경 이슈, 버전 관리 문제, 스크립트 간섭까지 - 실전 트러블슈팅 사례
SpacePlanning · SpacePlanning AI Team2026년 3월 4일조회 168