# 9시 네이버 이메일 분석 미전송 실패 은닉 리서치

tags: [naverworks, email, briefing, timeout, failure-observability]

**날짜**: 2026-03-09  
**작성자**: Codex  
**상위 원칙**: [문서 작성 원칙](../../book/300_architecture/312_writing-principles.md), [Backend Coding Principles](../../book/300_architecture/311_backend_coding_principles.md)

## 관련 문서
- [9시 네이버 이메일 분석 미전송과 실패 은닉 해결](../troubleshooting/260309_9시_네이버이메일분석_미전송_실패은닉_해결.md)
- [NAVER WORKS Refresh unauthorized_client 장애 및 재인증 복구 기록](../troubleshooting/260227_naverworks_refresh_unauthorized_client_reauth_recovery.md)

---

## 조사 대상
- 왜 `2026-03-09 09:00` 네이버 이메일 분석 브리핑이 전송되지 않았는가
- 왜 실패가 운영 로그에서는 성공처럼 보였는가
- 이번 문제를 우회 없이 닫으려면 무엇을 직접 수정해야 하는가

## 확인된 사실
1. `rb8001` 스케줄 로그 기준 `2026-03-09 09:00:00` KST에 `naverworks_daily` 작업은 실행됐다.
2. 같은 요청 흐름에서 `2026-03-09 09:00:30` KST에 `httpx.ReadTimeout`이 발생했다.
3. `rb8001/app/services/skills/naverworks_briefing.py`는 메일 조회 실패를 빈 목록처럼 다뤄 `No emails`로 끝낼 수 있는 구조였다.
4. `rb8001/app/scheduler/jobs/naverworks_briefing.py`는 작업 예외를 다시 올리지 않아 APScheduler 성공 로그와 실제 실패가 어긋날 수 있는 구조였다.
5. `skill-email/services/naverworks_provider.py`는 당시 단계별 elapsed 로그가 부족해, `30초 블로킹`의 최종 지점이 `auth-server refresh`인지 NAVER WORKS 외부 API인지 즉시 분리하기 어려웠다.
6. `skill-email`, `auth-server` 컨테이너는 UTC 기준으로 동작하고, `rb8001`은 KST 기준 스케줄을 사용한다.
7. `main_db.naverworks_token.expires_at`는 `timestamp without time zone`이라 비교 기준이 코드에서 명시되지 않으면 해석 흔들림이 생긴다.

## 해석
- 직접 원인은 `rb8001 -> skill-email /messages` 경로의 read timeout이다.
- 구조 원인은 "조회 실패"와 "조회 결과 0건"을 같은 값으로 취급한 설계다.
- 관측 실패 원인은 스케줄러 래퍼가 예외를 다시 올리지 않은 점이다.
- 시간대 혼재와 naive timestamp는 재발 위험을 높이는 구조 요인이지만, 이번 09:00 미전송의 직접 원인 자체를 대체하지는 않는다.

## 이번 문제를 닫는 수정 기준
1. 메일 조회 실패는 빈 목록이 아니라 명시적 실패 타입으로 분리한다.
2. 스케줄러는 실패를 다시 올려 운영 로그와 실제 결과를 일치시킨다.
3. `skill-email`에는 단계별 추적 로그를 추가해 다음 장애에서 block point를 즉시 좁힐 수 있게 한다.
4. 토큰 만료 판단은 UTC 기준을 코드에서 명시해 DB 값과 비교 축을 고정한다.

## 미확정
- `2026-03-09 09:00`의 실제 30초 block point가 `auth-server refresh`였는지, 외부 NAVER WORKS API였는지는 당시 세부 로그 부재로 100% 확정하지 못했다.
- 이 항목은 이번 수정 후 재발 시 새 추적 로그로 닫는다.

## 결론
- 이 문제의 핵심은 "9시 작업이 안 돌았다"가 아니라 "9시 작업 실패가 성공처럼 은닉됐다"는 점이다.
- 따라서 우회 없는 해법은 재시도 강화가 아니라 실패 타입 분리, 예외 재전파, 단계별 추적 로그 추가다.
