마지막 업데이트 2026-07-22
PR #911 — /main/report 페이지를 회기별 편집 테이블 하나에서 탭 3개(회기 보고서 세팅 · 프롬프트 테스트 · JSON 노션 테스트)로 재구성한다. 실제 AI 보고서 생성은 외부 리포터 서버(Gemini)가 맡고, web은 인증·키 은닉 프록시 / JSON→Notion 문서 변환·발행 / 학습플랜 프롬프트 저장 / 테스트 UI·폴링을 담당하는 오케스트레이션 계층이다.
리뷰 시 먼저 볼 지점 3가지: ① 두 curriculum 라우트에 parseReport* 검증기가 통째로 복붙됨, ② 테스트 탭이 dev에서 프록시/인증을 우회해 127.0.0.1:8787을 직접 호출(prod만 /api 경유), ③ updateCurriculum이 리포트 필드를 항상 덮어쓰는데 데이터 소실은 API 머지 로직으로만 방어된다.
PR #889에서 만든 회기 보고서 전사 파이프라인의 후속 작업으로, 그때 생긴 "보고서 탭"을 프롬프트 실험·검증 도구로 확장한다. 순변경은 +3,253/−238이고 대부분 신규 파일 추가이며, 기존 회기 편집 로직은 페이지에서 세팅 탭으로 위치만 이동했다.
withAuthAdminOrDeveloper — 관리자/개발자 전용, 진행자 차단(기존 보고서 탭 정책과 동일).ppi-curriculum-{stage} 아이템에 optional 속성 2개(reportCommonPrompt, reportSessionPrompts)만 얹힘. 기존 데이터 무변경.DPP_REPORT_TEST_URL, DPP_REPORT_TEST_SERVICE_KEY, NOTION_API_KEY, NOTION_REPORT_TEST_CONTAINER_ID) + 기본값 있는 선택 2 (NOTION_APP_WORKSPACE_SLUG, NOTION_API_VERSION).폼 입력 + 로그 CSV 변환
report-test.tsx
아동명·MP3(≤8)·수업로그·프롬프트 입력. 붙여넣은 탭 표를 Speaker,Text,Source CSV로 변환.
인증 프록시 (prod)
POST /api/reports/test
admin/dev 인증 후 X-Service-API-Key를 붙여 외부 서버로 formData 전달.
외부 생성 + 상태 폴링
GET /api/reports/test/[jobId]
Gemini 서버가 생성, 3초 간격 폴링으로 queued→completed 추적.
결과 JSON 붙여넣기
report-json-test.tsx
JSON 파싱 + 블록 변환
report-document.ts
구조화 JSON → Notion 블록(표/콜아웃/progress/metrics), 2,000자 분할.
Notion 페이지 생성
report-json-notion.ts
NOTION_API_KEY로 직접 호출, 100블록 단위 페이지네이션.
UpdateExpression이 두 필드를 부분 업데이트가 아니라 매번 SET한다. 필드를 뺀 채 호출하면 null로 지워진다.
curriculum-page.tsx의 카테고리/순서 변경 등 20여 곳이 updateCurriculum을 호출하지만, 이 머지 덕분에 리포트 프롬프트가 날아가지 않는다. 방어는 API 계층에만 존재한다.
dev에서는 인증·X-Service-API-Key 프록시(test/route.ts)가 실행되지 않는다. 반면 JSON 노션 탭은 dev에서도 항상 /api/reports/json-test를 탄다 — 두 탭의 경로 전략이 다르다.
| 레이어 | 파일 | 핵심 변경 |
|---|---|---|
| Page | components/pages/report-page.tsx | 단일 테이블 → 3탭 셸 (−205줄) |
| FE 섹션 | sections/report-session-settings.tsx | 신규 527줄. 회기 편집 + 공통/개별 프롬프트 UI, 편집 모달 |
| FE 섹션 | sections/report-test.tsx | 신규 575줄. MP3+로그 폼, 로그→CSV 변환, 상태 폴링 |
| FE 섹션 | sections/report-json-test.tsx | 신규 169줄. 결과 JSON→Notion 발행 UI |
| API | api/reports/test/route.ts | 신규. 외부 서버 프록시(GET 프롬프트, POST 폼) |
| API | api/reports/test/[jobId]/route.ts | 신규. 작업 상태 폴링 프록시 |
| API | api/reports/json-test/route.ts | 신규. JSON→Notion 생성 엔드포인트 |
| API | api/curriculums/route.ts · [id]/route.ts | 리포트 프롬프트 파싱·검증, 기존 값 머지 |
| Lib | lib/report-document.ts | 신규 741줄. JSON→Notion 블록 변환기 + 스키마 + 폴백 |
| Lib | lib/report-json-notion.ts | 신규 91줄. Notion API 클라이언트 |
| Lib | lib/report-evaluation-design-sample.ts | 신규 764줄. 테스트용 샘플 JSON(데이터) |
| DB | lib/db-queries.ts · types/db/curriculum.types.ts | curriculum에 리포트 프롬프트 필드 2개 |
검증기 복붙. parseReportCommonPrompt/parseReportSessionPrompts가 api/curriculums/route.ts와 [id]/route.ts에 완전히 동일하게 중복 정의됐다. 회차 키 정규식(^[1-9]\d*$) 등 규칙이 갈라질 위험이 있으니 공용 모듈로 추출 권장.
dev 우회 비대칭. 프롬프트 테스트 탭은 dev에서 127.0.0.1:8787을 직접 호출해 인증·프록시가 검증되지 않는다. JSON 노션 탭은 dev에서도 /api 경유라 로컬에도 NOTION_* 키가 필요하다. 두 탭의 경로 전략 차이가 의도된 것인지 확인 필요.
update가 항상 덮어씀. updateCurriculum(|| null)은 부분 업데이트가 아니다. API PUT의 기존 값 머지로만 방어되므로, 배치/스크립트가 db 함수를 필드 없이 직접 호출하면 프롬프트가 초기화된다.
워크스페이스 slug 기본값. NOTION_APP_WORKSPACE_SLUG 기본값이 "succulent-glitter-109"로 코드에 박혀 있다. 다른 워크스페이스에 배포하면 생성된 문서 링크가 잘못된 slug를 가리킨다.
파서 두 갈래. json-test는 parseGeneratedReportDocument(hasOnlyKeys 엄격 검증)만 사용 — Gemini가 규격 외 키를 하나라도 넣으면 전체가 REPORT_GENERATION_INVALID_JSON(400)으로 실패한다. 같은 파일의 관대한 parseReportDocument(dynamic 폴백)는 이 경로에서 안 쓰인다.
롤아웃 안전. DB는 optional 속성 추가라 마이그레이션·백필 불필요, 기존 curriculum 무변경. 신규 API는 admin/dev 전용. env 미설정 시 각각 *_NOT_CONFIGURED/503으로 안전하게 실패한다.
관련 문서: 회기 보고서 전사 파이프라인 — 기획과 구현 (75282732) — PR #889에서 만든 보고서 탭·전사→보고서→Notion 파이프라인을 이 PR이 프롬프트 실험/JSON 테스트 도구로 확장한다.