Skip to content

refactor: 스크롤 복원 공용화 및 토너먼트 아카이브 적용 - #408

Open
kanghaeun wants to merge 3 commits into
devfrom
refactor/407-scroll-restoration-commonize
Open

refactor: 스크롤 복원 공용화 및 토너먼트 아카이브 적용#408
kanghaeun wants to merge 3 commits into
devfrom
refactor/407-scroll-restoration-commonize

Conversation

@kanghaeun

@kanghaeun kanghaeun commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

작업 요약

  • 위시 전용이던 스크롤 복원 로직을 공용 모듈로 분리하고, 토너먼트 아카이브에도 적용

작업 세부 내용

#392 에서 구현한 스크롤 복원은 로직 자체는 범용적이지만 저장 키·타입·파일 위치가 위시에 종속되어 있었습니다. 토너먼트 아카이브에도 적용하기 위해 공용화했습니다.

토너먼트 페이지 구조에서 전제가 다른 지점들이 있어 그 부분을 함께 해결했습니다.

1. 공용 모듈로 분리

이전 이후
app/archive/wish/_utils/wishScroll.ts src/utils/scrollRestoration.ts
app/archive/wish/_hooks/useScrollRestoration.ts src/hooks/useScrollRestoration.ts
// 저장 — 항목 클릭 시 1회
saveScrollAnchor({ namespace, scope? }, element, anchorId);

// 복원 — 리스트 마운트 시
useScrollRestoration({ namespace, scope? });
  • namespace — 리스트 구분자. 저장·복원이 같은 키를 쓰도록 SCROLL_NAMESPACE 상수로 관리
  • scope — 같은 history entry 안에서 위치가 나뉘는 경우의 하위 구분자 (탭 등)
  • 저장 키 형태: piki:scroll:{namespace}:{scope}:{entryKey}
  • data-wish-iddata-scroll-anchor-id, wishIdanchorId 로 중립화

로직(history entry 도장으로 뒤로가기 판별, 복원 성공 시 저장값 삭제, rAF 재시도)은 #392 에서 그대로 옮겼습니다.

2. 탭별 스크롤 위치 분리

토너먼트 아카이브는 진행 중 / 완료 탭이 하나의 history entry 를 공유합니다.
(탭 전환이 라우팅이 아니라 useState + replaceState 로 처리됨)

entry 당 저장값이 하나면 탭을 오갈 때 다른 탭의 위치로 복원되므로, 탭을 scope 로 넘겨 저장 키를 분리했습니다.

useScrollRestoration({ namespace: SCROLL_NAMESPACE.ARCHIVE_TOURNAMENT, scope: statusTab });

3. history.state 보존

TournamentHistorySection은 서버 요청 없이 URL만 동기화하기 위해 history.replaceState를 사용하고 있었는데, state에 null을 전달하면서 기존 history.state가 덮어써지고 있었습니다.

- window.history.replaceState(null, '', `?tab=${tab}`);
+ window.history.replaceState({ ...window.history.state }, '', `?tab=${tab}`);

스크롤 복원은 history.state에 저장한 식별자를 기준으로 동일한 history entry를 판별하므로, 기존 state를 유지하도록 수정했습니다.

또한 history.state에는 Next 라우터의 내부 라우팅 정보(__NA, 라우팅 트리 등)도 포함되어 있어, 기존 값을 보존하는 편이 더 안전합니다.

4. 카드 링크 구조 차이

위시는 <Link> 가 카드를 감싸지만, 토너먼트 카드는 카드 안에 오버레이 링크가 있습니다. 저장(클릭)과 복원(속성 조회)이 같은 요소를 기준으로 측정해야 위치가 맞으므로, data-scroll-anchor-idonClick 을 모두 오버레이 링크에 붙였습니다.

연관 이슈

closes #407

Summary by CodeRabbit

  • 개선 사항
    • 토너먼트 목록에서 상세 화면을 다녀온 뒤 이전에 보던 스크롤 위치가 탭별로 복원됩니다.
    • 탭을 전환해도 URL 이동 과정에서 기존 탐색 상태가 유지됩니다.
    • 토너먼트 카드에서 일반 클릭 시 위치를 기억하며, 새 탭이나 새 창으로 여는 경우에는 적용되지 않습니다.
    • 위시리스트 보관함의 스크롤 위치 복원 동작이 공통 방식으로 개선되어 목록 탐색이 더 일관적입니다.

@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
piki Ready Ready Preview Jul 31, 2026 7:29pm

@github-actions

Copy link
Copy Markdown

Discord 스레드 연동용 메타데이터입니다. discord-pr-bot 워크플로가 자동 생성하며, 수정·삭제하면 PR 과 Discord 알림 연동이 끊깁니다.

@github-actions github-actions Bot added refactor Extra attention is needed WEB labels Jul 31, 2026
@github-actions
github-actions Bot requested a review from iOdiO89 July 31, 2026 19:29
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

스크롤 복원 로직을 공용 유틸리티와 훅으로 이동했습니다. 위시리스트는 공용 네임스페이스를 사용합니다. 토너먼트 아카이브는 탭별 복원과 history state 보존을 지원합니다.

Changes

스크롤 복원 공용 기반

Layer / File(s) Summary
공용 앵커 저장 및 복원
apps/web/src/utils/scrollRestoration.ts, apps/web/src/hooks/useScrollRestoration.ts
history state와 sessionStorage를 조합해 앵커를 저장·조회·삭제합니다. 앵커가 렌더링될 때까지 최대 1초 동안 복원을 재시도합니다.

위시리스트 스크롤 복원 전환

Layer / File(s) Summary
위시리스트 공용 유틸리티 적용
apps/web/src/app/archive/wish/_components/WishlistList.tsx, apps/web/src/app/archive/wish/_components/wish-grid/index.tsx, apps/web/src/app/archive/wish/_hooks/useScrollRestoration.ts, apps/web/src/app/archive/wish/_utils/wishScroll.ts
위시리스트가 공용 훅, SCROLL_NAMESPACE.ARCHIVE_WISH, saveScrollAnchor, data-scroll-anchor-id를 사용합니다. 기존 위시 전용 훅과 유틸리티를 삭제합니다.

토너먼트 탭별 스크롤 복원

Layer / File(s) Summary
탭 상태와 history state 연결
apps/web/src/app/archive/tournament/_components/TournamentHistorySection.tsx, apps/web/src/app/archive/tournament/_components/TournamentHistoryList.tsx
탭 변경 시 기존 window.history.state를 유지합니다. 현재 탭을 TournamentHistoryList와 복원 범위에 전달합니다.
토너먼트 카드 앵커 저장
apps/web/src/components/tournament-card/index.tsx
일반 클릭에서 토너먼트 앵커를 저장합니다. 새 탭·새 창 동작에서는 저장하지 않습니다. 카드 링크에 data-scroll-anchor-id를 추가합니다.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • TeamPiKi/client#347: 아카이브 목록 컴포넌트와 스크롤 복원 연결 지점을 함께 변경합니다.
  • TeamPiKi/client#366: 토너먼트 아카이브의 탭 렌더링과 TournamentHistorySection을 변경합니다.
  • TeamPiKi/client#401: 위시리스트 전용 스크롤 복원 구현과 직접 연결됩니다.

Suggested labels: fix

Suggested reviewers: iodio89

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning 공용화와 탭별 복원은 반영했지만, 토너먼트의 자체 스크롤 컨테이너를 파라미터로 처리한 근거가 없습니다. [#407] 토너먼트 아카이브의 스크롤 컨테이너를 명시적으로 전달하고, 공용 유틸리티가 해당 컨테이너를 사용하도록 수정하세요.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 스크롤 복원 공용화와 토너먼트 아카이브 적용이라는 주요 변경을 정확히 요약합니다.
Out of Scope Changes check ✅ Passed 변경 사항은 스크롤 복원 공용화, 위시리스트 전환, 토너먼트 아카이브 적용 범위에 포함됩니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/407-scroll-restoration-commonize

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (1)
apps/web/src/app/archive/tournament/_components/TournamentHistoryList.tsx (1)

12-17: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Props 타입 이름을 컴포넌트 이름으로 지정하세요.

PropsTournamentHistoryListProps로 변경하세요. 이 이름은 다른 컴포넌트의 props 타입과 구분되지 않습니다.

수정 예시
-type Props = {
+type TournamentHistoryListProps = {
   statuses: TournamentStatusT[];
   statusTab: TournamentStatusTabT;
 };
 
-function TournamentHistoryList({ statuses, statusTab }: Props) {
+function TournamentHistoryList({ statuses, statusTab }: TournamentHistoryListProps) {

As per coding guidelines: Props 타입은 {ComponentName}Props로 명명해야 합니다.

🤖 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 `@apps/web/src/app/archive/tournament/_components/TournamentHistoryList.tsx`
around lines 12 - 17, Rename the Props type used by TournamentHistoryList to
TournamentHistoryListProps and update the component’s parameter annotation to
reference the renamed type, following the {ComponentName}Props convention.

Source: Coding guidelines

🤖 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
`@apps/web/src/app/archive/tournament/_components/TournamentHistorySection.tsx`:
- Around line 20-23: Update handleTabChange so it constructs a URL from the
current location, changes only the tab query parameter, and passes the resulting
pathname, search, and hash to window.history.replaceState. Preserve all existing
query parameters and the URL hash while updating the active tab.

In `@apps/web/src/utils/scrollRestoration.ts`:
- Line 40: Update getScrollContainer and the related scroll save/restore flow to
accept a ScrollRestorationTargetT-specific container reference or resolver
instead of always using SCROLL_CONTAINER_ID. Ensure TournamentHistorySection’s
overflow-y-auto container is selected, and use that same resolved container for
both saving and restoring offsets.

---

Nitpick comments:
In `@apps/web/src/app/archive/tournament/_components/TournamentHistoryList.tsx`:
- Around line 12-17: Rename the Props type used by TournamentHistoryList to
TournamentHistoryListProps and update the component’s parameter annotation to
reference the renamed type, following the {ComponentName}Props convention.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 16a2c47c-22bc-4c45-8175-44f78acce3a9

📥 Commits

Reviewing files that changed from the base of the PR and between 6a07452 and e4c0450.

📒 Files selected for processing (9)
  • apps/web/src/app/archive/tournament/_components/TournamentHistoryList.tsx
  • apps/web/src/app/archive/tournament/_components/TournamentHistorySection.tsx
  • apps/web/src/app/archive/wish/_components/WishlistList.tsx
  • apps/web/src/app/archive/wish/_components/wish-grid/index.tsx
  • apps/web/src/app/archive/wish/_hooks/useScrollRestoration.ts
  • apps/web/src/app/archive/wish/_utils/wishScroll.ts
  • apps/web/src/components/tournament-card/index.tsx
  • apps/web/src/hooks/useScrollRestoration.ts
  • apps/web/src/utils/scrollRestoration.ts
💤 Files with no reviewable changes (2)
  • apps/web/src/app/archive/wish/_hooks/useScrollRestoration.ts
  • apps/web/src/app/archive/wish/_utils/wishScroll.ts

Comment thread apps/web/src/utils/scrollRestoration.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactor Extra attention is needed WEB

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor: 스크롤 복원 유틸 공용화 및 토너먼트 아카이브 적용

1 participant