Skip to content

[URECA-77] Feat: 네비바 수정#74

Merged
joonhyong merged 11 commits intodevelopfrom
URECA-77/Feat/navbar-update
Jan 29, 2026
Merged

[URECA-77] Feat: 네비바 수정#74
joonhyong merged 11 commits intodevelopfrom
URECA-77/Feat/navbar-update

Conversation

@joonhyong
Copy link
Copy Markdown
Contributor

@joonhyong joonhyong commented Jan 28, 2026

Key Changes

  • componentes/layout/bottom-navibar/index.tsx

작업 내역

  • 네비바의 아이콘을 SVG로 대체했습니다.

  • 네비바의 중앙 아이콘의 그림자에 색상을 입혔습니다.

  • 네비바 아이콘을 누르면 해당 페이지로 이동했을 때 색이 칠해집니다.

  • 마이페이지에서 등급 삭제 및 프로필 사진 부분을 변경했습니다.

  • 마이페이지 상담 횟수, 북마크 상담 수에 리다이렉트를 추가했습니다.

  • close: [URECA-77] Feat: 네비바 수정 #73

📸 스크린샷

네비바 마이페이지 프로필

💬 공유사항 to 리뷰어

비고

PR Checklist

PR이 다음 요구 사항을 충족하는지 확인하세요.

  • 커밋 메시지 컨벤션에 맞게 작성했습니다.
  • Eslint 및 Prettier 규칙을 준수했습니다.
  • origin/develop 브랜치에서의 최신 커밋을 확인하고 반영했습니다.
  • 변경 사항에 대한 테스트를 했습니다.(버그 수정/기능에 대한 테스트)

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 마이 페이지의 상담 횟수 및 북마크 타일을 클릭해 관련 화면으로 이동 가능
  • 스타일

    • 아이콘을 SVG로 교체해 확장성 및 호버 스타일 개선
    • 하단 네비게이션의 활성 상태 시각화 개선 및 버튼 스타일 업데이트
    • 일부 페이지 레이아웃과 여백이 최적화됨
  • 접근성

    • 로딩 컴포넌트에 상태 관련 접근성 속성 추가
  • UI 변경

    • 아바타 영역 아이콘 변경 및 일부 배지 제거

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link
Copy Markdown

vercel bot commented Jan 28, 2026

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

Project Deployment Review Updated (UTC)
unity Ready Ready Preview, Comment Jan 29, 2026 6:37am
unity-client Ready Ready Preview, Comment Jan 29, 2026 6:37am

@github-actions github-actions bot changed the title 네비바 수정 [URECA-77] Feat: 네비바 수정 Jan 28, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 28, 2026

📝 Walkthrough

Walkthrough

이번 PR은 UI 아이콘과 레이아웃 태그의 일관된 정리 작업입니다. PNG 기반 아이콘들을 SVG 컴포넌트로 교체하고 하단 네비게이션의 활성화 로직을 pathname 기반으로 전환했으며, 여러 레이아웃/페이지에서 DOM 태그(예: div ↔ main, section ↔ div) 및 래핑 구조를 수정했습니다. 마이페이지의 통계 타일을 클릭 가능한 요소로 변환하고 요약 관련 뒤로가기 아이콘에 호버 스타일을 추가했으며, 로딩 컴포넌트에 접근성(role/aria) 속성과 레이아웃 변경을 적용했습니다. 인증 흐름 판정 조건은 간소화되었습니다.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning 이슈 #73의 범위(네비바 수정)를 벗어나 마이페이지, 레이아웃, 로딩 컴포넌트 등 다양한 파일이 수정되어 있으며, 이러한 변경사항들은 최초 이슈 요구사항과 무관해 보입니다. 마이페이지 프로필, 레이아웃 구조 변경(section→div, main 태그 추가)은 이슈 #73 범위 밖입니다. 별도 이슈로 분리하거나 연관 이슈임을 문서화하세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 링크된 이슈 #73의 주요 변경사항(네비바 수정)과 일치하며, 실제 코드 변경사항의 핵심(bottom-navigation 컴포넌트 수정)을 명확히 반영하고 있습니다.
Linked Issues check ✅ Passed PR은 링크된 이슈 #73의 세 가지 주요 요구사항(페이지별 색상 반응, 아이콘 크기 조정, width 조절)을 모두 충족하며, 추가로 마이페이지 관련 개선사항도 구현하였습니다.

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

✨ Finishing touches
  • 📝 Generate docstrings

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/(private)/summary/bookmarks/page.tsx (1)

108-122: 훅 레벨의 정규화를 믿고 컴포넌트에서 불필요한 타입 체크 제거

useBookmarkedSummaryList 훅이 이미 keywords: string[]로 정규화하므로, 컴포넌트 레벨의 typeof summary.keywords === 'string' 체크는 불필요합니다. 훅의 타입 계약을 신뢰하고 중복 방어 로직을 제거하세요.

- typeof summary.keywords === 'string' ? [summary.keywords] : summary.keywords;
+ summary.keywords

훅에서 이미 JSON.parse를 포함한 방어적 파싱을 수행(useSummaryList.ts, useBookmarkedSummaryList.ts 참고)하고 있으므로, 컴포넌트는 정규화된 데이터를 받는다고 가정할 수 있습니다. 이렇게 하면 데이터 정규화 책임이 명확해지고 컴포넌트 로직도 단순화됩니다.

🤖 Fix all issues with AI agents
In `@src/app/`(private)/mypage/page.tsx:
- Around line 153-170: The two buttons that call handleSummary and
handleBookmarks (rendering summaryCount/bookmarkCount with statsLoading) are
missing an explicit type attribute and will default to type="submit" inside
forms; update both <button> elements to include type="button" to prevent
accidental form submission (add type="button" to the button rendering
summaryCount and the one rendering bookmarkCount).

In `@src/app/layout.tsx`:
- Around line 45-48: The root layout is missing the semantic <main> landmark
causing WCAG failures; update the Providers component in layout.tsx so that the
rendered children from {children} are wrapped in a single <main> element (keep
GlobalComponents outside the <main>), ensuring every page inherits exactly one
main landmark; check components named Providers and GlobalComponents when making
the change and verify other nested layouts/pages don’t inject additional <main>
elements.
🧹 Nitpick comments (4)
src/app/(private)/summary/_components/SummarySuccessPage.tsx (1)

62-64: 백 버튼 접근성 개선 권장

SVG 아이콘으로 전환은 좋은 변경입니다. 다만 키보드 사용자를 위한 포커스 상태 스타일링이 없습니다. focus:outline 또는 focus-visible:ring 클래스 추가를 권장합니다.

♻️ 포커스 스타일링 추가 제안
-          <button type="button" onClick={() => router.push('/summary')} className="ml-4">
+          <button
+            type="button"
+            onClick={() => router.push('/summary')}
+            aria-label="뒤로가기"
+            className="ml-4 rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500"
+          >
src/components/layout/bottom-navigation/index.tsx (2)

20-113: 네비게이션 아이템 구조 반복 - 선택적 리팩토링 제안

각 네비게이션 아이템이 동일한 패턴(버튼 → 아이콘 → 라벨)을 따르고 있습니다. 현재도 읽기 쉽지만, 향후 아이템 추가/수정 시 유지보수성을 위해 설정 배열과 매핑 방식으로 리팩토링을 고려해볼 수 있습니다.

♻️ 설정 기반 리팩토링 예시
const navItems = [
  { path: '/', label: '홈', icon: Home },
  { path: '/recommend', label: '추천', icon: Recommend },
  { path: '/summary', label: '요약', icon: Summary },
  { path: '/mypage', label: 'MY', icon: My },
];

// 상담 버튼은 특수 스타일이므로 별도 처리
{navItems.map(({ path, label, icon: Icon }) => (
  <li key={path}>
    <button type="button" onClick={() => router.push(path)} className="flex flex-col items-center gap-1">
      <Icon className={`h-5 w-5 transition-colors ${isActive(path) ? 'text-primary-500' : 'text-gray-600'}`} />
      <span className={`text-xs ${isActive(path) ? 'text-primary-500 font-semibold' : 'text-gray-600'}`}>
        {label}
      </span>
    </button>
  </li>
))}

105-111: 라벨 일관성 확인 필요

다른 탭들은 한글 라벨(홈, 추천, 상담, 요약)을 사용하는데, 마지막 탭만 영어 "MY"로 되어 있습니다. 의도된 브랜딩이라면 무시해도 되지만, 일관성을 위해 "마이" 또는 "마이페이지"로 변경을 고려해보세요.

src/app/(private)/summary/bookmarks/page.tsx (1)

24-101: 헤더 컴포넌트 중복 - 리팩토링 권장

동일한 헤더 구조가 4번 반복됩니다(로딩, 인증 에러, 목록 에러, 정상 렌더). 이는 DRY 원칙 위반이며, 헤더 스타일 변경 시 4곳을 모두 수정해야 합니다.

공통 헤더 컴포넌트를 추출하거나, 조건부 렌더링 구조를 개선하는 것을 권장합니다.

♻️ 헤더 컴포넌트 추출 제안
// 헤더 컴포넌트 추출
const BookmarkHeader = () => {
  const router = useRouter();
  return (
    <div className="relative flex h-14 items-center bg-white">
      <button
        type="button"
        aria-label="뒤로가기"
        onClick={() => router.push('/mypage')}
        className="ml-4 flex items-center"
      >
        <GoBack className="hover:text-primary-500" />
      </button>
      <p className="absolute left-1/2 -translate-x-1/2 text-sm font-semibold">북마크 상담</p>
    </div>
  );
};

// 본문에서 사용
export default function BookmarkedSummaryPage() {
  // ... hooks ...

  // 공통 레이아웃으로 감싸기
  const renderContent = () => {
    if (meLoading || listLoading) {
      return <div className="py-12 text-center text-sm text-gray-400">북마크 불러오는 중...</div>;
    }
    if (meError || !Number.isFinite(userId)) {
      return <div className="py-12 text-center text-sm text-gray-400">로그인이 필요합니다</div>;
    }
    // ... etc
  };

  return (
    <>
      <BookmarkHeader />
      {renderContent()}
      <BottomNav />
    </>
  );
}

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/(private)/layout.tsx (1)

9-35: 코드 중복: buildCookieHeadercallMe 함수 추출 권장

buildCookieHeadercallMe 함수가 다음 파일들에서 중복되어 있습니다:

  • src/app/(private)/layout.tsx
  • src/app/(public)/layout.tsx
  • src/app/(policy)/policy/page.tsx

src/lib/auth/server.ts에 이미 유사한 유틸리티가 존재하는 것으로 보입니다. DRY 원칙에 따라 공통 유틸리티로 추출하면 유지보수성이 향상됩니다.

♻️ 공통 유틸리티 추출 제안

src/lib/auth/server.ts에 통합:

// src/lib/auth/server.ts
import { cookies } from 'next/headers';

const API = process.env.NEXT_PUBLIC_API_BASE_URL;

export async function buildCookieHeader(): Promise<string> {
  const store = await cookies();
  return store
    .getAll()
    .map(({ name, value }) => `${name}=${value}`)
    .join('; ');
}

export async function callMe(cookieHeader: string): Promise<Response | null> {
  if (!API) return null;
  
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), 5000);

  try {
    const res = await fetch(`${API}/api/auth/me`, {
      method: 'GET',
      headers: cookieHeader ? { cookie: cookieHeader } : {},
      cache: 'no-store',
      signal: controller.signal,
    });
    return res;
  } catch (e) {
    console.error('[auth] callMe failed:', e);
    return null;
  } finally {
    clearTimeout(timeoutId);
  }
}

각 레이아웃에서 import하여 사용:

import { buildCookieHeader, callMe } from '@/lib/auth/server';
🤖 Fix all issues with AI agents
In `@src/app/`(policy)/policy/page.tsx:
- Around line 67-70: The page currently uses a nested <main> which violates
HTML5 (root layout already renders <main>), so replace the outer <main> in the
policy page with a neutral container (e.g., <div> or <section>) preserving the
existing className and children; update the element that wraps PolicyHeader,
PolicyAgree, and PolicyView in the component rendering logic (the block that
currently reads main className="min-h-dvh bg-[`#FBF8FB`] px-4 pt-6") to a
div/section to avoid nested main elements, or alternatively adjust the top-level
layout to not always emit <main> and ensure only one semantic main exists per
document.

In `@src/components/loading/index.tsx`:
- Around line 6-9: The outer container div in the Loading component (the div
with className "bg-primary-100 flex h-96 w-full ...") is missing ARIA
attributes; update that element in src/components/loading/index.tsx to include
role="status", aria-live="polite", and aria-busy="true" (matching
src/components/ui/fallback/LoadingComponent.tsx) so screen readers announce the
loading state for the Image/ loadingPng content.
- Around line 11-14: The container div in the loading component (the <div
className="flex items-center justify-center gap-4"> in
src/components/loading/index.tsx) is missing accessibility attributes; update
that div to include role="status", aria-live="polite", and aria-busy="true"
(same approach used in LoadingComponent.tsx) so screen readers are notified of
the loading state.
🧹 Nitpick comments (8)
src/app/(private)/mypage/page.tsx (5)

140-149: 접근성: Moono 아이콘에 대체 텍스트 또는 aria-hidden 속성 추가 권장

<Moono /> 컴포넌트가 프로필 아바타로 사용되고 있습니다. 스크린 리더 사용자를 위해 의미 있는 이미지라면 aria-label을, 순수 장식용이라면 aria-hidden="true"를 추가하는 것이 좋습니다.

🔧 수정 제안
 <div className="flex h-14 w-14 items-center justify-center rounded-2xl bg-white/70 shadow-md">
-  <Moono />
+  <Moono aria-hidden="true" />
 </div>

또는 의미 있는 아이콘인 경우:

 <div className="flex h-14 w-14 items-center justify-center rounded-2xl bg-white/70 shadow-md">
-  <Moono />
+  <Moono role="img" aria-label="프로필 아바타" />
 </div>

153-172: 접근성: 버튼에 hover/focus 상태 스타일 추가 권장

버튼이 type="button"과 클릭 핸들러가 잘 추가되어 있습니다. 다만 시각적 피드백을 위해 hoverfocus 상태의 스타일을 추가하면 사용자 경험이 향상됩니다. 키보드 내비게이션 시 포커스 상태가 명확히 보여야 접근성 기준을 충족합니다.

🔧 수정 제안
 <button
   type="button"
   onClick={handleSummary}
-  className="rounded-2xl bg-white/70 px-4 py-3 text-center"
+  className="rounded-2xl bg-white/70 px-4 py-3 text-center transition-colors hover:bg-white/90 focus:outline-none focus:ring-2 focus:ring-pink-300"
 >
 <button
   type="button"
   onClick={handleBookmarks}
-  className="rounded-2xl bg-white/70 px-4 py-3 text-center"
+  className="rounded-2xl bg-white/70 px-4 py-3 text-center transition-colors hover:bg-white/90 focus:outline-none focus:ring-2 focus:ring-purple-300"
 >

100-109: UX 개선: 미구현 설정 항목에 대한 처리 필요

"테마 변경"과 "언어 설정" 항목에 onClick 핸들러가 없어 noop이 할당됩니다. 사용자가 클릭하면 아무 반응이 없어 혼란스러울 수 있습니다. 다음 중 하나의 방법을 권장드립니다:

  1. 기능 준비 중임을 알리는 토스트 표시
  2. 비활성화 스타일 적용 (disabled 상태)
  3. 해당 항목을 임시로 숨김 처리
🔧 수정 제안 (토스트 방식)
+ const handleNotReady = () => {
+   dispatch(toastActions.show({ text: '준비 중인 기능입니다.', variant: 'info' }));
+ };

 const 설정Rows = [
   {
     label: '테마 변경',
     icon: <ThemeIcon width="18px" height="18px" />,
+    onClick: handleNotReady,
   },
   {
     label: '언어 설정',
     icon: <GroupIcon width="18px" height="18px" />,
+    onClick: handleNotReady,
   },
 ];

92-98: 유지보수성: 변수명 일관성 검토

한국어 변수명(상담Rows, 설정Rows, 지원Rows)이 사용되고 있습니다. 팀 컨벤션에 따라 괜찮을 수 있으나, 국제화나 협업 확장 시 영문 변수명(consultationRows, settingsRows, supportRows)이 권장됩니다. 현재 팀 컨벤션을 따르고 있다면 무시하셔도 됩니다.


45-45: 가독성: noop 함수명 개선 권장

noop이라는 이름은 일반적이지만 의도를 명확히 하기 위해 handleNotImplemented 등으로 변경하면 코드 리뷰 시 의도 파악이 더 쉬워집니다.

- const noop = () => {};
+ const handleNotImplemented = () => {};
src/app/(public)/oauth/callback/[provider]/page.tsx (2)

93-97: 접근성 개선 필요: 로딩 상태 ARIA 속성 누락

레이아웃 구조 변경은 PR의 일관성 있는 변경 방향과 부합합니다. 다만, 로딩 상태의 접근성을 개선할 수 있습니다.

src/components/ui/fallback/LoadingComponent.tsx에는 role="status", aria-live="polite", aria-busy="true" 속성이 있지만, 여기서 사용하는 Loading 컴포넌트에는 이러한 접근성 속성이 없습니다. 스크린 리더 사용자가 로딩 상태를 인지할 수 있도록 개선을 권장합니다.

♿ 접근성 개선 제안
  return (
-   <div className="flex min-h-screen flex-col items-center justify-center p-12">
+   <div 
+     className="flex min-h-screen flex-col items-center justify-center p-12"
+     role="status"
+     aria-live="polite"
+     aria-busy="true"
+   >
      <Loading />
-     <p className="text-sm text-gray-500">로그인 처리 중입니다...</p>
+     <p className="text-sm text-gray-500" aria-label="로그인 처리 중">
+       로그인 처리 중입니다...
+     </p>
    </div>
  );

54-90: 비동기 작업 중 컴포넌트 언마운트 처리 고려

OAuth 로그인 로직은 전반적으로 잘 구현되어 있습니다. 다만, 비동기 작업 진행 중 사용자가 페이지를 이탈하면 언마운트된 컴포넌트에서 상태 업데이트가 시도될 수 있습니다.

현재 코드에서는 store.dispatch를 사용하므로 React 컴포넌트 상태가 아닌 Redux 상태를 업데이트하여 이 문제가 크게 영향을 미치지 않습니다. 그러나 향후 유지보수성을 위해 클린업 패턴을 고려해 볼 수 있습니다.

🛡️ 방어적 코딩 패턴 (선택적)
  useEffect(() => {
    if (called.current) return;
    called.current = true;
+   let isMounted = true;

    // ... validation logic ...

    (async () => {
      try {
        const res = await apiClient.post(`/api/auth/login/${provider}`, null, {
          params: { code },
        });

        // ... token handling ...

+       if (!isMounted) return;
+
        store.dispatch(authActions.setAccessToken(accessToken));
        // ... rest of logic ...
      } catch (e) {
+       if (!isMounted) return;
        console.error('OAuth 로그인 중 에러 발생:', e);
        // ... error handling ...
      }
    })();
+
+   return () => {
+     isMounted = false;
+   };
  }, [router, searchParams, params?.provider]);
src/app/(public)/layout.tsx (1)

14-31: clearTimeout 호출 위치 개선 권장

clearTimeouttrycatch 블록 모두에서 호출되고 있습니다. finally 블록을 사용하면 코드 중복을 줄이고 타임아웃 정리가 보장됩니다.

참고로 src/app/(private)/layout.tsxsrc/app/(policy)/policy/page.tsxcallMe 함수는 이미 finally 패턴을 사용하고 있어 일관성이 필요합니다.

♻️ finally 블록 사용 제안
 async function callMe(cookieHeader: string) {
   const controller = new AbortController();
   const timeoutId = setTimeout(() => controller.abort(), 5000);
   try {
     const response = await fetch(`${API}/api/auth/me`, {
       method: 'GET',
       headers: cookieHeader ? { cookie: cookieHeader } : {},
       cache: 'no-store',
       signal: controller.signal,
     });
-    clearTimeout(timeoutId);
     return response;
   } catch (error) {
-    clearTimeout(timeoutId);
     console.error('Auth check failed:', error);
     return null;
+  } finally {
+    clearTimeout(timeoutId);
   }
 }

@joonhyong joonhyong self-assigned this Jan 29, 2026
@joonhyong joonhyong merged commit 86de450 into develop Jan 29, 2026
6 checks passed
@joonhyong joonhyong deleted the URECA-77/Feat/navbar-update branch January 29, 2026 08:32
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.

[URECA-77] Feat: 네비바 수정

1 participant