렌더 입자성 캠페인 Phase 1 — 핫패스 격리
통합 기록(형식 표준, Phase 현황·결과·남은 것): 2026-08-24-render-granularity.md
- 날짜: 2026-08-23 (Phase 0 #604 직후, 오너 결정 D1 (가))
- 목표: 바인딩을 새로 짓지 않고 스토어 쪽에서 불필요한 통지를 끊어 "라이브 운동 세트 편집 → 조립 루트 재실행 0"을 달성한다. renderCtx 키 계약은 유지,
workoutSnapshot의 의미만 "렌더 시점 최신값"으로 바뀐다.
바뀐 것
| 항목 | 파일 | 내용 |
|---|---|---|
| Phase 1-1 emit 위생 | activeWorkoutCommands.ts | setWorkoutEditorLocked가 잠금 불변이면 같은 인텐트 객체를 반환 |
| Phase 1-1 emit 위생 | workoutDraftStore.ts | set()이 동일 값(Object.is) 패치는 통지 생략 |
| Phase 1-2 스냅샷 비반응화 | workoutDraftStore.ts · workoutDraftController.ts | workoutSnapshot을 반응 상태에서 제거, workoutSnapshotRef 단일 저장. commitWorkoutSnapshot은 ref 기록만. 어댑터는 렌더 시점 ref 읽기로 같은 키를 제공 |
| Phase 1-3 이벤트 시점 소비자 | mobileApp.tsx · workoutWriteController.ts · appController.tsx | leaveWorkoutForTab·discardActiveWorkout이 ref를 읽음. write env에서 workoutSnapshot 값 제거, renderCtx에 workoutSnapshotRef 추가 |
| Phase 1-4 appController 부속 | appController.tsx | nav 미러에서 스냅샷 제거 + flow 복원(#338에서 빠졌던 topOpenLayer의 읽기 키), 복원 가드는 ref 읽기 |
검증
- 행동 계약:
workoutDraftStoreGranularity.test.mjs7계약 — 편집 중 휘발/내구 발행 통지 0 (ref는 최신), 동일 값 setter 통지 0·변경 시 1, 제목 변경 발행 통지 1, 잠금 전이 통지· 불변 시 인텐트 객체 유지, 스냅샷 커밋은 ref만, 인텐트 전이는 통지. 기존workoutDraftStore.test.mjs5계약 재조준(스냅샷 단언 → ref). - 소스 슬라이스:
controllerBoundaries.test.mjs커밋 슬라이스를 "ref 기록 + set/emit 부재"로 재조준(pending-changes 기존 항목에 사유 병합). - 전 체인
npm run check1771/1771 ·check:unused·check:dual-key초록. - 로컬 스택
test:e2e-local8/8 · 브라우저 회귀 CASE-001·011·015 3/3(프로덕션 빌드).
전후 계측 (Phase 0-2 계측기, 개발 React · CPU 4×)
| 상호작용 | 루트 커밋 전 → 후 | 커밋 전 → 후 | 커밋 시간 합 전 → 후 |
|---|---|---|---|
| ① 세트 입력 1회 (×10) | 2 → 0 | 3 → 1 | 77~137 ms → 3~8 ms |
| ① 세트 행 추가 | 2 → 0 | 3 → 1 | 98 → 7 ms |
| ① 세트 완료 체크 | 2 → 0 | 3~5 → 1~3 | 108~129 → 9~19 ms |
| ① 운동 시작 | 3 → 2 | 5 → 4 | 205 → 147 ms |
| ② 달력 이전 달(미적재) | 6 → 6 | 6 → 6 | 불변(범위 밖) |
| ③ 로그테이블 셀 열기 | 2 → 2 | 4 → 4 | 불변(범위 밖) |
남은 커밋 1회는 WorkoutFlow 자체의 state 렌더(화면이 원천)다. 편집 중 탭 이탈 persist· 취소·저장이 최신 스냅샷을 쓰는 것은 ref 전환 + CASE-001·011·015로 확인했다.
구조적으로 남는 것
- 통지 수 계약 — "콘텐츠 발행은 구독자를 깨우지 않는다"가 스토어 행동 테스트로 잠겼다.
- 핫 키 규칙 — env/ctx에 고빈도 값은 값이 아니라 ref·getter로 넘긴다. (a) 캠페인의 setEnv 렌더 주입 관용구는 "루트가 모든 변화에 재렌더한다"에 기대고 있었고, 입자성은 바로 그 전제를 지운다. 같은 키를 렌더 값으로 다시 싣는 PR은 이 규칙 위반이다.
다음
- D2(중단 기준선) 재상정: 시나리오 ②(미적재 월 이동 루트 커밋 6)·③(셀 열기 2)의 Phase 2~4 진행 여부. D4(Phase 5 ui/** 방침)는 캠페인 마지막에 리마인드.