Skip to content

[fix] 네비게이션바 수정 - #245

Merged
chan-byeong merged 2 commits into
devfrom
fix/#242/bug-fix
Apr 16, 2026
Merged

[fix] 네비게이션바 수정#245
chan-byeong merged 2 commits into
devfrom
fix/#242/bug-fix

Conversation

@chan-byeong

@chan-byeong chan-byeong commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

📌 Summary

해당 PR에 대한 작업 내용을 요약하여 작성해주세요.

네비게이션바 layout shift 문제를 해결했습니다.

관련 있는 Issue를 태그해주세요. (e.g. > - #1)

📄 Tasks

해당 PR에 수행한 작업을 작성해주세요.

  • 유저 정보, 알림 정보를 가져오는 패치 함수를 하위 컴포넌트로 이동하여 네비게이션이 늦게 렌더링되는 케이스를 제거했습니다.

👀 To Reviewer

리뷰어에게 요청하는 내용을 작성해주세요.

📸 Screenshot

작업한 내용에 대한 스크린샷을 첨부해주세요.

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 알림 벨 로딩 상태 개선
  • 리팩토링

    • 네비게이션 메뉴 컴포넌트 아키텍처 최적화
    • 데이터베이스 초기화 방식 개선
  • Chores

    • 테스트 환경 설정 개선

@chan-byeong chan-byeong self-assigned this Apr 16, 2026
@chan-byeong chan-byeong added the 🐛 Fix 버그 수정 label Apr 16, 2026
@chan-byeong
chan-byeong requested a review from a team as a code owner April 16, 2026 12:50
@chan-byeong
chan-byeong requested review from eojindesu and removed request for a team April 16, 2026 12:50
@coderabbitai

coderabbitai Bot commented Apr 16, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

네비게이션 UI를 슬롯 기반 아키텍처로 리팩토링하고, 데이터베이스 초기화를 지연 로딩 패턴으로 변경하며, 알림 및 사용자 정보 페칭을 별도의 서버 컴포넌트로 분리했습니다. 테스트 환경에서는 데이터베이스 모킹을 추가했습니다.

Changes

Cohort / File(s) Summary
알림 및 사용자 UI 컴포넌트
apps/web/src/features/notification/ui/navigation-notifications.tsx, apps/web/src/shared/ui/navigation-user.tsx
비활성화된 알림 벨 아이콘과 사용자 프로필 아바타를 렌더링하는 새로운 서버 컴포넌트 추가. 각각 알림 목록(getNotifications)과 사용자 데이터(api.user.getUser())를 비동기로 페칭.
네비게이션 메뉴 리팩토링
apps/web/src/shared/ui/navigation-menu.tsx, apps/web/src/shared/ui/navigation-menu-client.tsx
소품(prop) 기반에서 슬롯(slot) 기반 아키텍처로 전환. notificationsSlotuserSlot React 노드를 사용하고, Suspense 경계로 감싸서 로딩 상태 처리. NotificationMenu, ProfileAvatar 직접 렌더링 제거.
데이터베이스 초기화 패턴
apps/web/src/shared/db/index.ts
즉시 초기화 방식에서 지연 로딩(lazy initialization) 방식으로 변경. getDb() 함수로 drizzle 클라이언트를 캐싱하고, Proxy를 통해 기존 호출 패턴(db.query, db.insert 등) 유지.
테스트 환경 설정
apps/web/vitest.config.mts, apps/web/vitest.setup.ts
Vitest 설정에서 인라인 DATABASE_URL 환경 변수 제거 및 setupFiles 참조 추가. 새로운 셋업 파일에서 @/shared/db 모듈을 전역적으로 모킹하여 테스트 실행 시 데이터베이스 연결 제거.

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: 렌더링 완료
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Suggested reviewers

  • eojindesu
  • haesol2022

Poem

🐰 슬롯으로 나뉜 UI, 지연된 데이터베이스,
Suspense가 감싸 안네, 로딩 대기 따뜻이.
서버와 클라이언트 춤을 추며,
버그는 여전히 꼬물꼬물 숨어있지만,
우리 토끼는 계속 뛰어간다! 🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed PR의 코드 변경사항이 연결된 이슈 #242의 목표와 부합합니다. 네비게이션바 레이아웃 이동 문제 해결 및 지연 렌더링 방지를 위해 사용자 정보/알림 조회 함수를 자식 컴포넌트로 이동했습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 네비게이션바 레이아웃 이동 문제 해결 범위 내입니다. DB 레이지 초기화 패턴 적용, 네비게이션 컴포넌트 리팩토링, 슬롯 기반 아키텍처 도입이 모두 목표 달성을 위한 변경사항입니다.

✏️ 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.

❤️ Share

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

@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: 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

📥 Commits

Reviewing files that changed from the base of the PR and between b4e7042 and 7548b62.

📒 Files selected for processing (7)
  • apps/web/src/features/notification/ui/navigation-notifications.tsx
  • apps/web/src/shared/db/index.ts
  • apps/web/src/shared/ui/navigation-menu-client.tsx
  • apps/web/src/shared/ui/navigation-menu.tsx
  • apps/web/src/shared/ui/navigation-user.tsx
  • apps/web/vitest.config.mts
  • apps/web/vitest.setup.ts

Comment on lines +18 to +21
export async function NavigationNotificationsServer() {
const result = await getNotifications({ size: 10 });

return <NotificationMenu notifications={result.data} nextCursor={result.nextCursor} hasMore={result.hasMore} />;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

알림 조회 예외가 헤더 전체를 깨뜨릴 수 있습니다.

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.

Suggested change
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.

Comment on lines 79 to +80
<div className="flex items-center gap-2">
{loggedIn ? (
<NotificationMenu notifications={notifications} nextCursor={nextCursor} hasMore={hasMore} />
) : null}
<div className="h-9 w-9">{notificationsSlot}</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

비로그인 모바일 헤더에 빈 알림 칸이 남습니다.

apps/web/src/shared/ui/navigation-menu.tsx Line 17은 비로그인 시 notificationsSlotnull로 넘깁니다. 지금은 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.

Comment thread apps/web/vitest.setup.ts
Comment on lines +7 to +15
vi.mock("@/shared/db", () => ({
db: {
query: {},
insert: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
select: vi.fn(),
},
}));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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.

Suggested change
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.

@chan-byeong
chan-byeong merged commit 4fdfd71 into dev Apr 16, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐛 Fix 버그 수정

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[fix] 버그 수정

1 participant