2026-09-19 밤 답안 뭉침 해소, 클리어 화면 평가 수집, 그림 WebP 전환

저녁 — 그림 75장 PNG→WebP 전환, 화면 전환 전 선로딩 (19:30~19:41, 미커밋)

출발은 09-18 밤 실플레이의 사용자 체감 “장면 넘어갈 때 사진이 늦게 뜬다”였다. 모델 지연(planner 10초)은 운영 전환 뒤 코드 동결이라 손댈 수 없지만 그림 로딩은 프론트만의 문제라 오늘 처리했다. 프론트는 Codex astra, 컨트롤러가 변환·검증.

  • 규모 — public/assets의 PNG 75장(장면 C·E·N군, 단서 clues/ 22장 포함)을 표시 해상도에 맞게 리사이즈한 WebP로 교체. 원본 PNG는 한 장에 2.3~4.65MB(최소 0.23MB), 합계 약 248MB → WebP 75장 합계 20MB(du). 단서 그림 한 장이 4MB라 “보임과 디코딩 완료 사이에 틈”이 있다고 테스트 주석에 적혀 있던 문제의 원인이기도 했다.
  • 경로 한 곳 — imageMap.ts의 img()가 .png 대신 .webp를 만들고, 단서 표 20개와 결말 closure 그림 경로를 같이 바꿨다. 추적 파일 diff 96개(+151/−52, 그 중 75개는 PNG 삭제), 새 파일 preloadImages.ts 1개 + WebP 75장은 아직 untracked.
  • 선로딩(preloadImages) — 14줄짜리 유틸. 이미 데운 경로는 Set으로 걸러 한 번만 new Image()로 브라우저 캐시에 올린다. 다음 화면이 정해져 있는 지점에서만 부른다: 랜딩(진입 배경·1회차 아침·로고) → 진입 화면(회차별 아침 0·1단계, 3단계는 4회차, 낮 1장면 묶음, 인물 초상 4장) → 아침(낮 1장면 묶음) → 낮 SceneIntro(현재 장면 묶음 + 다음 장면 + 그 장면의 단서 그림) → 낮이 끝나면 밤 배경 → 밤(밤 배경·신 그림) → 파멸 전환(결말 그림 + 글리치 단서). dayWarmImages(beat)가 장면 그림·관리자·원숭이손·초상 4장을 한 묶음으로 돌려준다.
  • 브라우저 힌트 — 전체 화면 배경 <img> 6곳(랜딩·진입·아침·낮 장면·밤·파멸)에 decoding="async"·fetchPriority="high", 소개 카드 초상과 글리치 띠는 decoding="async"만.
  • 캐시 헤더 — next.config.ts에 /assets/:path* → Cache-Control: public, max-age=31536000, immutable. 파일명이 ID 고정이라 내용을 바꾸면 이름을 바꿔야 한다는 뜻이기도 하다(v1·v2 접미 관행 유지).
  • 헤드리스 기대값 — 다섯 스크립트(connected-investigation·day-screen-vn·five-loop-flow·scene-illustrations·voice-transitions)의 src$=".png" 단언을 .webp로.
  • 검증 — 세션 기록에는 scene-illustrations·day-screen-vn·gameplay-clarity 통과로 남아 있다. 이 일지를 쓰는 시점(23:45)에 tsc·헤드리스 재실행과 로컬 서버 확인은 권한 문제로 하지 못했다 — 커밋 전에 한 번 더 돌려야 한다.
  • 남은 것 — 커밋 미완(사용자 확인 대기). 루트에 09-18 스크린샷 PNG 1장이 untracked로 남아 있다(작업 산출물 아님, 정리 대상).

오전 — 클리어 화면 플레이 평가 수집 (09:30~10:25)

사용자 요청: 5회차를 마친 화면에서 다섯 항목 별점을 받아 DB에 쌓고, 나중에 리서치 사이트에서 완주율·투표참가율·항목별 평점·전체 평점을 보이게 한다. 수집만 오늘 넣고 표시는 나중에 — 리서치 저장소는 코드 프리즈 대상이 아니라서 급하지 않다.

브레인스토밍으로 6건을 먼저 확정했다: 저장은 새 테이블, 자유 의견란은 없음, 부분 응답 허용, 설문을 먼저 보이고 건너뛰기도 같은 크기, 집계는 사람 판만, 오늘은 수집만. 설계서 specs/2026-09-19-clear-survey-design.md, 계획 plans/2026-09-19-clear-survey.md.

  • 스키마 — 새 테이블 survey_votes 하나. attempts와 1:0..1(판당 한 표를 유니크 제약이 보장), users와 1:N. 점수 다섯 칸은 1~5 검사 제약에 NULL 허용(부분 응답), 건너뛰기는 skipped 행으로 남겨 참가율을 정확히 잰다. 기존 테이블은 하나도 바뀌지 않았고 외래키는 이 저장소 관행대로 걸지 않았다. 마이그레이션 216f290d03b3.
  • API — POST /attempts/{attempt_id}/survey, 기존 owned 라우터라 로그인·판 소유자 확인을 그대로 물려받는다. 중복 투표는 오류가 아니라 {recorded: false}로 첫 표를 지킨다.
  • 화면 — 결말 → 설문 → 회고·다시 시작 3단계. 별점 다섯 줄과 “평가 보내기”·”건너뛰기”를 같은 크기로 나란히. 요청이 실패해도 다시 시작이 열린다 — 평가가 게임을 막으면 안 된다.
  • 검토에서 잡은 것 — ① 건너뛰기로 보내면서 별점을 같이 보내면 DB 제약에 걸려 500이 나던 경로(Critical) ② 제약 위반과 오타를 구분 못 하던 느슨한 테스트 단언 ③ 중복 투표 때 첫 표가 살아남는지 확인하지 않던 테스트 ④ 새 라우트를 소유권 인벤토리에 등록하지 않아 fail-closed 검사가 빨갛던 것 ⑤ 설문 단계가 생기면서 클리어 화면을 지나가던 기존 헤드리스 2종이 막힌 것.
  • 검증 — 백엔드 1059 passed, import 계약 4 kept, tsc 클린, 헤드리스 12종 중 11 통과(dev-login은 개발 로그인을 꺼 둔 상태라 의도된 실패). 서버 재기동 후 운영 설정 그대로(Sonnet 5/Haiku 4.5, 구글 로그인만), 새 경로는 비로그인에 401.
  • 최종 리뷰 반영(a154752, 10:35) — 전체 리뷰 Important 1건: 평가 전송 요청이 오래 걸리면 “다시 시작”이 그만큼 늦게 열렸다. Promise.race로 5초를 넘기면 전송 결과와 무관하게 진행하고, 보낸 뒤 감사 한 줄을 보인다. clear-survey.cjs에 응답이 영영 안 오는 경우를 추가(+30줄). 범위 재검토에서 새 결함 없음.
  • 커밋·배포 — 09:28 설계서부터 10:35까지 커밋 12건. 브랜치 푸시 뒤 main을 a154752로 올렸다(origin/main이 이 커밋을 가리키는 것을 확인). Vercel 반영은 세션 기록상 10:44 완료.
  • 남은 것 — 리서치 사이트 집계 페이지. 표가 쌓인 뒤 별도 저장소에서 만든다. 필요한 정보는 판과 표에 이미 다 있다.

아침 — 9번째 문장부터 줄글로 뭉치던 확인 화면 (사용자 보고 08:0x)

사용자: “8개까지는 문장별로 정리가 돼서 나오고, 나머지들은 한 칸 안에 그냥 나열된다. 나머지도 문장별로 정리되게 해 달라.”

  • 원인 — source_claims가 8칸 상한을 두고 claims[:7] + [" ".join(claims[7:])]로 초과분을 마지막 칸에 뭉쳤다. 사용자 판(8fc30a72) 네 번의 밤이 모두 상한에 걸렸고 4회차 마지막 칸은 9문장·436자였다.
  • 점수에는 영향이 없었다 — judge_candidates가 채점 직전에 칸을 다시 문장으로 풀어 후보를 만들어 왔다. 이번 수정도 같은 이유로 점수 규칙을 바꾸지 않는다(회귀 테스트로 고정: 옛 7칸+뭉친 칸과 새 분할의 채점 후보가 동일).
  • 수정 — 상한을 도메인 상수 MAX_CLAIMS = 40으로 올려 game_constants에 두고 use case·이벤트 DTO·라우터가 함께 쓴다. 자유서술이 2000자 상한이라 보통 길이 문장으로는 닿지 않아 실제로는 뭉치지 않는다. 마침표 없이 줄만 바꾼 문장도 이제 각자 칸을 갖는다(옛 뭉친 칸에서는 다시 나뉘지 않았다).
  • 프론트(Codex astra) — 편집 저장의 slice(0, 8)를 40으로, 안내를 “한 줄이 주장 하나다. 최대 40줄.”로, 목록 아래 캡션을 “주장은 최대 8개까지만 세계에 닿는다.”(채점이 이미 문장별로 이뤄져 사실과 달랐다) → “문장 하나하나가 따로 판정된다.”
  • 문서 — api_contract.md(≤40·422 상한), 기획서 정본 v9.0 §7.2 정리 단계·§10 정답 답안 표·부록 프롬프트 + 패치 기록 한 줄.
  • 검증 — 백엔드 1041 passed(신규 test_source_claims.py 10건 포함), import 계약 4 kept, tsc, 헤드리스 11종 중 10 통과(dev-login은 개발 로그인을 끈 상태라 의도된 실패). 백엔드 재기동 후 설정 그대로(anthropic Sonnet 5/Haiku 4.5, 구글 로그인만) + MAX_CLAIMS 40 확인.