← 포트폴리오 HWANG YUBIN · DEEP DIVE
케이스 스터디 · ③ 신뢰성

정확성 감사에서 상시 평가 하네스로 — "스크래퍼가 맞는지 어떻게 아는가"

K-pop 이벤트 스크래퍼 (64개 쇼핑몰, 단독 운영)  ·  모든 수치·커밋은 스크래퍼 repo 에서 확인. 커밋 해시·파일 경로 명시.

한 문단 요약

이 시스템은 64개 쇼핑몰에서 K-pop 굿즈·팬사인 이벤트를 자동으로 수집한다. 그런데 "잘 수집하고 있다"를 어떻게 믿는가? 스크래퍼가 자기 눈(셀렉터)으로 자기를 검증하면 같은 실수를 못 본다. 그래서 두 단계로 신뢰를 만들었다 — (1) 스크래퍼와 다른 눈으로 화면을 다시 읽어 대조하는 일회성 정확성 감사, (2) 그 감사를 매번 1초 만에 재현되는 상시 평가 도구로 승격. 결과: 정확도를 112개 실사례로 수치화했고, 그 도구가 스스로 숨어 있던 결함 5건을 찾아냈으며, 고친 뒤 95.5% → 100% 가 됐다.


1. 문제 — 자기가 자기를 검증하는 함정

스크래퍼는 쇼핑몰 HTML 에서 셀렉터로 이벤트 목록과 상태(진행중 / 만료 / 품절), 기간, 분류를 뽑는다. 문제는 이 판정이 조용히 틀린다는 것이다.

즉 "화면엔 잘 뜬다"는 체감으로는 정확성을 보증할 수 없었다. 신뢰를 측정 가능한 것으로 바꿔야 했다.


2. 진단 — "감사가 감사받는 구조" (2026-07-02)

첫 접근은 스크래퍼와 독립적인 측정으로 대조하는 것이었다. 설계 원칙은 tasks/audit-accuracy-plan.md 에 남겼고, 체크포인트 커밋은 d0665c6 (2026-07-02 KST) 다.

핵심은 "검증은 감지와 다른 눈으로":

  1. 독립 측정 — 외부 실측은 스크래퍼의 셀렉터·파서를 재사용 금지. 별도 캡처 스크립트 scripts/audit-capture-listings.js 가 각 샵의 스크린샷 + HTML 을 저장하고, 판독 에이전트가 화면을 직접 읽어 제목 목록을 추출한다(사람이 눈으로 보는 것과 동형의 "제3의 눈").
  2. 증거 파일 — 모든 판정은 reports/audit-<ts>/<shop>/ 의 원본 스크린샷·HTML 로 2클릭 내 소급.
  3. 불일치 우선 — 누락 · 유령(없는데 있다고 함) · 검증불가를 숨기지 않고 리포트 최상단에.
  4. 2차 검증 — 판독 결과를 또 다른 에이전트가 재대조(verify/*.json). 감사 도구 자체도 감사받는다.

규모(감사 계획 문서 Phase 1~4 기준): 내부 수집 65샵 / 1,247건 / 228초(실패 0), 외부 캡처 64샵, 독립 판독 에이전트 11개, 대조는 scripts/audit-compare.js.

확정 결함 4계통 (전부 당일 수정, d0665c6)

결함파일영향
D1cafe24 admin-skin 품절 아이콘 오처리main/parsers.js isCafe24SoldOut품절/재고 오판 (airmusic·millionmusic·muvve 계열)
D2bstageplus soldout 미추출main/shops/bstageplus.js품절 10건 상태 누락
D3oliveyoung_global 셀렉터가 사이드메뉴 배너 매칭main/shops/oliveyoung_global.js상품 0건 수집
D4inmyroom 리스트 0건main/shops/inmyroom.js활성 2건 실누락

이때 isCafe24SoldOut 수정 주석(d0665c6)이 보여주는 교훈이 하나 더 있다 — 과거의 "cafe24 admin-skin 품절 아이콘은 전부 false-positive" 라는 판단이 일부 표본의 과잉 일반화였음이 독립 실측으로 드러났다. 실제로는 그 아이콘이 변별력 있는 실신호였고(muvve 20/20·airmusic 4/4 실측 일치), 감사가 스크래퍼가 아니라 과거의 내 가정을 교정했다.

당시 유닛 테스트는 505개 그린 상태였다(현재 1,082개). 감사는 성공했지만, 한 가지가 남았다 — 이건 그날 스냅샷의 일회성 교정이었다.


3. 재발 위험 — 일회성 감사의 한계

d0665c6 는 그날의 정확성을 고쳤을 뿐이다. 감사 자체는 (a) 사람 + 에이전트 손이 많이 가고, (b) 라이브 사이트에 의존해 언제든 똑같이 재현하기 어렵고, (c) 다음에 누군가 파서를 건드리면 같은 결함이 조용히 되돌아온다.

필요한 건 "매번 돌릴 수 있는 회귀 가드" 였다. 그래서 일회성 감사를 상시 평가 하네스로 승격했다.


4. 설계 — 상시 평가 하네스 (PR #63, b23937a)

아이디어: 결정적 판정 로직(만료 / 품절 / 진행중, 기간 파싱, 키워드 분류)을 실데이터 골든셋에 재실행해 정확도를 수치로 낸다.

설계에서 세 가지가 이 하네스를 "믿을 수 있게" 만든다:

① 결정성 = 시계 고정
   만료 판정은 내부에서 new Date() 를 읽는다. 평가 중에는 골든셋의 evalNow
   (2026-08-05T03:00 KST)로 전역 시계를 고정한다(core.js installPinnedClock).
   → 언제 돌려도 같은 결과. 고정 안 하면 "시간이 지나 실제로 만료된 이벤트"가
      가짜 회귀로 잡혀 신호가 오염된다.

② AI 판정은 범위 밖
   하네스는 결정적 로직만 평가한다. Gemini 이미지 판정은 비결정적이라 회귀
   가드에 넣을 수 없다 — 별도 트랙으로 분리(정직한 경계).

③ 라벨을 코드에 맞추지 않는다
   골든셋 라벨은 진실 기준이다. 코드가 틀리면 라벨을 바꾸는 게 아니라
   "의도적 실패"로 남긴다 → 리포트가 곧 결함 목록이 된다.

회귀 모드: 직전 리포트와 케이스별로 대조해 "새로 깨진 케이스"를 표시하고, 있으면 exit 2. 하네스 자체 테스트 15개(시계 고정·비교기·회귀 diff·결정성)로 도구를 도구로 검증한다.

첫 실측 — 95.5% (107/112)

CLI 한 번으로 정확도가 나왔고, 실패 5건은 전부 하네스가 스스로 발견한 실제 결함이었다 (라벨을 코드에 맞췄다면 0건이 나왔을, 바로 그 함정을 피한 결과).


5. 결과 — 하네스가 결함을 잡고, 수정을 지킨다 (PR #64, 6875fbe)

발견된 5건:

수정은 원인이 공통 파서냐 샵별 설정이냐를 먼저 판별해 3계통으로(61e534a date-engine 열린범위 + 64e2e21 dailyduck 샵 플래그 + 4b7dcd7 게이트/테스트). 여기서 하네스가 회귀 가드로 작동했다 — 공통 파서를 고치자 같은 게이트를 지나는 다른 케이스 7건 (m2urecord·highlightshop)의 라벨도 바뀌어야 함이 드러났고(재핀), 라이브 스냅샷 1,347건 전수 A/B 스윕으로 "변경 54건이 전부 의도된 것, 의도 외 부수효과 0건" 을 증명했다.

정확도 추이 (evalNow 고정, 동일 골든셋)
  도입 첫 실측 ──────────────── 95.5%  (107/112)   ← 실패 5 = 하네스가 찾은 실결함
  결함 5건 수정 + 재핀 ───────── 100.0% (112/112)   ← 새로 깨짐 0
  현재 main (e77ef03) ───────── 100.0% (112/112)   ← 유닛 1,082 그린

일회성 감사(라이브 의존, 재현 어려움)가 1초짜리 회귀 가드가 됐다. 이제 누가 파서를 건드리면 npm run eval:golden 이 "무엇이 새로 깨졌는지"를 즉시 말한다.


6. 배운 것

  1. 자기 검증의 함정 — 검증은 감지와 다른 눈으로 해야 한다. 스크래퍼 셀렉터를 재사용한 검증은 같은 맹점을 공유한다. 화면 직독(판독 에이전트)이 그 눈이 됐다.
  2. 일회성 감사 < 상시 하네스 — 한 번 맞힌 것은 신뢰가 아니다. 재현성과 회귀 가드가 신뢰의 실체다. 감사의 가치는 "그날 고친 것"이 아니라 "매번 지키는 것"으로 승격될 때 완성된다.
  3. 결정성이 평가의 전제 — 시계·입력을 고정하지 않으면 "회귀"와 "시간 경과"를 구분할 수 없다. 평가 도구의 첫 번째 요건은 정확함이 아니라 같은 입력에 같은 답이다.
  4. 라벨을 코드에 맞추지 않기 — 골든셋이 코드의 거울이 되는 순간 어떤 결함도 못 잡는다. 진실 기준 라벨 + "의도적 실패" 가 하네스를 결함 탐지기로 만든다.
  5. LLM 을 검증의 도구로 쓰되 결정론으로 감싼다 — 감사에서 판독은 에이전트(사람 눈 대체), 판정은 결정적 로직. AI 는 관찰에, 결론은 재현 가능한 규칙에 둔다.

근거 (repo 대조)

주장근거
2026-07-02 감사 · 4계통 결함 당일 수정커밋 d0665c6, tasks/audit-accuracy-plan.md
독립 측정 도구scripts/audit-capture-listings.js, scripts/audit-compare.js
상시 하네스 · 골든셋 112케이스PR #63 (b23937a); scripts/eval-golden.js, scripts/eval/core.js, tests/golden/golden-set.json, docs/EVAL-HARNESS.md
첫 실측 95.5% (107/112)도입 시 npm run eval:golden 리포트
결함 5건 수정 → 100%PR #64 (6875fbe); 61e534a·64e2e21·4b7dcd7
현재 100% (112/112) · 유닛 1,082main e77ef03 에서 npm run eval:golden / npm test 재실행

초고 · 2026-08-05 KST. 공개 게시 전 개인정보·API 키 없음 확인.