Skip to content

perf(map): stabilize real-data map rendering#10

Merged
seoJing merged 6 commits into
mainfrom
feature/spot-api-contract-sync
May 28, 2026
Merged

perf(map): stabilize real-data map rendering#10
seoJing merged 6 commits into
mainfrom
feature/spot-api-contract-sync

Conversation

@seoJing
Copy link
Copy Markdown
Member

@seoJing seoJing commented May 28, 2026

Summary

  • Smooth SPOT simulation playback by skipping quiet tick gaps, staggering crowded starts visually, and hiding agents after returning home
  • Stabilize real-data map rendering with keepPreviousData feed queries, rounded map-feed coordinates, viewport-based marker simplification, and reused first-page data for the bottom sheet
  • Refresh map tutorial copy/spotlight placement to match the actual marker/card-deck/control flow

Test Plan

  • pnpm test -- src/features/feed/model/map-feed-card-pager.test.ts src/features/map/model/marker-visual-mode.test.ts src/features/simulation/model/sim-clock.test.ts
  • pnpm lint — passes with existing warnings only
  • pnpm build
  • Notjing Final Gate pass after fixing initialItems pagination blocker

Notes

  • No backend/API mutation smoke test was run; this PR is frontend rendering/performance behavior only.

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 위치 기반 피드 검색 기능 추가 (근처 좌표로 필터링)
    • 피드 목록의 무한 스크롤 로딩 지원
    • 지도 튜토리얼 및 도움말 시스템 추가
    • 지도 줌 레벨과 마커 밀도에 따른 마커 표시 최적화
  • 개선 사항

    • 로그인되지 않은 사용자의 인증 처리 흐름 개선
    • 피드 하단 시트의 로딩 상태 및 표시 UI 개선

Review Change Stack

@vercel
Copy link
Copy Markdown

vercel Bot commented May 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
frontend Ready Ready Preview, Comment May 28, 2026 7:36pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

Warning

Review limit reached

@seoJing, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 44 minutes and 13 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0593d2af-22cd-4d93-ba56-f8348077a7ad

📥 Commits

Reviewing files that changed from the base of the PR and between ca5b368 and 1ff1de7.

📒 Files selected for processing (2)
  • src/features/feed/ui/MapFeedCardPager.tsx
  • src/features/map/ui/MapTutorialOverlay.tsx
📝 Walkthrough

Walkthrough

이 PR은 피드 목록을 무한스크롤로 전환하고, 맵에 튜토리얼 오버레이 및 마커 시각 모드를 추가하며, 시뮬레이션을 증분 청크 로딩 방식으로 개선합니다. API 인증 처리를 유연화하고 보호 라우트를 축소합니다.

Changes

피드, 맵, 시뮬레이션 통합 개선

Layer / File(s) Summary
피드 페이지네이션 API 계약 및 쿼리 파라미터
src/features/feed/api/feed-api.ts, src/lib/client-api.ts, src/lib/client-api.test.ts
FeedListParamsnearLat/nearLng 선택 파라미터 추가, feedApi.list/get에서 redirectOnUnauthorized: false, retryUnauthenticatedOnUnauthorized: true 옵션 적용
Feed 무한 페이지네이션 React Query 훅
src/features/feed/model/use-feed.ts
infiniteList 쿼리 키, DEFAULT_FEED_PAGE_SIZE, getNextFeedPageParam 도입, useFeedList 옵션 확장, useInfiniteFeedListuseLayerAwareInfiniteFeedList 추가
FeedBottomSheet 무한스크롤 구현
src/features/feed/ui/FeedBottomSheet.tsx
useLayerAwareInfiniteFeedList로 전환, IntersectionObserver + 스크롤 감지로 다음 페이지 요청, initialItems/initialTotalCount/isInitialItemsLoading 주입 옵션 추가
Promoted 카드 가시 범위 계산 및 즉시 제거
src/features/feed/model/map-feed-card-pager.ts, src/features/feed/model/map-feed-card-pager.test.ts
MAX_PROMOTED_FEED_CARDS = 7, getVisiblePromotedCardRange() 유틸 추가, instantDismissIds로 overflow 카드 즉시 퇴장
MapFeedCardPager 외부 데이터 주입 및 튜토리얼
src/features/feed/ui/MapFeedCardPager.tsx
items, isInitialLoading, onTutorialCardPromote, onTutorialCardDismiss props 추가, getVisiblePromotedCardRange 기반 가시 구간 계산
맵 튜토리얼 오버레이 및 마커 시각 모드
src/features/map/model/marker-visual-mode.ts, src/features/map/model/marker-visual-mode.test.ts, src/features/map/ui/MapTutorialOverlay.tsx
MarkerVisualMode 타입, decideMarkerVisualMode(), shouldRenderPersonaDots() 함수, MAP_TUTORIAL_STEPS 상수 정의
ClusterBlob 간단/전체 렌더링 모드
src/features/map/ui/ClusterBlob.tsx
visualMode?: 'full' | 'simple' prop 추가, simple 모드는 원형, full 모드는 gooey SVG 마커 조건부 렌더링
MapClient 튜토리얼 상태 및 피드 파라미터 통합
src/features/map/client/MapClient.tsx
튜토리얼 상태 관리, 중심 좌표 기반 mapFeedParams, viewportMarkerCount + getMarkerVisualMode 계산, 시뮬레이션 tickDurationMs 2500→3500, MapTutorialOverlay 렌더링
MapFooter 도움말 버튼 + MapV3Canvas center/zoom 콜백
src/features/map/ui/MapFooter.tsx, src/features/map/ui/MapV3Canvas.tsx
onOpenHelp 콜백 prop, onCenterChangeAction/onZoomChangeAction callbacks 추가
FeedParticipationActions 로그인 리다이렉트
src/features/feed/ui/detail/FeedParticipationActions.tsx, src/features/feed/ui/detail/FeedParticipationActions.test.tsx
역할 선택 시 미로그인 사용자를 /login?next=...로 리다이렉트, usePointBalance 조건화
clientApiFetch 인증 처리 옵션 체계
src/lib/client-api.ts, src/lib/client-api.test.ts
ClientApiFetchOptions 타입 정의, redirectOnUnauthorized/refreshOnUnauthorized/retryUnauthenticatedOnUnauthorized 플래그로 401 처리 제어
시뮬레이션 API 무인증 리다이렉트 비활성화
src/features/simulation/api/sim-api.ts
fetchSimManifest/fetchSimMovements/fetchSimLifecycle 모두 redirectOnUnauthorized: false 옵션 추가
sim-clock 타임라인 및 위치 헬퍼
src/features/simulation/model/sim-clock.ts, src/features/simulation/model/sim-clock.test.ts
findNextMovement(), positionAt 옵션, resolveAgentPosition 산포, BuildAgentTimelinesOptions 시각 분산 로직 추가
useSimRun 증분 청크 로딩 및 버퍼
src/features/simulation/model/use-sim-run.ts
bufferedMovementsRef/bufferedLifecycleEventsRef/bufferedChunkVersion, spawnScatterM, skipEmptyEventTicks 옵션 추가, loadChunk 중복 제거 및 순서화, 증분 타임라인 빌드
useSimDomain 버퍼 기반 seed 계산 및 스냅샷 스로틀
src/features/simulation/model/sim-domain-adapter.ts
bufferedChunkVersion 기반 useMemo seed 계산, snapshotThrottleMs 스로틀, signature 기반 중복 회피
보호 라우트 목록 축소
src/proxy.ts
PROTECTED_ROUTES에서 /feed, /map, /spot 제거, ['/admin-post', '/chat', '/my', '/pay', '/post']만 유지

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • spot-platform/frontend#2: MapFeedCardPager.tsx의 exit/transition 로직과 AnimatePresence 정리 처리에서 exitOverride 관련 변경이 직접 연결됨
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.24% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목 'perf(map): stabilize real-data map rendering'은 주요 변경사항인 실제 데이터 맵 렌더링 안정화 및 성능 최적화를 명확하고 간결하게 요약한다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/spot-api-contract-sync

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/features/feed/ui/FeedBottomSheet.tsx (1)

91-100: 💤 Low value

pageSizefeedListParams의 메모 의존성에 포함되어 있어, initialItems.length가 변경될 때마다 쿼리 키가 변경됩니다.

initialItems가 맵 뷰포트에서 제공될 경우, 길이가 자주 변할 수 있으며 이로 인해 불필요한 쿼리 재실행이 발생할 수 있습니다. size 파라미터를 고정값으로 분리하거나, 쿼리 키에서 size를 제외하는 것을 고려해볼 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/feed/ui/FeedBottomSheet.tsx` around lines 91 - 100, The memo for
feedListParams includes pageSize in its dependency array which causes the query
key to change when initialItems.length varies; update feedListParams (the
useMemo block that defines feedListParams) to remove pageSize from the
dependency array and instead supply a fixed size value (e.g., a constant or a
separate constant variable) for the size property so that changes to
initialItems.length don't retrigger the memo/queries — adjust only the
dependencies and the size assignment in feedListParams, leaving feedType and
categories dependencies intact.
src/features/simulation/model/use-sim-run.ts (1)

298-352: 💤 Low value

lifecycle 이벤트 순회 시 성능 고려.

findNextActivityTick에서 lifecycleByTickRef.current.keys()를 순회하면서 다음 활성 tick을 찾고 있습니다. lifecycle 이벤트가 많을 경우 매 quiet period마다 O(lifecycle_events) 비용이 발생합니다.

현재 구현에서는 quiet period에만 호출되므로 실제 부하는 제한적이지만, 이벤트 수가 많아지면 정렬된 tick 배열을 캐시하고 이진 탐색을 사용하는 것이 효율적일 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/simulation/model/use-sim-run.ts` around lines 298 - 352,
findNextActivityTick currently scans lifecycleByTickRef.current.keys() linearly
causing O(n) work each quiet-period; fix by maintaining a cached sorted array of
lifecycle ticks (e.g. lifecycleTicksCache) and use binary search to find the
first tick > tFloat inside findNextActivityTick, and update/invalidate the cache
whenever lifecycleByTickRef is mutated (ensure cache is refreshed where
lifecycle events are added/removed). Update references in
maybeSkipEmptyEventTicks to rely on the new binary-search-backed
findNextActivityTick so quiet-period checks become O(log n).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/features/feed/ui/MapFeedCardPager.tsx`:
- Around line 119-123: The current setPromotedCount(safePromoted + 1) call in
MapFeedCardPager is using a stale render-time value (safePromoted) and can drop
increments under rapid interactions; change it to use a functional state update
(e.g., setPromotedCount(prev => prev + 1)) so increments are accumulated
reliably, and apply the same fix to the other occurrence around the
onTutorialCardPromote branch (the block that checks overflowItem and the similar
block at 131-134) to ensure consecutive taps/swipes are not lost.

In `@src/features/map/ui/MapTutorialOverlay.tsx`:
- Around line 89-94: The current logic around safeIndex/step assumes steps is
non-empty and will set step to undefined for an empty steps array causing
runtime errors; modify the top of the MapTutorialOverlay render (or the
component body where safeIndex/step are computed) to early-return (e.g., return
null or a no-op UI) when steps.length === 0 so you never compute safeIndex or
access step.id; keep the rest of the logic (safeIndex, isFirst, isLast,
showSelectedMarker) unchanged and only run it after confirming steps is
non-empty.

---

Nitpick comments:
In `@src/features/feed/ui/FeedBottomSheet.tsx`:
- Around line 91-100: The memo for feedListParams includes pageSize in its
dependency array which causes the query key to change when initialItems.length
varies; update feedListParams (the useMemo block that defines feedListParams) to
remove pageSize from the dependency array and instead supply a fixed size value
(e.g., a constant or a separate constant variable) for the size property so that
changes to initialItems.length don't retrigger the memo/queries — adjust only
the dependencies and the size assignment in feedListParams, leaving feedType and
categories dependencies intact.

In `@src/features/simulation/model/use-sim-run.ts`:
- Around line 298-352: findNextActivityTick currently scans
lifecycleByTickRef.current.keys() linearly causing O(n) work each quiet-period;
fix by maintaining a cached sorted array of lifecycle ticks (e.g.
lifecycleTicksCache) and use binary search to find the first tick > tFloat
inside findNextActivityTick, and update/invalidate the cache whenever
lifecycleByTickRef is mutated (ensure cache is refreshed where lifecycle events
are added/removed). Update references in maybeSkipEmptyEventTicks to rely on the
new binary-search-backed findNextActivityTick so quiet-period checks become
O(log n).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 46abf6a2-4814-47ab-b3fa-c92a4a179dd8

📥 Commits

Reviewing files that changed from the base of the PR and between 7195354 and ca5b368.

📒 Files selected for processing (25)
  • src/features/feed/api/feed-api.ts
  • src/features/feed/api/feed-server-api.ts
  • src/features/feed/model/map-feed-card-pager.test.ts
  • src/features/feed/model/map-feed-card-pager.ts
  • src/features/feed/model/use-feed.ts
  • src/features/feed/ui/FeedBottomSheet.tsx
  • src/features/feed/ui/MapFeedCardPager.tsx
  • src/features/feed/ui/detail/FeedParticipationActions.test.tsx
  • src/features/feed/ui/detail/FeedParticipationActions.tsx
  • src/features/map/client/MapClient.tsx
  • src/features/map/model/marker-visual-mode.test.ts
  • src/features/map/model/marker-visual-mode.ts
  • src/features/map/ui/ClusterBlob.tsx
  • src/features/map/ui/MapFooter.tsx
  • src/features/map/ui/MapTutorialOverlay.tsx
  • src/features/map/ui/MapV3Canvas.tsx
  • src/features/pay/model/use-pay.ts
  • src/features/simulation/api/sim-api.ts
  • src/features/simulation/model/sim-clock.test.ts
  • src/features/simulation/model/sim-clock.ts
  • src/features/simulation/model/sim-domain-adapter.ts
  • src/features/simulation/model/use-sim-run.ts
  • src/lib/client-api.test.ts
  • src/lib/client-api.ts
  • src/proxy.ts

Comment thread src/features/feed/ui/MapFeedCardPager.tsx
Comment thread src/features/map/ui/MapTutorialOverlay.tsx
@seoJing seoJing merged commit 9f0bbe6 into main May 28, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant