From 7243d5f950ea560d162befa2c462e4b42d3a97e6 Mon Sep 17 00:00:00 2001 From: Claude-51124 Date: Tue, 10 Mar 2026 22:47:24 +0900 Subject: [PATCH] docs: add calendar holiday control research --- journey/README.md | 1 + ...지_행동제어_원인확정_리서치.md | 92 +++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 journey/research/260310_calendar_skill_자동휴일감지_행동제어_원인확정_리서치.md diff --git a/journey/README.md b/journey/README.md index c6d9a55..6017d17 100644 --- a/journey/README.md +++ b/journey/README.md @@ -23,6 +23,7 @@ - 네이버 이메일 분석 미전송과 실패 은닉 해결 – `troubleshooting/260309_9시_네이버이메일분석_미전송_실패은닉_해결.md` - 네이버 이메일 분석 실패 은닉 리서치 – `research/260309_9시_네이버이메일분석_미전송_실패은닉_리서치.md` +- Calendar Skill 자동휴일감지 원인확정 리서치 – `research/260310_calendar_skill_자동휴일감지_행동제어_원인확정_리서치.md` ### Intent / 리뷰 큐 diff --git a/journey/research/260310_calendar_skill_자동휴일감지_행동제어_원인확정_리서치.md b/journey/research/260310_calendar_skill_자동휴일감지_행동제어_원인확정_리서치.md new file mode 100644 index 0000000..0ecd217 --- /dev/null +++ b/journey/research/260310_calendar_skill_자동휴일감지_행동제어_원인확정_리서치.md @@ -0,0 +1,92 @@ +--- +tags: [calendar, scheduler, holiday, blackout, research] +--- + +# Calendar Skill 자동휴일감지 행동제어 원인확정 리서치 + +**날짜**: 2026-03-10 +**작성자**: Codex +**관련 계획**: [Calendar Skill 기반 자동 휴일 감지 행동 제어 시스템 구축](../plans/260214_calendar_skill_자동_휴일감지_행동제어_구축.md) +**상위 원칙**: [문서 작성 원칙](../../book/300_architecture/312_writing-principles.md), [Backend Coding Principles](../../book/300_architecture/311_backend_coding_principles.md) + +## 관련 문서 +- [Calendar Skill 기반 자동 휴일 감지 행동 제어 시스템 구축](../plans/260214_calendar_skill_자동_휴일감지_행동제어_구축.md) +- [Scheduler Endpoint E2E 테스트](../../../../robeing/rb8001/tests/test_scheduler_endpoint_e2e.py) + +## Facts + +### 1. `scheduled_jobs`에는 휴일/블랙아웃 정책 필드가 아직 없습니다. +- 실제 DB `scheduled_jobs` 컬럼은 `id`, `name`, `job_type`, `cron_expression`, `enabled`, `config`, `created_at`, `updated_at`만 존재합니다. +- 계획 문서에 적힌 `schedule_policy JSONB` 컬럼은 아직 추가되지 않았습니다. +- 현재 운영 데이터도 각 잡의 정책은 `cron_expression`과 `config`만 갖고 있습니다. + +### 2. `rb8001` 스케줄러 로더는 cron만 읽고 실행 가드를 두지 않습니다. +- [`rb8001/app/scheduler/db_loader.py`](../../../../robeing/rb8001/app/scheduler/db_loader.py) 는 `scheduled_jobs`에서 `cron_expression`과 일부 `config.channel_id`만 읽어 APScheduler에 등록합니다. +- `workday`, `holiday`, `blackout`, `schedule_policy` 같은 키를 해석하는 코드가 없습니다. +- 각 잡 실행 함수(`_run_*_with_logging`) 앞에 공통 휴일 판정 가드를 거는 코드도 현재 보이지 않습니다. + +### 3. `rb8001` 스케줄러 API도 정책 필드를 다루지 않습니다. +- [`rb8001/app/router/scheduler_endpoint.py`](../../../../robeing/rb8001/app/router/scheduler_endpoint.py) 의 생성/수정 API는 `enabled`, `cron_expression`, `config`만 처리합니다. +- 계획 문서에 적힌 `schedule_policy` 응답/수정 경로는 아직 구현되지 않았습니다. +- 관련 테스트 [`rb8001/tests/test_scheduler_repository.py`](../../../../robeing/rb8001/tests/test_scheduler_repository.py), [`rb8001/tests/test_scheduler_endpoint_e2e.py`](../../../../robeing/rb8001/tests/test_scheduler_endpoint_e2e.py) 도 휴일 정책 없이 기존 CRUD만 검증합니다. + +### 4. `robeing-monitor`는 `schedule_type`, `schedule_days`를 실제 저장하지 않습니다. +- [`robeing-monitor/app/api/monitor.py`](../../../../robeing/robeing-monitor/app/api/monitor.py) 의 `UserPreferencesUpdate`/`UserPreferencesResponse`에는 `schedule_type`, `schedule_days` 필드가 있지만, +- 코드 주석에 `schedule_type, schedule_days, include_* 컬럼들은 무시`라고 적혀 있고, 실제 DB UPDATE/INSERT에도 반영하지 않습니다. +- `UserPreferencesResponse`는 `schedule_type="workday"`, `schedule_days=[]`를 고정 기본값으로 돌려줍니다. +- 실제 DB `user_preference` 컬럼도 `user_id`, `news_keywords`, `email_filter`, `briefing_enabled`, `briefing_time`, `updated_at`, `created_at`, `id`만 있고 `schedule_type`, `schedule_days`는 없습니다. + +### 5. `skill-calendar`에는 계획된 휴일 판정 API가 없습니다. +- 계획 문서는 `GET /api/workday/check` 엔드포인트를 요구합니다. +- 실제 [`skill-calendar/routers/calendar.py`](../../../../robeing/skill-calendar/routers/calendar.py) 는 `POST /events`, `GET /events`, `DELETE /events/{event_id}`만 제공합니다. +- 실응답 기준으로도 `http://127.0.0.1:8512/api/workday/check?...` 는 `404 Not Found`입니다. + +### 6. 현재 구조로는 공휴일/연휴를 cron에서 직접 배제하지 못합니다. +- 운영 잡 다수는 실제로 `mon-fri` cron을 그대로 사용합니다. +- 예: `naverworks_daily 0 9 * * mon-fri`, `coldmail_daily 5 9 * * mon-fri`, `daily_headlines 10 9 * * mon-fri`. +- 따라서 연휴 중 평일이 포함되면 현재 구조에서는 기본적으로 실행 대상이 됩니다. + +## Interpretation + +### 1. 이 계획의 미구현 원인은 "스케줄 정책 저장층 부재"가 핵심입니다. +- 휴일 자동 제어를 하려면 최소한 `scheduled_jobs` 또는 별도 정책 테이블에 `workday/blackout` 정책이 저장돼야 합니다. +- 현재는 그 저장층 자체가 없어서, `rb8001`이 실행 직전에 참조할 정책 데이터가 없습니다. + +### 2. `robeing-monitor`의 UI/설정 필드는 아직 더미 상태입니다. +- 프런트/응답 모델에는 `schedule_type`, `schedule_days`가 보이지만, 실제 저장/조회는 되지 않습니다. +- 즉 운영자가 UI에서 값을 바꿔도 현재 구조에서는 실제 실행 정책이 바뀌지 않습니다. + +### 3. `skill-calendar`는 "일정 CRUD 스킬"까지만 구현되어 있고 "휴일 판정 서비스" 역할은 아직 시작되지 않았습니다. +- Google Calendar 연동과 이벤트 생성/조회/삭제는 있지만, +- 계획의 핵심인 `is_workday`, `is_blackout`, `should_run`, `reason` 판정 레이어는 없습니다. + +### 4. 현재 문제는 단일 버그보다 "계획의 각 레이어가 동시에 비어 있는 상태"에 가깝습니다. +- 저장층 없음 +- 판정 API 없음 +- 실행 가드 없음 +- 운영 설정 persistence 없음 +- 테스트도 휴일 정책 기준으로는 아직 없음 + +## Unresolved + +### 1. 휴일 기준 SSOT를 어디에 둘지 미확정입니다. +- Google Holiday Calendar를 단일 기준으로 둘지 +- 국가/타임존별 정책을 별도 테이블로 둘지 +- 수동 블랙아웃만 우선 둘지 결정이 필요합니다. + +### 2. 정책 저장 위치가 미확정입니다. +- `scheduled_jobs.schedule_policy` 컬럼으로 갈지 +- 별도 `scheduled_job_policies` 테이블로 분리할지 +- `user_preference`와 잡 정책을 어떤 경계로 나눌지 정리가 필요합니다. + +### 3. 실행 가드 위치가 미확정입니다. +- `db_loader` 등록 단계에서 래핑할지 +- 각 `_run_*_with_logging` 진입점 공통 헬퍼로 넣을지 +- APScheduler listener 레벨에서 막을지는 아직 결정되지 않았습니다. + +### 4. 테스트 기준 날짜/국가 범위가 미확정입니다. +- 계획 문서 예시는 `2026-02-16~2026-02-18` 연휴를 들고 있지만, +- 실제 운영 기준 국가/타임존(`KR`, `Asia/Seoul`)를 어디까지 고정할지 별도 확정이 필요합니다. + +## 한 줄 결론 +- 현재 `260214` 계획이 안 풀린 직접 원인은 "휴일 정책 저장, 휴일 판정 API, 실행 가드, 운영 설정 persistence" 네 레이어가 모두 아직 구현되지 않았기 때문입니다.