--- tags: [infra, robeing, rb8001, skill-email, naverworks, troubleshooting] --- # 9시 네이버 이메일 분석 미전송과 실패 은닉 이슈 ## 상위 원칙 - [Infra Project Identity](../../00_Philosophy/00_IDENTITY/Infra_Project_Identity.md) - [Core Infrastructure Principles](../../00_Philosophy/01_PRINCIPLES/Core_Infrastructure_Principles.md) - [Operational Guardrails](../../00_Philosophy/02_GUARDRAILS/Operational_Guardrails.md) - 공통 작성 원칙: [0_VALUE Writing Principles](https://github.com/happybell80/0_VALUE/blob/main/02_Governance/writing-principles.md) ## 관련 문서 - [Infra Journey](../README.md) - [9시 네이버 이메일 분석 미전송 실패 은닉 리서치](../research/260309_9시_네이버이메일분석_미전송_실패은닉_리서치.md) - [24서버 실서비스 운영전환 리서치](../research/260309_24서버_실서비스운영전환_리서치.md) - [51123 구 IP 하드코딩 실행 경로와 런타임 SSOT 불일치 리서치](../research/260309_51123_구IP하드코딩_실행경로_SSOT불일치_리서치.md) ## 문제 정의 - 2026-03-09 09:00 KST에 로빙의 NAVER WORKS(네이버웍스) 이메일 분석 브리핑이 전송되지 않았다. - 그러나 스케줄러 로그에는 작업이 `completed successfully`로 남아 있어, 실제 실패가 성공처럼 보였다. - 이번 이슈의 본질은 "스케줄 미실행"이 아니라 `rb8001 -> skill-email` 호출 실패가 `메일 없음`으로 은닉되는 실행 경로 문제다. ## 확인된 사실 ### 1. 9시 스케줄 자체는 정상 실행됐다 - `rb8001` 로그에는 `2026-03-09 09:00:00`에 `naverworks_daily`가 실행된 기록이 있다. - 같은 로그에 `Running job: NAVER WORKS Daily Briefing`와 `Starting NaverWorks briefing process for info@company-x.partners`가 남아 있다. - 따라서 이번 사건은 스케줄 등록 누락이나 프로세스 다운으로 보이지 않는다. ### 2. 실제 실패 지점은 `skill-email /messages` 호출 타임아웃이다 - `rb8001` 로그에는 `2026-03-09 09:00:30` 시점 `httpx.ReadTimeout` 예외가 남아 있다. - 실패가 발생한 호출은 `http://localhost:8501/messages`이며, 파라미터는 `provider=naverworks`, `searchDateType=read`, `startSearchDate=2026-03-06T09:00:00+09:00`, `endSearchDate=2026-03-09T09:00:00+09:00`, `limit=100`이다. - 따라서 오늘 미전송의 직접 원인은 메일 조회 단계의 읽기 타임아웃이다. ### 3. 실패 후 작업은 성공처럼 기록됐다 - `rb8001/app/services/skills/naverworks_briefing.py`는 `_fetch_recent_emails()`에서 예외나 비정상 응답을 `return []`로 처리한다. - 같은 파일 `process_briefing()`는 빈 리스트를 `No emails in the last 24 hours`로 처리하고 그대로 종료한다. - 그 결과 스케줄러 로그에는 `NAVER WORKS Daily Briefing completed successfully`가 남는다. ### 4. 같은 요청은 현재 시점에는 정상 응답한다 - 같은 파라미터로 `http://127.0.0.1:8501/messages`를 다시 호출했을 때 `HTTP 200`, 총 응답시간 약 `0.61초`로 정상 응답했다. - 따라서 현재는 상시 장애 상태가 아니라, 09:00 시점의 단발성 타임아웃 또는 외부 의존성 지연이 있었던 것으로 좁혀진다. ### 5. 당시 NAVER WORKS 토큰은 만료 상태였다 - `main_db.naverworks_token` 조회 결과, 해당 계정 토큰의 `expires_at`는 당시 `2026-03-05`로 남아 있었다. - 이후 수동 토큰 갱신 호출은 `HTTP 200`, 약 `0.23초`에 성공했고, 현재 `expires_at`는 `2026-03-09 13:59:37`로 갱신됐다. - 즉 9시 시점에는 토큰 만료 상태가 실제로 존재했다. ## 왜 문제인가 - 실패가 성공처럼 기록되면 운영자는 "메일이 없었다"고 오인하고 실제 장애를 놓치게 된다. - 상위 SSOT의 `Truth First`, `광범위 예외 폴백 금지`, `근본 원인 직접 수정 우선` 원칙과 충돌한다. - 특히 외부 의존성 지연, 토큰 만료, 내부 서비스 타임아웃이 다시 발생해도 같은 방식으로 재발할 수 있다. ## 직접 원인 - `rb8001`의 NAVER WORKS 브리핑 로직이 `skill-email` 조회 실패를 원인별 실패로 올리지 않고 빈 메일 목록으로 삼킨다. ## 근본 원인 - 브리핑 로직이 `메일이 실제로 0건인 경우`와 `메일 조회 자체가 실패한 경우`를 같은 값 `[]`로 표현하는 구조다. ## 우회가 아닌 해결 기준 - `timeout`, `토큰 갱신 실패`, `비200 응답`, `응답 구조 이상`을 `메일 0건`과 분리한다. - 스케줄러 실행 결과는 실패면 실패로 기록되도록 올린다. - 로그에는 `No emails`가 아니라 실제 실패 원인과 단계가 남아야 한다. ## 완료 판단 기준 - `rb8001` 9시 브리핑에서 `skill-email` 조회 실패 시 스케줄러가 실패로 기록된다. - 실제 메일 0건일 때만 `No emails` 로그가 남는다. - 토큰 만료, 외부 API 지연, 내부 타임아웃이 발생해도 각각의 원인이 로그에서 구분된다.