[fix] 네비게이션바 수정 - #245
Conversation
📝 WalkthroughWalkthrough네비게이션 UI를 슬롯 기반 아키텍처로 리팩토링하고, 데이터베이스 초기화를 지연 로딩 패턴으로 변경하며, 알림 및 사용자 정보 페칭을 별도의 서버 컴포넌트로 분리했습니다. 테스트 환경에서는 데이터베이스 모킹을 추가했습니다. Changes
Sequence Diagram(s)sequenceDiagram
actor Client
participant NavigationMenu as NavigationMenu<br/>(Server)
participant Suspense as Suspense +<br/>Fallback
participant NotifServer as NavigationNotificationsServer<br/>(Server)
participant UserServer as NavigationUserServer<br/>(Server)
participant MenuClient as NavigationMenuClient<br/>(Client)
Client->>NavigationMenu: 페이지 요청
alt authenticated = true
Note over NavigationMenu: notificationsSlot 생성
NavigationMenu->>Suspense: NotificationBellFallback 폴백과 함께<br/>NotificationNotificationsServer 래핑
Suspense->>NotifServer: 알림 데이터 페칭 시작
Note over NavigationMenu: userSlot 생성
NavigationMenu->>Suspense: null 폴백과 함께<br/>NavigationUserServer 래핑
Suspense->>UserServer: 사용자 데이터 페칭 시작
par 병렬 처리
NotifServer-->>Suspense: getNotifications({ size: 10 })
UserServer-->>Suspense: api.user.getUser()
end
else authenticated = false
Note over NavigationMenu: notificationsSlot = null
Note over NavigationMenu: userSlot = null
end
NavigationMenu->>MenuClient: notificationsSlot,<br/>userSlot 전달
MenuClient-->>Client: 렌더링 완료
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/web/src/features/notification/ui/navigation-notifications.tsx`:
- Around line 18-21: NavigationNotificationsServer currently awaits
getNotifications() without handling errors, which can break global navigation
rendering on transient API/DB failures; wrap the getNotifications call in a
try/catch inside NavigationNotificationsServer, on error log (or swallow) and
return <NotificationMenu notifications={[]} nextCursor={undefined}
hasMore={false} /> (or equivalent local fallback) so the layout remains stable;
reference the NavigationNotificationsServer function, the getNotifications call,
and the NotificationMenu component when implementing the change.
In `@apps/web/src/shared/ui/navigation-menu-client.tsx`:
- Around line 79-80: The right-side blank notification box is always rendered
because the wrapper div around notificationsSlot is unconditional; update the
component (NavigationMenuClient / the JSX that renders the div with className
"h-9 w-9") to render that wrapper only when the user is logged in or when
notificationsSlot is non-null (e.g., guard with loggedIn ||
Boolean(notificationsSlot)), so when navigation-menu.tsx passes
notificationsSlot as null the wrapper is not output and the menu button won't
shift.
In `@apps/web/vitest.setup.ts`:
- Around line 7-15: Add a stub for db.transaction in the vi.mock("@/shared/db")
setup so tests that hit the transaction path in posts (e.g.,
apps/web/src/entities/post/queries.ts calling db.transaction) don't fail with
TypeError; implement db.transaction as a function that accepts a callback (and
optional options), invokes that callback with the mocked db or a
transaction-scoped object, and returns the callback result (or a resolved
promise) to match the real API surface while keeping existing mocked methods
insert/update/delete/select.
🪄 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: ASSERTIVE
Plan: Pro
Run ID: d3dece51-f68d-4bf3-b3aa-a9929bb343b7
📒 Files selected for processing (7)
apps/web/src/features/notification/ui/navigation-notifications.tsxapps/web/src/shared/db/index.tsapps/web/src/shared/ui/navigation-menu-client.tsxapps/web/src/shared/ui/navigation-menu.tsxapps/web/src/shared/ui/navigation-user.tsxapps/web/vitest.config.mtsapps/web/vitest.setup.ts
| export async function NavigationNotificationsServer() { | ||
| const result = await getNotifications({ size: 10 }); | ||
|
|
||
| return <NotificationMenu notifications={result.data} nextCursor={result.nextCursor} hasMore={result.hasMore} />; |
There was a problem hiding this comment.
알림 조회 예외가 헤더 전체를 깨뜨릴 수 있습니다.
getNotifications() 예외를 여기서 처리하지 않으면 일시적인 API/DB 장애가 전역 내비게이션 렌더링 실패로 번집니다. 같은 PR의 apps/web/src/shared/ui/navigation-user.tsx처럼 로컬 fallback으로 흡수해야 레이아웃 안정성이 유지됩니다.
🛠️ 제안된 수정안
export async function NavigationNotificationsServer() {
- const result = await getNotifications({ size: 10 });
-
- return <NotificationMenu notifications={result.data} nextCursor={result.nextCursor} hasMore={result.hasMore} />;
+ try {
+ const result = await getNotifications({ size: 10 });
+
+ return (
+ <NotificationMenu
+ notifications={result.data}
+ nextCursor={result.nextCursor}
+ hasMore={result.hasMore}
+ />
+ );
+ } catch {
+ return <NotificationBellFallback />;
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export async function NavigationNotificationsServer() { | |
| const result = await getNotifications({ size: 10 }); | |
| return <NotificationMenu notifications={result.data} nextCursor={result.nextCursor} hasMore={result.hasMore} />; | |
| export async function NavigationNotificationsServer() { | |
| try { | |
| const result = await getNotifications({ size: 10 }); | |
| return ( | |
| <NotificationMenu | |
| notifications={result.data} | |
| nextCursor={result.nextCursor} | |
| hasMore={result.hasMore} | |
| /> | |
| ); | |
| } catch { | |
| return <NotificationBellFallback />; | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/features/notification/ui/navigation-notifications.tsx` around
lines 18 - 21, NavigationNotificationsServer currently awaits getNotifications()
without handling errors, which can break global navigation rendering on
transient API/DB failures; wrap the getNotifications call in a try/catch inside
NavigationNotificationsServer, on error log (or swallow) and return
<NotificationMenu notifications={[]} nextCursor={undefined} hasMore={false} />
(or equivalent local fallback) so the layout remains stable; reference the
NavigationNotificationsServer function, the getNotifications call, and the
NotificationMenu component when implementing the change.
| <div className="flex items-center gap-2"> | ||
| {loggedIn ? ( | ||
| <NotificationMenu notifications={notifications} nextCursor={nextCursor} hasMore={hasMore} /> | ||
| ) : null} | ||
| <div className="h-9 w-9">{notificationsSlot}</div> |
There was a problem hiding this comment.
비로그인 모바일 헤더에 빈 알림 칸이 남습니다.
apps/web/src/shared/ui/navigation-menu.tsx Line 17은 비로그인 시 notificationsSlot을 null로 넘깁니다. 지금은 h-9 w-9 wrapper가 항상 렌더링되어 메뉴 버튼이 오른쪽 끝에서 한 칸 밀려 보입니다. 모바일에서는 loggedIn일 때만 이 칸을 렌더링하는 편이 맞습니다.
🛠️ 제안된 수정안
- <div className="flex items-center gap-2">
- <div className="h-9 w-9">{notificationsSlot}</div>
+ <div className="flex items-center gap-2">
+ {loggedIn ? <div className="h-9 w-9">{notificationsSlot}</div> : null}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/shared/ui/navigation-menu-client.tsx` around lines 79 - 80, The
right-side blank notification box is always rendered because the wrapper div
around notificationsSlot is unconditional; update the component
(NavigationMenuClient / the JSX that renders the div with className "h-9 w-9")
to render that wrapper only when the user is logged in or when notificationsSlot
is non-null (e.g., guard with loggedIn || Boolean(notificationsSlot)), so when
navigation-menu.tsx passes notificationsSlot as null the wrapper is not output
and the menu button won't shift.
| vi.mock("@/shared/db", () => ({ | ||
| db: { | ||
| query: {}, | ||
| insert: vi.fn(), | ||
| update: vi.fn(), | ||
| delete: vi.fn(), | ||
| select: vi.fn(), | ||
| }, | ||
| })); |
There was a problem hiding this comment.
db.transaction mock이 빠져 있어 트랜잭션 경로 테스트가 바로 깨집니다.
apps/web/src/entities/post/queries.ts Line 223은 db.transaction(...)을 호출합니다. 지금 mock은 해당 메서드를 제공하지 않아서 이 경로를 타는 테스트가 TypeError: db.transaction is not a function으로 실패합니다. 최소한 callback을 실행하는 stub까지 포함해 실제 surface와 맞춰 주세요.
🛠️ 제안된 수정안
-vi.mock("@/shared/db", () => ({
- db: {
- query: {},
- insert: vi.fn(),
- update: vi.fn(),
- delete: vi.fn(),
- select: vi.fn(),
- },
-}));
+vi.mock("@/shared/db", () => {
+ const tx = {
+ query: {},
+ insert: vi.fn(),
+ update: vi.fn(),
+ delete: vi.fn(),
+ select: vi.fn(),
+ };
+
+ return {
+ db: {
+ ...tx,
+ transaction: vi.fn(async (callback: (trx: typeof tx) => unknown) => await callback(tx)),
+ },
+ };
+});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| vi.mock("@/shared/db", () => ({ | |
| db: { | |
| query: {}, | |
| insert: vi.fn(), | |
| update: vi.fn(), | |
| delete: vi.fn(), | |
| select: vi.fn(), | |
| }, | |
| })); | |
| vi.mock("@/shared/db", () => { | |
| const tx = { | |
| query: {}, | |
| insert: vi.fn(), | |
| update: vi.fn(), | |
| delete: vi.fn(), | |
| select: vi.fn(), | |
| }; | |
| return { | |
| db: { | |
| ...tx, | |
| transaction: vi.fn(async (callback: (trx: typeof tx) => unknown) => await callback(tx)), | |
| }, | |
| }; | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/vitest.setup.ts` around lines 7 - 15, Add a stub for db.transaction
in the vi.mock("@/shared/db") setup so tests that hit the transaction path in
posts (e.g., apps/web/src/entities/post/queries.ts calling db.transaction) don't
fail with TypeError; implement db.transaction as a function that accepts a
callback (and optional options), invokes that callback with the mocked db or a
transaction-scoped object, and returns the callback result (or a resolved
promise) to match the real API surface while keeping existing mocked methods
insert/update/delete/select.
📌 Summary
해당 PR에 대한 작업 내용을 요약하여 작성해주세요.
네비게이션바 layout shift 문제를 해결했습니다.
📄 Tasks
해당 PR에 수행한 작업을 작성해주세요.
👀 To Reviewer
리뷰어에게 요청하는 내용을 작성해주세요.
📸 Screenshot
작업한 내용에 대한 스크린샷을 첨부해주세요.
Summary by CodeRabbit
릴리스 노트
새로운 기능
리팩토링
Chores