서울 시간 · 개인 일정 오늘의 시간표

Bluehair 시간표 운영·유지보수·디버깅·확장 인덱스#

이 문서는 timetable-cloudflare 저장소의 현재 구현을 기준으로 작성한 운영자·개발자용 색인입니다. 기능을 수정하거나 장애를 조사할 때는 먼저 이 문서를 보고, 이어서 연결된 파일을 여십시오. 배포·시크릿 변경·D1 데이터 변경은 되돌리기 어렵거나 서비스에 영향을 줄 수 있으므로, 로컬 검증과 백업 확인 뒤에만 수행합니다.

기준: 현재 단일 워크스페이스(default), 시간대 Asia/Seoul, 주 서비스 이름 timetable.

0. 가장 빠른 길#

목적 먼저 볼 위치 다음 행동
시간표가 갱신되지 않음 src/index.ts의 revision/SyncHub, public/app.js /api/changes/ws를 각각 확인한다.
웹 화면/디자인 수정 public/index.html, public/styles.css, public/app.js pnpm test 후 브라우저에서 수동 확인한다.
API·검증 규칙 수정 src/index.ts, test/worker.test.ts 입력 검증, D1 변경, revision 증가, 알림을 함께 검토한다.
데이터 구조 변경 migrations/, src/index.ts, seed.sql 새 migration을 추가하고 기존 migration은 수정하지 않는다.
ChatGPT 도구 문제 integrations/chatgpt-mcp/ Service Binding과 양쪽 APP_TOKEN을 확인한다.
Windows 위젯 문제 windows/README.md, windows/src/TodayWidget/, windows/scripts/ 로그와 Smoke Test를 먼저 확인한다.
배포/도메인 문제 wrangler.jsonc, MCP의 wrangler.jsonc dry-run, 도메인·바인딩·시크릿 순서로 확인한다.
CI 실패 .github/workflows/ci.yml 실패한 job과 같은 명령을 로컬에서 재현한다.

1. 시스템 구성요소 지도#

브라우저 PWA ───────────────┐
Windows TodayWidget(WebView2) ─┼─▶ 주 Timetable Worker ─▶ D1(권위 데이터)
                              │          ├─▶ public/(정적 화면)
ChatGPT Web ─▶ OAuth MCP Worker ┘          └─▶ SyncHub ─▶ WebSocket 무효화 신호
                    └─▶ OAUTH_KV

MCP Worker ── TIMETABLE_API Service Binding + 서버 측 Bearer ──▶ 주 Worker

권한과 데이터의 단일 원천#

  • D1이 일정·일회성 할 일·체크리스트·완료 기록·현재 revision의 권위 있는 저장소다.
  • 주 Worker만 D1에 연결되어 입력 검증, 인증, 변경 commit, revision 증가를 수행한다.
  • **Durable Object SyncHub**은 상태를 저장하지 않는다. 연결된 클라이언트에 { type: "invalidate", revision }를 보내는 fan-out 용도다.
  • MCP Worker는 D1을 직접 읽거나 쓰지 않는다. TIMETABLE_API Service Binding을 통해 주 Worker의 같은 API를 호출한다.
  • SSE는 구현되어 있지 않다. 실시간 전달 방식은 인증된 WebSocket 하나다.

2. 디렉터리와 핵심 파일 인덱스#

경로 책임 변경 시 함께 확인할 곳
src/index.ts 주 Worker: 라우팅, 인증, API, D1 CRUD, revision, SyncHub, 보안 헤더 test/worker.test.ts, worker-configuration.d.ts, wrangler.jsonc
migrations/0001_initial.sql 초기 D1 스키마·인덱스·sync_meta 새 스키마 변경은 새 번호 migration으로 추가
seed.sql 초기 반복 일정과 체크리스트 운영 데이터를 덮어쓰지 않는지 검토 (INSERT OR IGNORE)
public/index.html PWA 마크업 public/styles.css, public/app.js
public/styles.css 웹·위젯 화면 스타일 브라우저와 Windows WebView2에서 확인
public/app.js 인증 세션, API 호출, revision 동기화, WebSocket 재접속, CRUD UI public/timetable-core.js, Worker API
public/timetable-core.js 서울 시간 기준 현재/다음 일정 및 타임라인 계산 test/web-core.test.js
public/sw.js PWA 앱 셸 캐시 API와 /ws는 캐시하지 않는 정책을 유지
docs/USER_GUIDE_KO.md 비개발자용 순차 사용 가이드의 원본 pnpm docs:buildpublic/guide.html 갱신
docs/MAINTENANCE_INDEX_KO.md 운영·디버깅·확장 인덱스의 원본 pnpm docs:buildpublic/maintenance.html 갱신
scripts/build-docs.mjs 두 Markdown 원본을 배포용 HTML로 결정적으로 변환 public/docs.css, pnpm docs:check
public/docs.css 가이드 페이지의 반응형·인쇄 스타일 데스크톱과 좁은 위젯 폭에서 수동 확인
wrangler.jsonc 주 Worker, custom domain, D1, DO, 정적 assets, 관측 설정 실제 Cloudflare 계정 바인딩·도메인
test/worker.test.ts 인증, WebSocket, post-commit 알림, revision API 테스트 src/index.ts
test/web-core.test.js 서울 시간/정렬/동기화 burst 제어 테스트 public/timetable-core.js
integrations/chatgpt-mcp/src/index.ts OAuth 2.1, MCP 도구, 주 Worker 프록시 MCP 테스트와 별도 Wrangler 설정
integrations/chatgpt-mcp/wrangler.jsonc MCP custom domain, KV, cron, Service Binding, secret 주 Worker 서비스명과 시크릿
integrations/chatgpt-mcp/test/worker.test.ts 서비스 바인딩, 오류 은닉, OAuth 보안 테스트 MCP Worker
windows/src/TodayWidget/ WinUI 3 + WebView2 위젯, 설정, 시작 프로그램, 로그 windows/scripts/, windows/README.md
windows/scripts/ 빌드, publish, 설치, 시작 프로그램, 제거, Smoke Test Windows에서만 실행
.github/workflows/ci.yml PR/push CI; 검사만 수행하며 배포하지 않음 root/MCP/Windows 명령
docs/ARCHITECTURE.md 설계 경계 데이터 흐름을 크게 바꿀 때 갱신
docs/HANDOFF.md 인계와 검증 명령 운영 상태가 달라질 때 갱신

3. 데이터·API·실시간 흐름#

D1 스키마#

migrations/0001_initial.sql에는 다음 테이블이 있다.

테이블 용도 핵심 규칙
sync_meta workspace별 최신 revision 현재는 workspace_id = 'default' 한 행
schedule_items 반복 일정 weekday 0~6, HH:MM, enabled, sort_order
one_off_tasks 날짜별 추가 할 일 task_date, 선택 시간, completed_at
checklist_items 재사용 체크리스트 정의 is_active, sort_order
checklist_completions 날짜별 완료 상태 (item_id, completion_date) 복합 PK

모든 정상적인 변경은 D1 batch에서 데이터 조작 뒤 sync_meta.revision을 1 증가시킨다. commit이 끝난 후에만 SyncHub.invalidate(revision)을 호출한다. 알림 전송 실패는 이미 성공한 데이터 commit을 되돌리지 않는다. 따라서 클라이언트는 WebSocket 메시지를 정답 데이터로 취급하지 않고 스냅샷을 다시 읽어야 한다.

API 요약#

모든 /api/*/ws는 아래 둘 중 하나로 인증해야 한다.

  • 브라우저: POST /api/auth/session에서 토큰을 HttpOnly/SameSite=Strict 세션 쿠키로 교환
  • 자동화·네이티브·Service Binding: Authorization: Bearer <APP_TOKEN>

쿠키를 사용한 쓰기 요청은 현재 Origin과 같은 Origin 헤더가 반드시 필요하다. API JSON 본문은 Content-Type: application/json이며 16 KiB를 넘을 수 없다.

목적 경로와 메서드 동기화 결과
세션 생성/삭제/확인 POST, DELETE /api/auth/session; GET /api/auth/status 세션 쿠키 설정/삭제
범위 스냅샷 GET /api/snapshot?from=YYYY-MM-DD&to=YYYY-MM-DD revision 포함 전체 범위 반환
변경 확인 GET /api/changes?since=N&from=...&to=... 동일 revision이면 { changed: false }, 다르면 스냅샷
반복 일정 POST /api/schedule; PATCH, DELETE /api/schedule/:id revision 증가, WebSocket invalidation
일회성 할 일 POST /api/tasks; PATCH, DELETE /api/tasks/:id revision 증가, WebSocket invalidation
체크리스트 정의 POST /api/checklist; PATCH /api/checklist/:id revision 증가, WebSocket invalidation
체크 완료 PUT /api/checklist/:id/completions/:YYYY-MM-DD { completed: boolean }, revision 증가
실시간 무효화 GET /ws + WebSocket Upgrade { type: "invalidate", revision } push

범위는 서울 날짜 기준이고 기본값은 오늘이며, 최장 31일이다. Worker가 허용하는 category는 english, exam, content, review, rest다.

클라이언트 동기화 순서#

  1. public/app.js가 인증 상태를 확인한다.
  2. 지정한 주간 범위의 /api/snapshot을 받아 localStoragetimetable.snapshot.v2에 마지막 정상 데이터를 보관한다.
  3. 인증된 온라인 클라이언트가 /ws에 연결한다.
  4. 어느 클라이언트/ChatGPT가 변경을 commit하면 revision이 증가하고 SyncHub가 invalidate를 push한다.
  5. 메시지의 revision이 로컬 값보다 새로우면 클라이언트가 /api/changes 또는 snapshot으로 다시 읽고 렌더링한다.
  6. WebSocket이 끊기면 재접속한다. 오프라인일 때는 마지막 정상 스냅샷을 표시한다.

public/sw.js는 앱 셸의 GET 요청만 캐시하며 /api/*/ws는 명시적으로 제외한다. API 응답도 Cache-Control: no-store다.

4. 설정·시크릿·Cloudflare·도메인#

주 Worker 설정 (wrangler.jsonc)#

항목 현재 선언 운영 주의점
Worker 서비스 timetable MCP Service Binding의 대상 이름과 일치해야 한다.
주 custom domain today.bluehair.blue 도메인/DNS 변경은 Cloudflare 계정에서 별도 확인이 필요하다.
정적 자산 public/ASSETS, run_worker_first: true Worker가 API와 정적 앱을 함께 제공한다.
D1 binding DB, 이름 timetable database_id는 실제 운영 DB를 가리킨다. 함부로 바꾸지 않는다.
Durable Object SYNC_HUBSyncHub DO migration tag는 v1이다.
관측 observability.enabled: true, sampling 1 로그 보관·알림·외부 전송은 별도 설정이 없다.
필수 secret APP_TOKEN 설정 파일·소스·클라이언트에 넣지 않는다.

MCP Worker 설정 (integrations/chatgpt-mcp/wrangler.jsonc)#

항목 현재 선언 운영 주의점
Worker 서비스 timetable-chatgpt-mcp 주 Worker와 별도 배포 단위다.
custom domain mcp.bluehair.blue /mcp, OAuth discovery와 authorize/token 경로를 같은 origin으로 제공한다.
Service Binding TIMETABLE_APItimetable D1 직접 바인딩을 추가하지 않는다.
KV OAUTH_KV OAuth grant/token 자료용이다.
cron 매일 17 3 * * * 만료 OAuth KV 레코드 정리만 수행하며 시간표 데이터는 바꾸지 않는다.
필수 secret APP_TOKEN 주 Worker와 동일한 운영 값이어야 한다.

시크릿 규칙#

  • .dev.varsintegrations/chatgpt-mcp/.dev.vars는 Git ignore 대상이다. 예제 파일만 커밋한다.
  • 운영 APP_TOKEN은 두 Worker의 Cloudflare secret에 함께 설정한다. 교체 시 새 값이 양쪽에 적용되기 전까지 MCP 호출이 실패할 수 있다.
  • 입력 토큰과 HMAC 세션 서명의 원천이 같은 APP_TOKEN이므로, 유출 또는 분실이 의심되면 즉시 두 Worker에서 교체하고 기존 브라우저 세션이 무효화됨을 전제한다.
  • CI의 local-development-token-not-for-production은 테스트용이며 운영 시크릿이 아니다.

5. 로컬 개발과 검증#

필요한 도구#

  • root 및 MCP: Node.js 22, pnpm 10, 프로젝트의 lockfile에 맞는 의존성
  • Windows: .NET SDK 8 (windows/global.json은 8.0.423), Windows SDK, WebView2 Runtime
  • Cloudflare 실제 배포/원격 D1 조작: 해당 계정에 인증된 Wrangler

다음으로 셸의 도구 상태를 먼저 확인한다.

node --version
pnpm --version
pnpm exec wrangler --version

node가 인식되지 않거나 22 계열이 아니면, 먼저 Node.js 22를 설치/활성화한 뒤 새 PowerShell 창에서 재시도한다.

주 Worker/PWA 안전 검증#

저장소 root에서 실행한다.

pnpm install --frozen-lockfile
pnpm docs:check
pnpm types
pnpm types:check
pnpm typecheck
pnpm test
pnpm check

pnpm docs:check는 Markdown 원본과 배포용 HTML이 같은지 확인한다. 실패하면 pnpm docs:build로 HTML을 다시 생성한다. pnpm checkpnpm deploy:dry-runwrangler deploy --dry-run이며 실제 배포를 하지 않는다. 생성된 타입 파일의 변경이 의도된 것인지 확인한 뒤 커밋한다.

로컬 데이터로 UI를 실행하는 순서는 다음과 같다.

Copy-Item .dev.vars.example .dev.vars
# .dev.vars의 APP_TOKEN을 개발용 긴 난수로 교체한다.
pnpm db:migrate:local
pnpm db:seed:local
pnpm dev

브라우저에서 http://localhost:8787을 열고, 개발용 토큰으로 로그인한다. .wrangler/의 로컬 D1 상태는 운영 DB가 아니다.

ChatGPT MCP 패키지 검증#

다음 명령은 MCP 하위 디렉터리에서 실행한다.

Set-Location integrations/chatgpt-mcp
pnpm install --frozen-lockfile --ignore-workspace
pnpm types
pnpm typecheck
pnpm test
pnpm check

로컬 OAuth/MCP 실험은 Copy-Item .dev.vars.example .dev.vars 뒤에 실행한다. Service Binding을 포함한 end-to-end 로컬 검증은 두 Worker를 함께 Wrangler 개발 런타임으로 실행해야 한다. 이 저장소에는 이를 한 번에 띄우는 compose/script는 없다.

Windows 위젯 검증#

Set-Location windows
.\scripts\Build.ps1 -Configuration Release
.\scripts\Publish.ps1
.\scripts\Smoke-Test.ps1

Build.ps1은 CI용 빌드이고, Publish.ps1은 self-contained x64 산출물과 ZIP을 만든다. 설치 전 Smoke Test를 통과시킨다.

CI가 보장하는 범위#

.github/workflows/ci.yml은 pull request, push, 수동 실행에서 다음 세 job을 수행한다.

Job 보장하는 것 보장하지 않는 것
Worker and web checks root install, 문서 생성물 일치, 타입 생성/검사, Worker·웹 테스트, dry-run 실제 배포, 운영 D1, 실 브라우저 UI
ChatGPT MCP checks MCP install, 타입/테스트, dry-run 실제 OAuth 승인, 실제 ChatGPT 연결, 운영 KV
Windows x64 build Release/x64 restore/build publish, 설치, WebView2 실기기 smoke test

CI에는 배포 권한이나 운영 secret을 주지 않는다.

6. 배포·롤백·백업·복구#

주 Worker 배포 전 점검#

  1. pull request CI가 모두 녹색인지 확인한다.

  2. wrangler.jsonc의 서비스명, today.bluehair.blue, D1 binding/ID, DO binding을 확인한다.

  3. 운영 APP_TOKEN이 이미 주 Worker에 설정됐는지 확인한다. 새 환경이면 대화형으로 설정한다.

    pnpm exec wrangler secret put APP_TOKEN
    
  4. 새 migration이 있으면 배포 전에 원격 D1에 적용할 대상과 백업 상태를 재확인한다.

    pnpm exec wrangler d1 migrations apply timetable --remote
    
  5. 사람이 dry-run 결과를 검토한 뒤 배포한다.

    pnpm deploy:dry-run
    pnpm exec wrangler deploy
    

seed.sqlINSERT OR IGNORE를 사용하지만, 운영 seed 실행은 새 기본 행을 추가할 수 있다. 운영 데이터에 필요한지 확인하지 않은 상태에서 재실행하지 않는다.

MCP Worker 배포 순서#

  1. TIMETABLE_API의 서비스 대상이 timetable인지 확인한다.

  2. MCP Worker에도 같은 운영 APP_TOKEN을 설정한다.

    Set-Location integrations/chatgpt-mcp
    pnpm exec wrangler secret put APP_TOKEN
    pnpm check
    pnpm exec wrangler deploy
    
  3. mcp.bluehair.blue/mcp의 OAuth discovery와 인증 전 401을 확인한다. 실제 시간표를 변경하는 smoke test는 피한다.

롤백 원칙#

  • 이 저장소에는 자동 배포나 원클릭 롤백 스크립트가 없다.

  • 코드 장애는 우선 현재 배포 이력을 읽기 전용으로 확인한다.

    pnpm exec wrangler deployments list
    pnpm exec wrangler deployments status
    
  • 즉시 이전 Worker 버전으로 되돌려야 한다면 정상 version ID를 사람이 확인한 뒤 다음 명령을 실행할 수 있다. 이 명령은 운영 배포를 바꾸므로 변경 사유와 version ID를 기록한다.

    pnpm exec wrangler rollback <NORMAL_VERSION_ID> --message "장애 대응: <REASON>"
    
  • 또는 마지막 정상 Git commit을 새 브랜치에서 복원한 뒤 같은 검증과 wrangler deploy를 다시 실행하면 소스와 배포 상태를 일치시키기 쉽다.

  • D1 migration은 일반적으로 자동 역방향 실행되지 않는다. 잘못된 migration을 이미 원격 적용했다면, 먼저 영향 범위와 최신 백업을 확인하고 별도 forward-fix migration 또는 복구 계획을 작성한다. 기존 migration 파일을 수정해 과거 상태를 바꾸지 않는다.

  • custom domain/DNS 변경 롤백은 Worker 코드 롤백과 별개다. Cloudflare DNS/route 상태를 따로 확인한다.

백업·복구 현황과 필수 운영 절차#

  • 저장소에는 D1 자동 백업, export 스케줄러, 복구 스크립트가 구현되어 있지 않다.
  • 현재 고정된 Wrangler에서 원격 SQL export 문법은 pnpm exec wrangler d1 export timetable --remote --output <BACKUP.sql>이다. 권한·보존 위치를 사전 검증하고, 실행 직전 pnpm exec wrangler d1 export --help로 설치된 CLI 문법을 다시 확인한다.
  • 최소 절차는 “변경 전 export를 암호화된 접근 제한 저장소에 보관 → 파일 해시·DB명·시각 기록 → 복구는 격리된 대상에서 먼저 검증 → 원격 복구 승인”이다. Git 저장소나 공개 artifact에 SQL export/토큰을 넣지 않는다.
  • 시점 복구가 필요하면 먼저 pnpm exec wrangler d1 time-travel info timetable --timestamp <RFC3339>로 bookmark를 조회한다. time-travel restore는 운영 DB를 덮어쓰므로 최신 export와 별도 승인을 확보한 뒤에만 사용한다.
  • 복구 시 revision과 WebSocket 상태도 고려한다. D1을 이전 시점으로 복구하면 연결된 클라이언트의 로컬 snapshot보다 revision이 낮아질 수 있으므로, 사용자 세션을 새로고침/재인증하고 동기화 동작을 수동 점검한다.

7. 로그·관측·점검 순서#

현재 확인 가능한 신호#

신호 위치 사용법
Worker 관측 주/MCP wrangler.jsoncobservability.enabled Cloudflare 대시보드에서 해당 Worker의 요청·오류를 본다.
요청 상관관계 주 Worker 응답 X-Request-Id 사용자에게 받은 응답 헤더 값을 오류 로그의 requestId와 대조한다.
예외 로그 주 Worker console.error 예상 밖 오류는 { event: "request_error", requestId, path } 형식이다.
실시간 상태 웹 화면 연결 상태, public/app.js 연결/재연결/오프라인 표기를 확인한다.
Windows 충돌 로그 %LOCALAPPDATA%\Bluehair\TodayWidget\Data\Logs\today-widget-YYYYMMDD.log WebView2 초기화, 설정 저장, startup 오류를 확인한다.
Windows 설정/쿠키 profile %LOCALAPPDATA%\Bluehair\TodayWidget\Data\settings.json, WebView2 설치 업데이트 시 보존된다. 민감 세션 정보가 있을 수 있어 공유하지 않는다.
CI GitHub Actions 실행 로그 실패 job의 명령과 에러를 확인한다.

현재 알림 규칙, Logpush, Analytics Engine, 외부 APM, 가동률 모니터링은 저장소 설정에 없다. 장기 운영 시 별도 설계·권한 승인 후 추가한다.

8. 증상 → 확인 → 조치#

증상 먼저 확인 조치
로그인 후에도 401 APP_TOKEN 값, /api/auth/status, 브라우저 쿠키 토큰 오타를 확인하고, HTTPS origin에서 다시 로그인한다. 토큰 교체 뒤에는 기존 세션이 무효가 된다.
쓰기 요청이 403 CSRF_REJECTED 요청의 Origin, cookie 인증 여부 브라우저는 같은 origin에서 실행한다. 외부 origin의 cookie 쓰기는 지원하지 않는다. 자동화는 bearer 방식을 쓴다.
415 또는 400/422 Content-Type, JSON, 날짜/시간/category application/json 객체인지, 실제 YYYY-MM-DD, HH:MM, 허용 category인지 확인한다.
수정은 성공했는데 다른 화면이 늦게 바뀜 응답 revision, /ws 연결 상태, /api/changes WebSocket은 무효화 신호다. 끊겼다면 snapshot/changes 재조회가 정답이며, commit 자체는 이미 성공했을 수 있다.
데이터가 오래된 것처럼 보임 revision label, localStorage cache, 네트워크 온라인에서 snapshot을 재조회한다. 앱 셸만 캐시되며 API는 캐시되지 않는다. 필요 시 해당 사이트 저장소의 timetable.snapshot.v2를 지운 뒤 재로그인한다.
/ws426 WebSocket Upgrade 헤더 HTTP fetch가 아닌 WebSocket 클라이언트로 연결한다.
새 schema가 없거나 D1 오류 wrangler.jsonc의 DB binding/ID, migration 적용 여부 새 migration을 만들고 원격 적용 여부를 확인한다. 운영 DB에 seed로 임시 해결하지 않는다.
주 도메인이 열리지 않음 today.bluehair.blue route/DNS, Workers.dev fallback Cloudflare DNS/route/배포를 분리해 확인한다. Windows 위젯은 주 도메인 실패 시 Workers.dev fallback을 한 번 시도한다.
MCP가 시간표를 못 읽거나 쓰지 못함 MCP TIMETABLE_API, 두 APP_TOKEN, OAuth scope Service Binding target이 timetable인지, 양쪽 secret이 같은지, timetable.read/write 권한이 있는지 확인한다. MCP에 D1 binding을 추가해 우회하지 않는다.
ChatGPT 앱에서 MCP가 안 보임 ChatGPT workspace/Developer Mode/웹 지원 여부 MCP README의 등록 요건을 확인한다. 저장소 기능만으로 ChatGPT 모바일 custom MCP 사용을 보장하지 않는다.
Windows 위젯이 빈 화면/초기화 실패 Data\Logs, WebView2 Runtime, Smoke-Test.ps1 WebView2 Runtime과 Windows/.NET 요구사항을 확인한 뒤 smoke test를 재실행한다.
위젯이 부팅 시 안 열림 HKCU\Software\Microsoft\Windows\CurrentVersion\RunBluehairTodayWidget windows\scripts\Set-Startup.ps1 -Mode Enable 또는 위젯의 tray 메뉴로 다시 등록한다.
Windows 설정/로그인이 이상함 %LOCALAPPDATA%\Bluehair\TodayWidget\Data 먼저 백업한 뒤 settings.json 또는 WebView2 profile을 격리/재생성한다. Uninstall.ps1 -RemoveUserData는 사용자 데이터도 삭제한다.
CI에서 node/의존성 실패 Node 22, pnpm 10, lockfile pnpm install --frozen-lockfile을 새 셸에서 재실행한다. lockfile을 임의로 재생성하지 않는다.

9. 보안 운영 체크리스트#

배포 전 또는 시크릿/권한 변경 전에 확인한다.

  • 실제 APP_TOKEN이 소스, wrangler.jsonc, .dev.vars.example, Git diff, 이슈/PR, 화면 캡처에 없는가?
  • 주 Worker와 MCP Worker의 운영 APP_TOKEN이 의도적으로 같은 최신 값인가?
  • .dev.vars와 Windows의 Data\WebView2를 공유/커밋하지 않는가?
  • API 쓰기 요청은 cookie일 경우 same-origin CSRF 검사를, 자동화일 경우 bearer 인증을 유지하는가?
  • 새 API가 readJson, 길이·날짜·시간·category 검증, 에러 코드 정책을 따르는가?
  • 모든 성공 mutation이 D1 transaction + revision 증가 + notify 경로를 거치는가?
  • MCP의 새 도구가 OAuth scope를 확인하고, Service Binding만 사용하며, upstream 오류 본문/시크릿을 노출하지 않는가?
  • 보안 헤더(CSP, HSTS, frame 차단 등)를 바꿀 경우 PWA/WebView2/MCP 동작을 함께 시험했는가?
  • Windows 배포 파일을 외부에 배포한다면 Authenticode 서명과 SHA-256 검증 절차가 준비됐는가? 현재 저장소는 서명 자동화를 제공하지 않는다.

10. 기능 확장 지점과 경계#

안전한 변경 순서#

  1. 요구사항과 데이터 모델을 먼저 정한다.
  2. 저장 구조가 바뀌면 새 D1 migration을 추가한다.
  3. src/index.ts에 입력 검증·권한·CRUD를 추가한다.
  4. 쓰기라면 mutatecommitWithBestEffortNotification 경로로 revision/invalidation을 보장한다.
  5. API 테스트를 추가한다.
  6. public/app.js와 필요 시 timetable-core.js/웹 테스트를 갱신한다.
  7. ChatGPT에서도 편집해야 한다면 MCP Zod schema, 도구, scope 검사, MCP 테스트를 추가한다.
  8. Windows는 WebView2로 웹 UI를 호스팅하므로, 기본적으로 웹 UI 변경이 반영된다. 네이티브 tray/창 동작 변경만 windows/src/TodayWidget를 수정한다.
  9. root/MCP/Windows 검증과 CI를 모두 통과한 뒤 배포한다.

구현되어 있는 확장 지점#

목표 주 변경 위치 주의점
일정/할 일 필드 추가 migration, Worker parser/serializer, PWA, MCP schema snapshot 계약과 기존 행의 기본값·migration을 함께 설계
새 category Worker CATEGORIES, DB CHECK, PWA UI/스타일, MCP Zod schema 한 곳만 바꾸면 API/DB/UI가 불일치한다.
새 PWA 화면 public/index.html, styles.css, app.js widget mode와 mobile 반응형·offline shell을 확인
실시간 효율 개선 SyncHub, public/app.js WebSocket은 push-only invalidation이라는 복구 가능 모델을 유지
새 ChatGPT 도구 MCP createServer, schema, tool tests destructive action annotation은 UX 힌트일 뿐, scope 검사가 실제 경계
Windows 네이티브 기능 windows/src/TodayWidget/Infrastructure x64 build, startup, 로그, WebView2 허용 host 정책을 함께 검토

명시적으로 미구현/별도 설계가 필요한 항목#

  • Android 네이티브 앱/APK 및 Android 위젯
  • 다중 사용자·다중 workspace 권한 모델 (현재 default 한 workspace)
  • ChatGPT native Scheduled Tasks 저장소와 D1의 자동 양방향 동기화
  • SSE 전송
  • 푸시 알림/캘린더 동기화/외부 캘린더 API
  • D1 자동 백업·자동 복구·정기 export
  • 운영 알림, 외부 로그 수집, 가동률 모니터링
  • Windows 코드 서명/공개 배포 파이프라인
  • CI 기반 자동 배포와 자동 롤백

이 항목들은 설정만 추가해서 안전하게 완성되지 않는다. 데이터 소유권, 인증, 비용, 장애 복구, 개인정보 보관 정책을 정한 뒤 별도 작업으로 설계한다.

11. 변경 후 최소 인계 기록#

운영 변경 또는 기능 확장 PR에는 아래 정보를 남긴다.

  1. 변경한 데이터/라우트/클라이언트/Cloudflare 바인딩
  2. migration 이름과 원격 적용 여부
  3. 시크릿 교체 여부(값 자체는 기록하지 않음)
  4. 실행한 root, MCP, Windows 검증 명령과 결과
  5. 배포 Worker 버전/시각, 도메인 확인 결과
  6. 백업 위치의 식별자와 복구 책임자(비밀/경로 공개 금지)
  7. 롤백 기준 commit과 D1 영향 여부

이 기록이 있어야 다음 운영자가 “코드는 정상인데 데이터/도메인/시크릿이 다르다”는 상황을 빠르게 분리할 수 있다.