- src/dist 산출물 분리 원칙 정리(.gitignore, .gitattributes) - 루트 및 주요 폴더(config/scripts/prompts/tests/src, 런타임 폴더 5종)에 안내용 README.md 추가 - CHANGELOG.md, LICENSE, docs/ops/05-release-and-versioning.md 추가 - docs/README.md 문서 지도 갱신
36 KiB
핸드오프 — DMF Crawler
다음 세션은 이 문서부터 읽어라. 지금까지 확정된 것, 실측으로 밝혀진 것, 진행 중인 작업, 다음에 할 일이 전부 여기 있다.
작성 시각: 2026-09-03
프로젝트 루트: D:\workspace\DMF_Crawler
상태: 구현 진행 중 / 실제 9,084건 기준선 import 완료 / API 키 선택 정책 복구 / xlsx·Windows GUI 디자인 RED 통과 / 온보딩 스크롤·문장 UX 수정 / AGY headful CCBAC03 xlsx 수집 수동 smoke 성공(9,816건) / 전체 79개 테스트 통과
2026-09-03 추가 사용자 지적 — 스크롤·상단 안내문·AGY headful 수집
사용자 지적:
- 온보딩 스크롤창이 실제 항목/텍스트 위에서 휠 스크롤되지 않는다.
- 상단 노란 안내 박스의 문제가 색/박스 자체라기보다 문장이
무엇/왜/어떻게/다음식으로 AI스럽고 한국어 UX 문장으로 부자연스럽다. - AGY가 사람처럼 headful Chrome 으로 수집해야 한다고 여러 번 말했는데, 현재 구현은 공식 API/기존 DB 중심이고 AGY는 요약용 부가 단계처럼만 동작한다.
TDD 처리:
- 추가 RED:
test_scroll_frame_mousewheel_scrolls_when_pointer_is_over_contenttest_onboarding_status_panel_names_blockers_instead_of_generic_yellow_info_boxtest_headful_browser_prompt_contract_forbids_headless_http_and_secretstest_headful_browser_collect_validates_download_and_returns_raw_recordstest_headful_browser_collect_rejects_incomplete_downloadstest_headful_browser_collect_reports_mcp_permission_denial_from_stderrtest_headful_browser_collect_reports_execute_url_permission_denial_from_stderrtest_headful_mcp_allow_rule_is_scoped_and_does_not_use_dangerous_skiptest_headful_browser_prompt_blocks_wrong_review_result_exporttest_headful_browser_accepts_public_ccbac03_xlsx_headerstest_source_auto_without_api_key_routes_to_headful_browsertest_source_browser_mode_routes_to_headful_even_when_api_key_existstest_agy_google_login_is_optional_when_ai_disabled_for_api_modetest_agy_is_prompted_for_headful_collection_when_auto_has_no_api_keytest_agy_is_prompted_for_forced_browser_mode_even_with_api_key
- RED 실패 확인:
- 콘텐츠 라벨 위에서
<MouseWheel>발생 시canvas.yview()가 이동하지 않음. - 상단 안내문에
무엇:,왜:,어떻게:,다음:라벨이 실제로 노출됨. dmf_crawler.agy.browser_collect모듈 및pipeline._fetch_with_headful_browser경로가 없음.- 첫 실제 headful smoke 에서 AGY가
mcppermission auto-deny 로 빈 응답을 냄. mcp(chrome-devtools/*)를 top-level 에만 넣었을 때 실제 AGY shared config 에 매칭되지 않아,userSettings.globalPermissionGrants.allow도 필요함을 확인.- 다음 smoke 에서
execute_urlpermission auto-deny 발생. - CCBAC03 실제 xlsx 헤더(
신청인,제조국가,최초등록일자,최종변경일자)를 기존 API/baseline 헤더(업체명,제조국가명,발급일자)만 요구하던 파서가 거부함. - 진단 화면이
agy.enabled=false만 보고auto+API 키 없음또는browser모드에서도 AGY/Google 로그인을 “필요 없음”으로 표시할 수 있었음. - AGY가 처음에는 같은 파일명(
의약품등심사결과공개.xlsx)을 wrong export 로 보냈다고 판단했으나, parser alias 보강 후 실제 CCBAC03 데이터 9,816건으로 확인됨. 파일명은 사이트가 generic 하게 붙이는 이름이다.
- 콘텐츠 라벨 위에서
- 수정:
ScrollFrame이 canvas 뿐 아니라 내부 텍스트/카드 위의 wheel 이벤트도 처리하도록event.widget부모 체인을 검사하는 전역 wheel binding 으로 변경.- 상단 안내문을 자연스러운 한국어 2~3문장으로 교체. 예:
먼저 자료 보관소, 리포트 저장 폴더를 확인해 주세요. src/dmf_crawler/agy/browser_collect.py추가. AGY 프롬프트는 visible/headful Chrome + chrome-devtools MCP 를 명시하고 curl/headless/API 직접 호출과 비밀번호/토큰 prompt 입력을 금지한다.source.mode를auto | api | browser로 확장.auto에서 API 키가 없으면 AGY headful 수집을 시도하고,browser는 키가 있어도 headful 수집을 강제한다.- AGY headful 다운로드는
.crdownload/.tmp없음, xlsx zip/XML 구조, 행 수, sheet XML 파싱을 통과해야FetchResult로 인정한다. - headful 권한 준비 함수
ensure_headful_mcp_permission()추가. 실제 AGY CLI가 읽는 top-levelpermissions.allow와userSettings.globalPermissionGrants.allow양쪽에mcp(chrome-devtools/*),execute_url(nedrug.mfds.go.kr)만 좁게 추가한다.mcp(*),execute_url(*),--dangerously-skip-permissions는 금지. - 프롬프트는 ambiguous 버튼 클릭 대신 visible Chrome 에서
https://nedrug.mfds.go.kr/pbp/CCBAC03/getExcel을 열도록 좁혔다. prototype_xlsx.read_rows()는 CCBAC03 웹 xlsx 헤더 alias(신청인,제조국가,최초등록일자,최종변경일자)를 API 표준 RawRecord 필드로 매핑한다. 날짜는최종변경일자를 우선하고 없으면발급일자,최초등록일자순으로 사용한다.checks.py는 AGY 필요 여부를agy.enabled만으로 보지 않고source.mode와 API 키 존재까지 함께 본다. API 모드+AI 요약 꺼짐이면 AGY 불필요,auto+키 없음/browser면 수집용 AGY 준비를 WARN으로 안내한다.
- 검증:
pytest tests -q→ 79 passed- 실제 수동 smoke:
./.venv/Scripts/python.exe -m dmf_crawler run --trigger manual --force --skip-agy→ SUCCESS exit=0 - 실측 run_id:
run_20260903_130332 - 다운로드:
data/headful_downloads/run_20260903_130332/의약품등심사결과공개.xlsx(사이트 generic 파일명) - 수집/정규화: 9,816건 → 9,816건, persist/report/backup 성공
- 리포트:
reports/DMF_리포트_2026-09-03.xlsx(9,903행급)
- 남은 확인:
- 전용 Chrome profile Preferences, CDP 포트 sentinel 은 RED backlog 에 남아 있다.
- 06:00 스케줄러가 실제 로그인 데스크톱 세션에서 headful Chrome 을 안정적으로 띄우는지는 별도 수동 smoke 가 필요하다.
2026-09-03 릴리즈 배포본 생성 완료
사용자 요청: 카카오톡으로 전달할 수 있게 원드라이브 바탕화면에 압축 배포본과 가이드를 만들어 둘 것.
산출물:
- 폴더:
C:\Users\encep\OneDrive\바탕 화면\DMF_Crawler_v0.1.0_20260903 - zip:
C:\Users\encep\OneDrive\바탕 화면\DMF_Crawler_v0.1.0_20260903.zip - SHA256:
C:\Users\encep\OneDrive\바탕 화면\DMF_Crawler_v0.1.0_20260903.sha256.txt - 사용자 첫 안내:
00_먼저_읽어주세요.txt - 검증 요약:
릴리즈_검증.txt
포함:
bootstrap.cmd,README.md,pyproject.toml,src,config,scripts,prompts,docs일부, fixture 샘플 리포트 1개.
제외 감사 통과:
.venv,data,logs, 실제reports,backup,state,tests,__pycache__,*.pyc,*.lnk,config.local.toml,service_key,oauth-token없음.
검증:
pytest tests -q→ 79 passedcompileall→ exit 0dmf_crawler --help→ exit 0- 최종 zip contents audit → 필수 파일 누락 없음 / 금지 파일 없음
- 최종 zip 압축 해제본
compileall→ exit 0 - 최종 zip 압축 해제본
dmf_crawler --help→ exit 0 - 현재 개발 폴더
doctor --json은 exit 2: Excel 이DMF_리포트_2026-09-03.xlsx를 열고 있어report_writableCRITICAL. 배포본 결함은 아님. Excel 닫으면 기존처럼 WARN 수준으로 내려갈 것.
주의:
- zip 안에는 실제 DB/리포트/로그가 없다. 새 사용자 PC는 압축 해제 후
bootstrap.cmd를 먼저 실행해야 한다. - fresh PC 에서
bootstrap.cmd전doctor는.venv가 없어 CRITICAL이 정상이다. - 인증키 없이 최신 수집을 원하면 AGY 설치/Google 로그인 후 headful Chrome 경로를 사용한다.
2026-09-03 사용자 실측 UI 회귀 — RED 고정·수정 완료
사용자가 새로 제보한 스크린샷:
C:\Users\encep\AppData\Local\Temp\pi-clipboard-5df73a57-a813-41be-b025-819e48bd73c2.png
관찰된 문제:
- 온보딩/진단 창 하단 footer 버튼들이 글자 없이 빈 버튼처럼 보임. 이전에 “하단 버튼 최소 크기” RED를 추가했지만 실제 화면 결함을 충분히 잡지 못했다.
- 일부 버튼이 여전히 짜부라지거나 clipped 된다.
- “새 버전” 화면에서 보여야 할 버튼/액션이 아예 안 보이는 경우가 있다.
- 현재 테스트
test_onboarding_footer_buttons_have_readable_minimum_size는 단순width/요청 크기만 보므로, 실제 렌더링에서 텍스트가 사라지는 문제를 놓쳤다.
TDD 처리:
- 스크린샷 상태를 재현하는 fixture/check 결과를 주입해,
refresh()이후 실제 버튼text,winfo_ismapped(),winfo_width/height(),winfo_reqwidth/height(), 창 밖 clipping, scroll viewport overflow 를 검사하도록 RED를 강화했다. - 추가 RED:
test_onboarding_footer_buttons_render_visible_text_after_refreshtest_onboarding_footer_buttons_keep_text_when_required_items_existtest_onboarding_row_action_buttons_are_not_clipped_inside_scroll_viewtest_onboarding_new_grouped_layout_keeps_primary_actions_visible
- RED 실패 확인:
- footer 버튼
winfo_ismapped() == 0으로 실제 화면에 배치되지 않음. - 창 요청 높이
771px이 실제 높이700px을 넘어 footer가 잘림. - 긴 한글/경로 제목에서 scroll 내부 요청 폭
1745px이 viewport867px을 넘어 overflow.
- footer 버튼
- 수정:
src/dmf_crawler/gui/app.py의 shell layout 을pack누적 방식에서 grid 기반 고정 footer/가변 scroll 구조로 변경.SCROLL_VIEW_HEIGHT=300으로 1366×768 노트북 예산 안에서 footer를 보존.- 긴 행 제목/detail/fix_hint 에
wraplength를 적용해 행 액션 버튼이 viewport 밖으로 밀리지 않게 함. - CRITICAL 실패가 있으면 footer [지금 실행] 버튼을 disabled 로 유지.
- 검증:
pytest tests/test_ux_regressions.py tests/test_gui_design_contracts.py -q→ 14 passedpytest tests -q→ 65 passed
- 남은 확인: 실제 Windows tkinter 창을 열어 육안 확인은 아직 하지 않았다. 스크린샷 기반 결함은 자동화 RED로 고정됐다.
2026-09-03 API 키 선택 정책 복구
- 사용자 지적에 따라 회귀 수정: 공공데이터포털 인증키는 선택이다.
- 키가 없으면
preflight에서BLOCKED하지 않는다. 공식 APIfetch만SKIPPED처리하고ctx.stale=True로 마지막 성공 스냅샷 리포트 경로를 사용한다. doctor/온보딩에서도api_key_present,api_key_valid는WARN선택 기능이다. 실패해도 [지금 실행]을 막지 않는다.- GUI 그룹에서 인증키는
필수 설정이 아니라선택 기능으로 이동했다. - 추가/수정 RED:
test_run_without_service_key_uses_existing_baseline_instead_of_blockingtest_public_data_api_key_is_optional_in_doctortest_public_data_api_key_is_grouped_as_optional_in_onboardingtest_public_data_api_key_optional_policy_is_documented_in_ssot
- 주의: 키 없는 최신 웹/엑셀 수집 소스는 AGY headful Chrome 경로로 구현·수동 smoke 성공했다. 다만 06:00 스케줄러에서 로그인 데스크톱 세션/Chrome 프로필/9222 CDP 포트가 항상 준비되는지는 아직 별도 RED·수동 smoke 가 필요하다.
2026-09-03 디자인 업그레이드 완료
design.md생성: 컨셉은 감사 가능한 조용한 계기판. Windows 앱은tkinter/한국어/Fluent식 그룹화, xlsx는 규제 원장/출처/접근성 중심.- Windows 앱 개선:
- 상단 요약 strip:
필수 N/권장 N/정상 N. - 체크 항목을
필수 설정/선택 기능/운영 상태로 그룹화. 공식 API 인증키와 AI 요약은 선택 기능입니다. 없어도 기존 자료 리포트 생성은 막지 않습니다.문구를 화면 구조에 포함.- 각 행에
StatusBadge+ 텍스트 상태(통과/필수 조치/권장 조치) + 최소 폭 action button 적용.
- 상단 요약 strip:
- xlsx 개선:
- 워크북 문서 속성 추가: title/subject/author/category/keywords/comments/created.
00_대시보드!A1에 스크린리더용 목적문 추가.- dashboard 내부 링크, 각 데이터 시트의
← 대시보드역링크를 RED로 검사. - 외부 링크 문구를
조회에서원문 조회로 변경. - 집계 내부 링크 문구를
원장 보기, 워치리스트 링크를변경분 보기로 변경. - 데이터 시트 visible column width cap 검사 추가.
01_오늘변경분변경내용 열은 52 → 48로 줄여 긴 값은 wrap 처리.
- 새/강화 테스트:
tests/test_gui_design_contracts.pytests/test_report_e2e_design.py
- 검증:
python -m compileall -q src testsPASSpytest tests -q→ 58 passed
- 실제 9,084건 리포트 재생성:
reports/DMF_리포트_2026-09-02.xlsx- 크기: 1,273,793 bytes
- OOXML 확인: A1 목적문 존재, dashboard internal links 8개,
원문 조회포함, core title 포함, visible max column width <= 48.71. - 주의:
reports/~$DMF_리포트_최신.xlsx잠금 파일이 있어reports/DMF_리포트_최신.xlsx는 갱신 실패. Excel에서 최신본을 닫고 재실행해야 최신본 복사가 된다.
1. 프로젝트가 무엇인가
Windows 11 PC 에서 매일 06:00 에 한국 식약처 원료의약품 등록(DMF) 데이터를 수집하고, 전일 대비 신규·변경·취하를 탐지해, 탭별로 연동된 보기 좋은 xlsx 리포트를 만든다.
AI CLI 는 Google Antigravity CLI (agy) 를 두 역할로 쓴다. 수집용 AGY는 visible/headful Chrome 을 조작하고, 요약용 AGY는 사용자가 AI 요약을 켰을 때만 headless/non-interactive 로 호출한다. Claude Code 가 아니다.
쓰는 사람은 개발자가 아니다. 더블클릭 한 번으로 설치·설정이 끝나야 하고, 문제가 생기면 창이 떠서 고치는 법을 알려줘야 한다.
전체 요구사항: docs/00-REQUIREMENTS.md (R1R8, N1N8)
2. 사용자가 확정한 것 (인터뷰 답변)
| 항목 | 사용자 결정 |
|---|---|
| PC 상태 | 매일 다르다. 켜둘 때도 끌 때도 있음 → 트리거 3개(매일 06:00 + 로그온 시 + 놓친 작업 따라잡기), 중복 실행 방지 잠금 필수 |
| 관리자 승격 | 설치 시 1회 허용 |
| API 키 | 아직 없음. 발급받은 적 없음. 공공데이터포털 인증키는 선택이며 없어도 기존 자료 리포트 생성은 막지 않는다. |
| 수집 방식 | source.mode=auto: API 키가 있으면 공식 API, 없으면 AGY headful Chrome 으로 CCBAC03 xlsx 다운로드. source.mode=browser: 키가 있어도 headful 강제. source.mode=api: 공식 API만 사용하고 키 없으면 기존 성공 자료로 폴백. |
| 크롤링 방식 | agy 가 headful Chrome 을 몰아 사람처럼 접근. 실제 수동 smoke에서 CCBAC03/getExcel 다운로드 및 9,816건 정규화 성공. |
| 브라우저 로그인 | 프로필/세션 유지로 처리한다. 비밀번호를 agy 프롬프트에 넣거나 모델에게 입력시키지 않는다. |
| 리포트 공유 | 본인이 보되 공유 옵션도 설정 가능해야 함 |
| 리포트 첫 화면 | 대시보드 |
| 워치리스트 | 필요함. 목록은 나중에 등록 |
| 알림 | 화면 창 + 메신저 둘 다 |
아직 사용자에게 못 받은 답
- 의약품안전나라 계정을 갖고 있는가? 로그인 시 익명보다 더 보이는 정보가 있는가?
- 메신저는 어느 것인가 (디스코드/텔레그램/슬랙/이메일)
- 워치리스트 초기 목록
DMF_현황.xlsx를 만든 것이 본인인가 타인인가 (카카오톡으로 받음)
3. 실측으로 밝혀진 결정적 사실
이것들은 전부 실제로 실행해서 확인한 것이다. 추측이 아니다.
3.1 기존 프로토타입이 이미 있었다
사용자가 준 C:\Users\encep\OneDrive\문서\카카오톡 받은 파일\DMF_현황.xlsx 를 전수 분석했다. 정본: docs/design/00b-baseline-data-analysis.md
- 시트 3개: 전체(9,084건) / 신규(헤더만) / 갱신이력(
2026-09-02, 9084, 0) - 컬럼 8개 = API 7필드 + 최초수집일(파생). 즉 이미 공식 API 로 수집하고 있었다.
- 서식이 전혀 없다 — 틀 고정·자동필터·조건부서식·차트가 전부 0. "예쁘게" 요구가 겨냥하는 지점.
3.2 API 7필드로 신규·변경·취하 전부 탐지 가능하다
| 사실 | 값 |
|---|---|
| API 전체 건수 | 9,084건 |
| 웹 화면 건수 | 9,840건 |
| 차이 | 756건 → API 는 정상 건만 반환 → 취하 = 레코드 소멸로 판정 가능 |
| 등록번호 중복 | 0건 → 완전한 자연 키 |
| 등록번호 앞8자리 vs 발급일자 | 44.5% 불일치 → 발급일자는 최종 갱신일로 움직인다 = 변경의 직접 신호 |
예: 20050831-33-A-81-08(18) 의 발급일자가 2026-08-18. 괄호가 변경 차수.
놓치는 것: 연차보고, 변경 사유, 취하 vs 취소 구분. 대상의약품은 등록번호 포맷에서 파생 가능(수 접두어 = 신물질).
3.3 nedrug 은 봇 차단이 없다
curl/8.10.1 UA 그대로 200 OK, HTML 382,506 bytes 수신. Cloudflare/Akamai/DataDome 없음. Apache + JSESSIONID + Elevisor for J2EE(WAS 모니터링).
즉 차단 회피 기술은 원래부터 불필요했다. 브라우저 방식의 근거는 "블락 방지"가 아니라 robots.txt 에 대한 태도와 로그인 세션 대비다.
3.4 엑셀 다운로드 실측 성공 + 함정 2개
POST /pbp/CCBAC03/getExcel 로 9,841행 / 1,379,451 bytes 1회 수신 성공. 요청 2회(세션 GET + 엑셀 POST).
- ⚠️ 날짜 파라미터를 비우면 헤더만 든 3,575 bytes 빈 파일이 온다. 조용한 실패.
- ⚠️ 받은 xlsx 를 openpyxl 로 열면 0행으로 읽힌다.
<dimension ref="A1"/>때문. sheet1.xml 직접 파싱 필수.
3.5 robots.txt 는 자기모순
robots.txt 는 User-agent: * / Disallow: / (26 bytes) 인데, /bbs/117 HTML 에는 <meta name="robots" content="index,follow"/> 가 있다.
3.6 법적 선
- 형사: 대법원 2022.5.12. 2021도1533 — 정보통신망법 §48 등 전부 무죄 확정(공개정보 + 보호조치 없음 + 약관상 이용제한은 접근제한 아님)
- 민사: 야놀자 사건 10억(부정경쟁방지법 성과도용), 잡코리아 v 사람인 2.5억(저작권법 §93)
- 두 민사 판결의 공통 결정요소는 "재배포·경쟁 이용"
- → 지켜야 할 선: 내부 리포트 전용, 재배포 없음. 이 선 안이면 리스크 Low
3.7 agy CLI 실측
정본: docs/research/05a-agy-cli-ssot.md (1,079줄)
- v1.1.22, 경로
%LOCALAPPDATA%\agy\bin\agy.exe - OAuth 토큰이 키링이 아니라 평문 파일:
~/.gemini/antigravity-cli/antigravity-oauth-token→ 작업 스케줄러를 동일 사용자 계정으로 돌리면 인증 통과. SYSTEM 금지. - ⚠️
--json-schema를 신뢰하면 안 된다. 실측에서structured_output이 없었고response에 JSON 이 4회 반복 + 한국어 산문 혼입, 토큰 3배. 자체 파싱·검증 필수. - ⚠️ 첫 호출 오버헤드 input 28,317 토큰 / 33.7초. 호출을 묶어야 한다.
- chrome-devtools MCP 가 이미 등록·활성화됨 → agy 가 Chrome 제어 가능
- 배치 체크리스트는 SSOT §16
3.8 Windows 실행 방식 제약
- 이 계정(Administrators 미소속)은 S4U 태스크 등록이 거부된다. Interactive 만 성공. 실측 확인.
- 사용자가 관리자 승격 1회를 허용했으므로 승격하면 S4U 가능한지는 미확인
- ⚠️ BOM 없는 UTF-8
.ps1을 Windows PowerShell 5.1 이 CP949 로 오독해 한글이 깨진다. 실측 재현. 모든.ps1은 UTF-8 with BOM 필수. - 콘솔 창을 원리적으로 없애려면 GUI 서브시스템 호스트(
wscript.exe/pythonw.exe)를 최상위에 둬야 한다
3.9 공공데이터 API 제약
- 트래픽 10,000 = 호출 횟수(레코드 아님). 하루 수십 호출이면 1% 미만. 운영계정 전환·활용사례 등록 영원히 불필요
- ⚠️ 인증키는 포털 계정당 1개. 재발급하면 기존 키 자동 폐기. 사용자가 무관한 목적으로 재발급하면 배치가 죽는다
- ⚠️ 인증키에 사용 기한 있음(오류코드 31). 24개월설은 미확정 → 하드코딩 금지, 오류 코드 관측으로 감지
4. 확정된 아키텍처
정본: docs/design/01-architecture.md (1,380줄, ADR 18개)
뼈대: 후보 3안을 독립 생성 → 3개 렌즈(운영 단순성/실패 내성/진화 가능성)로 심사 → SQLite 이벤트 소싱 안을 뼈대로 채택하고 나머지 두 안의 좋은 부분을 이식.
핵심 결정:
- 저장소: SQLite 단일 파일 + 이벤트 소싱(append-only 스냅샷 + 이벤트)
- 의존성 3개만:
httpx,XlsxWriter,jsonschema. pandas·openpyxl·PyYAML 기각 - 집계는 SQL GROUP BY로. pandas 안 씀
- xlsx 는 XlsxWriter 100%, 매일 새 파일 통째 생성
- 모든 숫자는 파이썬이 계산해 값으로 씀(LibreOffice 재계산 안 함)
- GUI 는 stdlib tkinter(외부 모듈 의존 시 "설치 안 되면 알림도 안 됨" 순환 실패)
- 배치는 UI 를 절대 안 띄운다. alert 의도만 기록 → 로그온 세션의 Interactive 에이전트가 15분마다 읽어 창을 띄움
- 온보딩=복구는 단일 컴포넌트.
checks.py진단 엔진을 CLI(doctor)와 GUI(onboard)가 공유 - agy 는 두 계층이다. 수집 계층은
fetch단계에서 headful Chrome 으로 CCBAC03 xlsx 를 받을 수 있고, 요약 계층은 diff·저장 후 선택적으로만 호출된다.enrichment테이블을events와 물리 분리해 R4.4(AI 요약 실패해도 리포트 생성)를 스키마 레벨에서 강제 - 오케스트레이션은 순차 STAGES 리스트 + 체크포인트. DAG 아님
이번 방향 전환으로 이미 반영된 ADR 보정
| ADR | 현재 정리 |
|---|---|
| ADR-01 | 1차 소스는 공식 API지만, API 키가 없거나 source.mode=browser이면 AGY headful Chrome CCBAC03 xlsx 수집을 사용한다. |
| ADR-02 | 별도 거대 소스 추상화 대신 source.mode 분기와 browser_collect.fetch_all()을 둔다. |
| ADR-06 | Playwright/Selenium 직접 의존은 쓰지 않고, 브라우저가 필요할 때 AGY + chrome-devtools MCP + visible Chrome 을 쓴다. |
| ADR-10 | headful 수집은 로그인 데스크톱 세션이 필요하다. 06:00 스케줄 smoke 는 남아 있다. |
| ADR-15 | AGY 요약은 diff 후 선택 단계, AGY 수집은 fetch 단계의 별도 경로다. 두 AGY를 혼동하지 않는다. |
5. 문서 현황 (총 5.6만 줄)
| 문서 | 줄 | 역할 |
|---|---|---|
00-REQUIREMENTS.md |
244 | 요구사항 SSOT. 여기서 시작 |
00-PROJECT-OVERVIEW.md |
1,051 | 프로젝트 개요·로드맵·리스크·용어집 |
README.md |
97 | 문서 지도 |
design/00-DATA-SOURCE-DECISION.md |
386 | 데이터 소스 결정 + API 완전 명세 |
design/00b-baseline-data-analysis.md |
591 | 기존 프로토타입 전수 분석. 실측 근거의 원천 |
design/01-architecture.md |
1,380 | 아키텍처 확정안. ADR 18개 |
design/02-data-model.md |
2,894 | 스키마 DDL·정규화·변경탐지 |
design/03-xlsx-report-spec.md |
3,554 | 시트별 셀 단위 명세 |
design/04-onboarding-wizard.md |
4,861 | 온보딩=복구 GUI 명세 |
ops/01-scheduling-and-resilience.md |
2,379 | 스케줄 등록·재부팅 내성·워치독 |
ops/02-failure-alerting.md |
4,724 | 알림 등급·문구·버튼 액션 |
ops/03-api-usage-policy.md |
1,796 | API 제약·오류 코드·키 만료 대응 |
ops/04-official-data-request-channels.md |
401 | 필드 추가 공식 요청 절차 + 문구 초안 |
research/01~10 |
30,018 | 조사 정본 10종 |
research/_raw/*.raw.md |
— | 원본 조사 덤프 8개(1.8MB). WebFetch 428건 + WebSearch 224건 결과 보존 |
⚠️ WebSearch 예산이 이 세션에서 소진됐다(200/200). 다음 세션에서는 다시 쓸 수 있을 것이나, 안 되면 https://www.bing.com/search?q=... 를 WebFetch 하는 우회를 쓴다.
6. 진행 중인 작업 / 남은 검증
- AGY headful CCBAC03 xlsx 수집은 구현했고, 실제 수동 smoke 에서 9,816건 수집·정규화·저장·리포트 생성까지 성공했다.
- 남은 핵심은 스케줄러 환경 검증이다. 06:00 태스크가 로그인 데스크톱 세션에서 visible Chrome, chrome-devtools MCP,
execute_url(nedrug.mfds.go.kr)권한, 다운로드 폴더를 안정적으로 준비하는지 확인해야 한다. - 전용 Chrome profile Preferences, CDP 포트 sentinel, headful 실패 시 온보딩 복구 버튼은 backlog 다.
7. 코드 현황
주요 모듈은 현재 존재하며, 전체 smoke 를 통과했다.
- CLI/pipeline/storage/repo/report/gui/notify/agy 모듈 구현됨.
src/dmf_crawler/agy/browser_collect.py: 수집용 AGY headful Chrome 경로.src/dmf_crawler/importers/prototype_xlsx.py: 기존 baseline xlsx + CCBAC03 웹 xlsx 헤더 alias 파싱.tests/: 79개 통과.
검증된 명령:
.\.venv\Scripts\python.exe -m compileall -q src tests
.\.venv\Scripts\python.exe -m dmf_crawler --help
.\.venv\Scripts\python.exe -m dmf_crawler doctor --json
.\.venv\Scripts\python.exe -m pytest tests -q
.\.venv\Scripts\python.exe -m dmf_crawler run --trigger manual --force --skip-agy
8. 다음 세션이 할 일 (우선순위 순)
- 스케줄러에서 headful Chrome 이 실제 로그인 세션에 뜨는지 수동 smoke 한다.
- Chrome profile/다운로드 폴더/CDP 9222 sentinel RED를 추가하고 구현한다.
- headful 실패 원인별 온보딩 문구와 복구 버튼을 다듬는다. 특히
mcp/execute_url권한 부족은 사용자가 바로 고칠 수 있어야 한다. - 실제 리포트를 Excel에서 열어 디자인/내용을 육안 감사하고, 깨지는 부분을 RED로 추가한다.
- 사용자에게 미해결 질문 확인: 메신저 종류, 워치리스트 초기 목록, 의약품안전나라 로그인 필요 여부.
9. 반드시 지킬 것 (함정 모음)
.ps1은 UTF-8 with BOM. 아니면 한글 깨짐openpyxl로 nedrug xlsx 를 열지 마라. 0행으로 읽힌다. sheet1.xml 직접 파싱- agy
--json-schema결과를 믿지 마라. 자체 JSON 추출 + jsonschema 검증 - agy 는 절대 경로로 호출,
AGY_CLI_DISABLE_AUTO_UPDATE=true,--disable-slash-commands, stdout/stderr 분리 - 작업 스케줄러는 사용자 계정으로. SYSTEM 은 agy 토큰에 접근 못 함
- 수집 완결성 검증 없이 diff 하지 마라. 전건 취하 오탐이 최악의 실패 모드. 안전장치 5종 + 취하 비율 임계
- 비밀번호를 agy 프롬프트에 넣지 마라. 클라우드 모델로 전송된다. 브라우저 로그인은 프로필 세션 유지 또는 CDP 직접 입력으로
- API 키·OAuth 토큰을 로그·문서·저장소에 남기지 마라
- 재배포하지 마라. 법적 선이 거기 있다
- 여러 에이전트에게 같은 파일을 맡기지 마라. 이 세션에서 실제로 충돌이 났다
10. 이 세션에서 있었던 일 (요약)
- 첫 리서치 워크플로우 8축이 사용자 중간 메시지로 인터럽트됨 → 트랜스크립트에서 전량 복구해
docs/research/_raw/에 보존 - 그 원본을 opus 로 정제해
research/01~10생성 - robots.txt 전면 금지 발견 → 공식 API 로 방향 전환
- 공식 Open API 발견 → 우회 불필요 판단
- 아키텍처 3안 → 3렌즈 심사 → 확정
- 사용자가
DMF_현황.xlsx제공 → 프로토타입 존재 확인, API 7필드 충분성 입증 - 온보딩 마법사·API 제약·공식 요청 채널 설계
- 사용자가 agy headful 브라우저 방식 요구 → 실현성 실측 착수
- 사용자 인터뷰로 운영 조건 확정
- 구현 착수 (진행 중)
11. 새로 추가된 작업 원칙 — TDD/RED/디자인 감사
사용자가 2026-09-03에 확정한 새 원칙:
- TDD 를 최상위 개발 원칙으로 둔다. No RED, No Code.
- 기능뿐 아니라 xlsx 리포트와 tkinter GUI 디자인도 RED 로 감사한다.
- 디자인 RED 는 중앙 정렬 남용, UX 붕괴, 시각적 치우침, 안 보이는 폰트, overflow, flex/grid 오류, input padding, 아이콘-텍스트 vertical align, 폼 라벨, 실제 텍스트 입력/버튼 클릭/복합 시나리오까지 잡는다.
- RED 는 계속 늘리기만 하지 않고, 사용자 유즈케이스 기준으로 통폐합·삭제·강화한다.
새 정본:
docs/research/11-tdd-red-and-design-audit-theory.md— Kent Beck/GDS/Google/Playwright/Testing Library/WCAG/NN/g/Vercel/LLM-TDD 논문 근거 아카이브docs/design/07-tdd-red-system.md— 프로젝트 적용 SSOTAGENTS.md— AI agent 실무 지침CLAUDE.md— Claude Code 포인터
이번 세션 완료
- R0 계약 RED 를
tests/test_project_contracts.py로 코드화했다. - 첫 RED 실패는 기대대로
dmf_crawler.gui.app,dmf_crawler.notify.pump누락이었다. - 최소 구현으로
src/dmf_crawler/gui/app.py,src/dmf_crawler/notify/pump.py를 추가했다. - 검증 결과:
python -m compileall -q src tests통과- 전체 import scan 통과
python -m dmf_crawler --help통과python -m dmf_crawler doctor --json은 API 키 없음으로 exit 2 + JSON 출력(정상)pytest tests -q→ 44 passed
추가 사용자 제보 UX 결함 수정(2026-09-03 04시대)
사용자 실행 로그/스크린샷으로 확인된 문제:
cp949콘솔에서—문자를 출력하다가UnicodeEncodeError로 CLI 가 다시 죽음.- Google 로그인/agy 가 기본 온보딩에서 필수처럼 보임. 사용자가 “Google 로그인 불가, 애당초 필요하면 물어봐야 한다”고 지적.
- 온보딩 하단 버튼 텍스트가 세로로 찌그러져 읽기 어려움.
TDD 처리:
tests/test_ux_regressions.py추가.test_cli_result_output_survives_cp949_consoletest_doctor_json_survives_cp949_consoletest_agy_google_login_is_optional_when_ai_disabledtest_google_login_optional_policy_is_documented_in_ssottest_onboarding_footer_buttons_have_readable_minimum_size
- RED 실패 확인 후 수정:
src/dmf_crawler/cli.py: 모든 사용자 출력/JSON 출력에 cp949 안전_safe_print적용, em dash 구분자 제거.src/dmf_crawler/checks.py:agy.enabled=false면 agy 설치/Google 로그인 체크를 OK +fix_action=None으로 처리.src/dmf_crawler/config.py,config/config.toml: AI 요약 기본값 false. Google 로그인은 사용자가 AI 요약을 켠 뒤에만.src/dmf_crawler/gui/widgets.py,src/dmf_crawler/gui/app.py: 버튼 padding/최소 width 토큰 추가.docs/00-REQUIREMENTS.md,docs/design/04-onboarding-wizard.md,AGENTS.md: Google 로그인은 선택 정책 반영.
- 검증 결과:
pytest tests/test_ux_regressions.py -q→ 5 passedpytest tests -q→ 54 passedcompileall통과- 전체 import scan 통과
doctor --jsoncp949 출력에서UnicodeEncodeError없음(exit 2는 API 키 없음으로 정상)
xlsx E2E 산출물 결함 수정(사용자 지적: “유의미한 xlsx를 단 한번도 배출한 적 없음”)
사용자 지적에 따라 완료 기준을 “파일 존재”가 아니라 seeded DB → 실제 xlsx 생성 → zip/XML 디자인 감사 통과로 올렸다.
TDD 처리:
tests/test_report_e2e_design.py추가.- 기준선 2건 + 둘째 날 신규/변경/취하/워치리스트/AI 코멘트 fixture 를 실제 SQLite DB 에 적재.
build_report()로 실제.xlsx생성.- zip/XML 로 검사: 파일 구조, 시트 순서, sharedStrings 내용, freeze panes, filter/table, 조건부서식 수, 탭 색, Excel table 이름.
- RED 실패 1:
report.data가v_current_records.ingredient_key를 읽지만 DB view 에 컬럼 없음.- 수정:
src/dmf_crawler/storage/migrations/0004_report_views.sql추가. 기존 0001 checksum 을 깨지 않고 view 재생성.
- 수정:
- RED 실패 2:
05_워치리스트시트에 필터/표 없음.- 수정:
src/dmf_crawler/report/sheets/s05_watchlist.py에T_WATCHExcel table 추가.
- 수정:
- 과특정 RED 1건 정정: 출처 문구의 OOXML/문장 순서 정확 일치 대신
출처:+식품의약품안전처포함으로 SSOT 정합화. - 검증 결과:
pytest tests/test_report_e2e_design.py -q→ 1 passedpytest tests -q→ 55 passedcompileall통과
- 사용자 확인용 SAMPLE 산출물 생성:
reports/sample/DMF_리포트_SAMPLE_2026-09-04.xlsx- 크기: 약 48 KB
- 시트:
00_대시보드,01_오늘변경분,02_전체현황,03_성분별,04_업체별,05_워치리스트,06_추이,99_메타 - 주의: fixture 기반 샘플이며 실제 식약처 데이터가 아니다. 현재 실제 데이터 리포트는 공식 API 키 또는 AGY headful CCBAC03 xlsx 수집으로 생성 가능하다.
실제 기존 데이터 import 완료(사용자 지적: “기존 데이터는 다 어디 갔어”)
확인 결과:
- 기존 원본 파일은 살아 있음:
C:\Users\encep\OneDrive\문서\카카오톡 받은 파일\DMF_현황.xlsx- 크기: 757,331 bytes
전체시트:A1:H9085= 헤더 1 + 데이터 9,084건
- 이전까지 프로젝트 DB 는 비어 있었음:
records=0,snapshots=0,events=0,fetch_stats=0- API 키 없음으로
BLOCKED실행 기록만 존재
TDD 처리:
tests/test_import_prototype_xlsx.py추가.- openpyxl 없이 zip/XML 로 기존 프로토타입 xlsx 구조를 읽는 importer RED.
- 한글 헤더(
등록번호,성분명,업체명,제조소명,제조소소재지,제조국가명,발급일자)를 API 표준 컬럼으로 매핑. - baseline DB 적재 + 실제 새 xlsx 리포트 생성까지 검사.
- RED 실패:
dmf_crawler.importers모듈 없음. - 수정:
src/dmf_crawler/importers/__init__.pysrc/dmf_crawler/importers/prototype_xlsx.py
실제 기존 파일 적용 결과:
- 실행:
- 원본:
C:\Users\encep\OneDrive\문서\카카오톡 받은 파일\DMF_현황.xlsx - run_id:
import_prototype_20260902 - run_date:
2026-09-02
- 원본:
- DB:
schema_version=4record_versions=9084records=9084snapshots=9084fetch_stats=1quality_checks=7events=0(기준선 수립일이므로 정상)
- 실제 리포트:
reports/DMF_리포트_2026-09-02.xlsxreports/DMF_리포트_최신.xlsx- 크기: 1,273,258 bytes
- 포함 시트:
00_대시보드,01_오늘변경분,02_전체현황,03_성분별,04_업체별,06_추이,99_메타 - 원본 문자열 검증:
프레가발린,젬시타빈염산염,출처:,식품의약품안전처,기준선포함 확인 - 워치리스트 시트는 현재 watchlist DB 가 0건이라 생성되지 않음(조건부 생성 설계)
- 검증:
compileall통과pytest tests -q→ 56 passed
다음 구현 작업은 실제 리포트 파일을 사람이 열어본 뒤 디자인 결함을 스크린샷/RED로 추가한다. 그 다음 API 키 입력 후 실제 다음 수집을 수행해 9,084 기준선 대비 신규/변경/취하 이벤트를 만들어야 한다.
이 문서는 세션 간 인수인계용이다. 프로젝트 정본은 docs/00-REQUIREMENTS.md, docs/design/01-architecture.md, docs/design/07-tdd-red-system.md 다.