Skip to content

탭 첫 진입 데이터 워밍 — "캐시 없으면 느린 탭 전환"에서 유휴 선요청까지 (2026-08-31)

  • 기간: 2026-08-31 (세션 1, 오너 보고 "탭들 사이에서 이동할때 캐싱되어있는거 없으면 반응이 너무 느린데 개선할 방법이 있을까?" → D1 "둘다 해줘")
  • 랜딩: PR #1014(f4c10dcf, Phase 1) — 마이그레이션 없음, Vercel 자동 배포, Actions 결제 블로커로 admin 머지
  • 설계서: 없음(이슈 #1013 본문이 정본 — 전수 데이터 경로 매핑 + Production RPC 실측 포함)
  • 정본: src/react/appController.tsx 탭 데이터 워밍 이펙트(주석 앵커 "탭 첫 진입 데이터 워밍(이슈 #1013")
  • 도구: Production RPC 실측 = Management API 읽기 프로브(jwt 주입·끝 raise 롤백)
  • 게이트: tests/react/tabDataWarming.test.mjs(계약 6 — 게이트·유휴 스케줄·순서·allowInactive·래치)
  • 버그리포트: 없음(성능 트랙)
  • 계약: 없음

Phase 현황

Phase내용상태
Phase 1유휴 데이터 워밍(일지·그룹·리포트)✅ PR #1014 (f4c10dcf)
Phase 2일지 대기열 수리❌ 불필요 판정 — 전면 로드는 큐 우회(코드 실측)
Phase 3카탈로그 낙관 시작➜ 지속 캐시 트랙으로 편입(이득이 콜드 캐시 한정)
Phase 4지속 캐시 확장(D2)➜ 별도 트랙(후속 이슈) — 리비전 정합 설계 동반

1. 배경

화면 코드는 부팅 유휴 시간에 전량 프리로드(2026-08-19)하지만 데이터는 홈·피드·PR 전광판만 부팅 병렬 프리페치였다. 일지(3왕복)·그룹(1왕복)·리포트(응답 최대 3MB)는 첫 진입 때 네트워크를 그대로 기다렸고, 재부팅 간 지속 캐시는 홈 스냅샷뿐이라 "캐싱 없으면 느리다"는 체감이 정확했다.

2. 문제 제기

탭별 첫 진입 대기가 데이터 로드 시점 정책의 비대칭에서 왔다

Production 실측(오너 계정): 홈 42ms/43kB·피드 52ms/38kB(부팅 선수신 — 빠름) vs 일지 18ms/40kB×3왕복·리포트 425ms/716kB·카탈로그 328ms/913kB(지연 로드). 서버는 대체로 빠르고, 체감 지연 = 왕복 횟수·페이로드 다운로드·로드 시점.

3. 해결 방안

원칙 (오너 결정, 2026-08-31)

  • D1 "둘다 해줘" — 리포트(볼륨) 워밍 포함(데이터 사용 증가 감수) + 지속 캐시 확장(D2)도 진행.
  • 워밍 규약은 코드 워밍과 동일: 유휴·순차·실패 조용히·중복은 기존 래치가 차단.

접근

  • 새 캐시 계층을 만들지 않고 기존 로더를 유휴 시간에 미리 한 번 호출 — 첫 진입 코드 경로가 그대로 캐시 히트가 된다. 달력은 allowInactive 탈출구(백필 리프레시가 쓰던 것) 경유.
  • 리포트를 마지막 순번으로(응답 최대), 일지 탭 활성 중엔 달력 워밍 생략(loadDay의 선택 날짜 요청 abort 방지).

4. 적용한 내용

Phase 1 — 워밍 이펙트 (#1014)

appController에 모바일 전용 유휴 이펙트: ready 후 requestIdleCallback(4s 타임아웃)으로 일지 이번달(allowInactive·background)+오늘 상세 → 그룹 목록(activateForGroupTab 래치 선점) → 볼륨(loadPrDashboard screen:volume, 진입 경로와 동일 모양·force 없음). 오너당 1회 래치, 취소 정리 포함.

주요 결정과 그 근거

  • 원계획 Phase 2 기각: "전면(보이는 달) 요청이 ±1 프리페치 큐에 밀린다"는 가설은 코드 실측으로 반증 — loadMonth 직접 호출은 ensureMonth 큐(동시 2)를 타지 않는다.
  • 원계획 Phase 3 축소: 카탈로그는 캐시 워밍 시 이미 네트워크 0(셸 후 localStorage 히트) — 콜드 설치·버전 범프의 셸 뒤 줄서기만 남고, 이는 D2 트랙에서 리비전 정합(homeFragmentExpectation의 셸 결합)과 함께 풀어야 안전.

작업 중 드러난 것

  • get_pr_overview는 서버 적립형(user_exercise_pr_events — 저장 시 계산해 적립, 조회는 읽기만 44ms). 기기 미저장이라 부팅마다 287kB 재수신 — 부팅 체감의 몸통, D2 대상.
  • 워밍으로 그룹 목록의 "첫 진입 = 신선" 의미가 "부팅 시점 신선"으로 이동 — 종전에도 첫 진입 후 래치라 등가.

5. 적용 결과

항목결과
일지 첫 진입3왕복 대기 → 워밍 완료 시 캐시 히트(0왕복)
그룹 첫 진입1왕복 → 0왕복
리포트 첫 진입716kB~ 다운로드 대기 → 0 (부팅 유휴에 선수신)
테스트전체 2,246건 통과(신규 6) · tsc·경계·unused·manifest 통과
eslint·CI미검증 — Actions 결제 블로커 지속(오너 선례 지시 admin 머지)
실기기 체감시각 정보 항목(§14) — 워밍 실패 시에도 기존 경로 그대로라 기능 위험 없음

6. 이번 개선으로 향상된 것

탭 전환이 "받아둔 것을 여는" 동작이 됐다

코드 워밍(8/19)과 데이터 워밍이 짝을 이뤄, 하단 탭 전부가 부팅 직후부터 즉시 반응. 구조적으로 남는 것: 워밍 규약(유휴·순차·조용한 실패·래치 위임) + 계약 테스트.

남은 것

  • D2 지속 캐시 확장 — 후속 이슈로 진행(오너 승인): PR 오버뷰·일지 이번달·그룹을 기기에 남겨 부팅 즉시 그리기. 리비전 정합(셸 적용 순서)·신선도 규칙 설계 동반. 볼륨(최대 3MB)은 지속 저장 제외 권장.
  • GitHub Actions 결제/한도 복구(오너) — 전 PR CI 공통 블로커.