하단 네비게이션 최적화 해보기

안승찬·2026년 5월 25일

Next

목록 보기
4/4

안녕하세요.

사내 B2C WebView에서 서브 4탭 하단바전역 5탭 하단바를 다루면서, 체감 성능·애니메이션 일관성·초기 로드 부담을 맞추는 작업을 정리했습니다.


개요

구분내용
환경Next.js App Router + Flutter WebView

하단 네비는 두 갈래입니다.

  • 전역 5탭: 앱 전체에서 쓰는 메인 하단바 (홈, 금 모으기, MY 금고, 내역, 금거래소 등)
  • 금 모으기 4탭: collect-gold 탭 쉘 안에서만 쓰는 전용 하단바 (홈, 친구적립 + coming soon 2개)

같은 “하단바”처럼 보여도 라우팅 모델이 달라서 prefetch·애니메이션·gate 처리 전략을 나눴습니다.


하단 네비 구조

1) 전역 5탭 — 컴포넌트·데이터 흐름

탭 구성 (좌 → 우)
홈 · 금 모으기 · MY 금고 · 내역 · 금거래소 (실제 라벨/아이콘은 서비스 스펙 기준)


2) 금 모으기 4탭 — keep-alive

탭 클릭 시 (같은 쉘 · URL만 변경)

  [홈] ──Link──▶  /collect-gold
  [친구적립] ──▶  /collect-gold/friend-referral

        CollectGoldTabPanelHost
              │
    ┌─────────┴─────────┐
    ▼                   ▼
  home 패널          friend 패널
  display:block      display:none   ← 한쪽만 visible (hidden 클래스)

  하단 CollectGoldBottomNav 는 리마운트 없음 (레이아웃에 1회만 마운트)


3) Prefetch 3단계 (공통 훅)

  하단바 mount
       │
       ├─① Link prefetch={false}     ← 즉시 5탭 동시 로드 ❌
       │
       ├─② idle (requestIdleCallback)
       │      전역: / 와 /collect-gold 만 (현재 탭 제외)
       │      금모으기: /collect-gold, 친구적립 href
       │
       └─③ onPointerEnter / onTouchStart
              손·마우스 올린 탭만 router.prefetch(href)

아키텍처 차이 — 왜 전략이 다른가

금 모으기 4탭전역 5탭
라우팅같은 탭 쉘 안 keep-alive (CollectGoldTabPanelHost) — URL만 바뀌고 패널은 hidden 토글탭마다 풀 라우트 전환 (별도 page.tsx 트리)
하단 UICollectGoldBottomNavBottomNavigation + BottomNavGateClient
prefetch 부담4탭·같은 쉘 → 상대적으로 가벼움5개 대형 라우트 동시 prefetch 시 초기 부담이 크게 드러남
입장 애니 scopecollectGoldglobal

1. 아이콘 입장·재탭 활성화 애니메이션

목표

  • 아이콘만 스태거 scale 입장 (라벨은 고정)
  • 세션당 입장 애니 1회만 재생
  • 활성 탭 재탭 시 아이콘 pulse (햅틱 포함)

공통 모듈

파일역할
BottomNavIconEntrance.tsxFramer Motion scale, scope: global / collectGold
useBottomNavIconEntranceOrchestration.ts입장 1회·탭 전환·재탭 pulse 오케스트레이션
bottomNavIconEntrance.const.tsstagger·duration 상수
bottomNavEntranceSession.tsxProvider (전역 / 금 모으기 분리)

금 모으기는 CollectGoldBottomNav.tsx + collectGoldBottomNavEntranceSession.storage.ts(sessionStorage 키 분리).
전역은 BottomNavigation.tsx + bottomNavEntranceSession.storage.ts.

두 하단바가 같은 UX를 내면서도, 입장 애니가 서로 간섭하지 않도록 scope와 storage를 나눴습니다.


문제

withViewTransition + router.push 조합이 App Router 클라이언트 전환과 맞지 않아 지연·깜빡임이 났습니다.
prefetch를 전 탭에 켜 두면 마운트 시 5개 대형 라우트가 한꺼번에 선로딩됩니다.

조치

  • 탭 이동을 Next.js Link 기본 네비로 통일 (scroll={false} 유지)
  • View Transition + imperative router.push 제거

브라우저·WebView가 익숙한 네비게이션 경로로 돌려서, “눌렀는데 늦게 바뀌는” 느낌을 줄였습니다.


3. 절충형 prefetch 전략

원칙

  1. Link prefetch={false} — 마운트 시 모든 탭 동시 선로딩 방지
  2. 의도 prefetchonPointerEnter / onTouchStart에서 router.prefetch(href)
  3. idle 선로딩requestIdleCallback (미지원 시 1.5s timeout) 이후 고빈도 탭만 1~2개
  4. 중복·현재 탭 스킵Set으로 이미 prefetch한 href 제외, 활성 탭은 생략

구현

파일설명
useBottomNavRoutePrefetch.ts공통 훅
bottomNavPrefetch.config.ts전역 idle: /, /collect-gold
CollectGoldBottomNav.tsxidle: /collect-gold, 친구적립 href
BottomNavigation.tsx전역 5탭 연동

idle 대상 요약

네비idle prefetch호버/터치만
전역 5탭홈, 금 모으기MY 금고, 내역, 금거래소 등
금 모으기 4탭홈, 친구적립(coming soon 탭 제외)

“prefetch를 끄면 느리고, 켜면 첫 화면이 무거운” 딜레마를, idle + 의도 두 단계로 나눠 풀었습니다.


4. 금 모으기 탭 쉘 (데이터·레이아웃)

하단바 최적화와 별도로, 탭 쉘은 서버 prefetch로 첫 진입 데이터를 맞춥니다.

파일역할
collect-gold/(tab-shell)/layout.tsx탭 쉘 레이아웃
prefetchCollectGoldTabShell.tsReact Query prefetch
CollectGoldTabPanelHost.tsxkeep-alive 패널 호스트

재방문 시에는 이미 마운트된 패널을 hidden으로 토글하므로, 전역 5탭 대비 전환 체감이 가볍습니다.


6. 테스트

테스트검증 내용
useBottomNavRoutePrefetchintent / 현재 탭 스킵 / idle prefetch
BottomNavigationhover 시 router.prefetch
CollectGoldBottomNavLink prefetch={false}, scroll={false}
useBottomNavIconEntranceOrchestration입장·재탭 pulse

마무리 · 향후

  • 전역 탭도 부분 keep-alive 또는 parallel route 검토는 범위가 커서, 이번에는 prefetch·gate·애니 공통화에 집중했습니다.
  • WebView + App Router 조합에서는 “보이는 것(하단바)”과 “로드되는 것(prefetch)” 을 분리해서 설계하는 편이 체감에 가장 잘 먹혔습니다.

0개의 댓글