Skip to content

Conversation

@seungdeok
Copy link
Member

@seungdeok seungdeok commented Oct 21, 2025

📝 PR 유형

  • 🚀 feature 기능 추가
  • 🐞 버그 발생
  • 🔨 리팩토링
  • 📋 문서작성
  • 🌍 빌드 설정 및 문제
  • ETC

📝 PR 설명

홈 페이지에 탭 기능과 네비게이션 바를 추가하여 사용자가 일일 업무와 회의 일정을 구분하여 볼 수 있도록 구현했습니다.

주요 변경사항

  • 디자인 컴포넌트 추가
  • Storybook 스토리 추가: 새로 추가된 컴포넌트들의 스토리북 스토리 작성

관련된 이슈 넘버

close #78

✅ 작업 목록

  • 디자인 컴포넌트 추가
    • 탭 컴포넌트 추가: 일일 업무와 회의 일정을 구분하는 탭 UI 구현
    • 네비게이션 바 추가: 하단 네비게이션 바 컴포넌트 추가

MR하기 전에 확인해주세요

  • local code lint 검사를 진행하셨나요?
  • local ci test를 진행하셨나요?

📚 논의사항

📚 ETC

Summary by CodeRabbit

  • 새로운 기능

    • 하단 네비게이션 바 추가 (홈, 일정, 문서, 프로필)
    • 확장 가능한 플로팅 액션 버튼 추가 (여러 액션 지원, 토글 애니메이션)
    • 탭 인터페이스 추가 — 일일 업무 / 회의 일정 전환
  • 스토리북

    • 네비게이션 바와 탭 상호작용을 확인할 수 있는 데모 스토리 추가
  • 지역화

    • 탭 레이블 등 UI 텍스트 한국어로 적용
  • Chores

    • 빌드/포스트인스톨 스크립트 정리 및 분리

@seungdeok seungdeok requested a review from widse October 21, 2025 14:20
@seungdeok seungdeok self-assigned this Oct 21, 2025
@coderabbitai
Copy link

coderabbitai bot commented Oct 21, 2025

Walkthrough

홈 화면용 UI 컴포넌트(BottomNavigation, Tabs, FloatingActionButton)와 관련 Storybook 스토리를 추가했고, 일부 컴포넌트의 CSS import 경로를 상대 경로로 변경했으며 package.json에 panda:codegen 스크립트를 추가하고 postinstall을 해당 스크립트로 위임했습니다.

Changes

코호트 / 파일(s) 변경 요약
하단 네비게이션
apps/web/src/views/home/components/BottomNavigation.tsx
고정형 하단 네비게이션 컴포넌트 추가(홈, 일정, 문서, 프로필), usePathname 기반 활성 경로 판별 및 아이콘 활성화 처리.
탭 컴포넌트
apps/web/src/views/home/components/Tabs.tsx, apps/web/src/widgets/home/Tabs.tsx
새 Tabs 컴포넌트 추가(활성 탭 prop/setter), 기존 위젯 Tabs.tsx의 UI 텍스트 한국어화 및 CSS import 경로를 상대 경로로 변경.
플로팅 액션 버튼(FAB)
apps/web/src/views/home/components/FloatingActionButton/FloatingActionButton.tsx, apps/web/src/views/home/components/FloatingActionButton/FloatingActionButton.type.ts, apps/web/src/views/home/components/FloatingActionButton/index.ts, apps/web/src/shared/components/FloatingActionButton/FloatingActionButton.tsx
위치 지정 가능한 FAB 컴포넌트 및 타입 추가, 액션 패널 토글(isOpen)과 액션 아이템 렌더링, 공유 FAB 파일에 "use client" 지시문 추가.
스토리북 스토리
apps/web/src/views/home/home.stories.tsx
Navigation_Bar 스토리 추가(탭은 useState로 상호작용 구현). 기존 FAB 스토리는 유지.
스타일 시스템 import 경로 변경
apps/web/src/widgets/home/TopBanner.tsx, apps/web/src/widgets/home/Tabs.tsx
styled-system/css 외부 import를 ../../../styled-system/css로 상대 경로 전환.
패키지 스크립트
apps/web/package.json
panda:codegen 스크립트 추가 및 postinstallpnpm run panda:codegen으로 변경.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant BottomNavigation
    participant Router as Next.js Router
    participant Tabs
    participant FAB as FloatingActionButton

    rect rgb(240,248,255)
    Note over User,BottomNavigation: 하단 네비게이션 상호작용
    User->>BottomNavigation: 네비게이션 항목 클릭
    BottomNavigation->>Router: 현재 경로 확인(usePathname)
    Router-->>BottomNavigation: 경로 반환
    BottomNavigation->>BottomNavigation: 활성 항목 판별
    BottomNavigation-->>User: 활성 아이콘/링크 렌더링
    end

    rect rgb(255,240,245)
    Note over User,Tabs: 탭 전환 상호작용
    User->>Tabs: 탭 클릭
    Tabs->>Tabs: setActiveTab 호출(상태 변경)
    Tabs-->>User: 활성 탭 UI 업데이트
    end

    rect rgb(245,255,250)
    Note over User,FAB: FAB 확장 상호작용
    User->>FAB: 메인 FAB 클릭
    FAB->>FAB: isOpen 토글
    FAB-->>User: 액션 패널 표시/숨김(애니메이션)
    User->>FAB: 액션 버튼 클릭
    FAB->>FAB: 해당 액션 콜백 실행
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • 추가 검토 권고 파일:
    • BottomNavigation.tsx — 경로 매칭 로직 및 이미지 경로 정확성 확인
    • FloatingActionButton.tsx (components 및 shared) — 포지셔닝, 애니메이션, 접근성(버튼 역할/포커스) 점검
    • 상대 경로로 바뀐 styled-system/css 임포트가 빌드/번들러에서 올바르게 해석되는지 확인

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • widse

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning PR은 몇 가지 범위 외의 변경사항을 포함하고 있습니다. TopBanner.tsx의 CSS import 경로가 "styled-system/css"에서 "../../../styled-system/css"로 변경되었고, 이와 유사한 import 경로 변경이 Tabs.tsx(widgets/home)에도 적용되었으며, package.json에 "panda:codegen" 스크립트가 추가되었습니다. 이러한 변경사항들은 PR 설명에서 명시적으로 언급되지 않으며, 주요 변경사항으로 서술된 "디자인 컴포넌트 추가"와 "Storybook 스토리 추가"의 범위를 벗어나 보입니다. TopBanner.tsx의 import 경로 변경과 package.json의 panda:codegen 스크립트 추가에 대한 명확한 설명이 필요합니다. 이러한 변경사항이 홈 컴포넌트 개발에 필수적인 부분이라면 PR 설명에 추가하거나, 별도의 build/setup 관련 PR로 분리하는 것을 권장합니다.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed PR 제목 "[Feat/#78] 홈 컴포넌트 개발"은 변경 사항의 핵심을 명확하게 요약합니다. 원본 요약에서 보면 새로운 탭 컴포넌트, 하단 네비게이션 바, FAB(Floating Action Button) 등이 추가되었으며, 이는 모두 홈 컴포넌트 개발과 직접적으로 관련이 있습니다. 제목은 간결하고 구체적이며, 팀원이 커밋 히스토리를 스캔할 때 주요 변경 사항을 쉽게 파악할 수 있습니다.

[pre_merge_check_pass]

PR 설명은 제공된 템플릿의 필수 항목을 대부분 충실하게 작성하였습니다. PR 유형(✓ feature), PR 설명(홈 페이지 탭 기능 및 네비게이션 바 추가 설명), 관련 이슈 넘버(close #78), 작업 목록(디자인 컴포넌트 추가 내역), MR 전 확인사항(✓ lint 및 CI 테스트 완료) 등이 모두 포함되어 있습니다. 논의사항과 ETC 섹션은 비어있으나 이는 필수 항목이 아니므로 설명의 완성도에 큰 영향을 주지 않습니다.

[pre_merge_check_pass]

PR의 변경 사항이 링크된 이슈 #78의 요구사항을 충족합니다. 이슈에서 완료(completed)로 표시된 디자인 컴포넌트들이 모두 구현되었습니다: Navigation Bar(BottomNavigation.tsx로 구현 ✓), 탭 컴포넌트(Tabs.tsx로 구현 ✓), FAB(FloatingActionButton 컴포넌트 구현 ✓). 또한 홈.stories.tsx에 Navigation_Bar와 탭 스토리가 추가되어 Storybook 스토리 작성 요구사항도 충족합니다. UI 컴포넌트 항목들(상단 인포, 주간 캘린더, 일일 업무, 노티피케이션 카드)은 이슈에서 미완료로 표시되어 있으므로 이 PR의 범위에서 제외하는 것이 적절합니다. |

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#78

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.

@github-actions
Copy link

📚 Storybook is ready for review!
🔗 Preview: https://68ceb72871701a8b3beb1d8f-wdbzmglpxk.chromatic.com/

Copy link

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

🧹 Nitpick comments (1)
apps/web/src/shared/components/FloatingActionButton/FloatingActionButton.tsx (1)

76-84: 선택적 개선: 액션 아이템에 고유 식별자 사용 권장

현재 key={index.toString()}를 사용하고 있습니다. actions 배열이 재정렬되지 않는다면 문제없지만, 가능하다면 각 액션에 고유한 id 속성을 추가하여 사용하는 것이 React 베스트 프랙티스입니다.

예시:

-            key={index.toString()}
+            key={action.id ?? index.toString()}

참고: 액션 배열이 정적이고 순서가 변하지 않는다면 현재 구현도 충분합니다.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between acdd886 and f139928.

📒 Files selected for processing (3)
  • .github/workflows/chromatic-CD.yml (2 hunks)
  • apps/web/package.json (1 hunks)
  • apps/web/src/shared/components/FloatingActionButton/FloatingActionButton.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Deploy Storybook to Chromatic
  • GitHub Check: Deploy Storybook to Chromatic
🔇 Additional comments (4)
apps/web/package.json (1)

13-14: 스크립트 추가 및 postinstall 훅 업데이트 - 적절한 변경입니다.

Panda CSS 코드 생성을 별도의 재사용 가능한 스크립트로 분리하고, postinstall 훅에서 이를 호출하도록 구성했습니다. 이를 통해 CI/CD 워크플로우 (.github/workflows/chromatic-CD.yml)에서도 동일한 명령을 사용할 수 있어 일관성이 유지됩니다.

.github/workflows/chromatic-CD.yml (2)

19-22: 경로 필터 확장 - 올바른 트리거 조건입니다.

새로 추가된 위젯(widgets) 및 뷰(views) 컴포넌트, Panda CSS 설정, 그리고 package.json을 모니터링하는 경로 필터가 추가되었습니다. 이를 통해 관련된 파일들이 변경되었을 때만 워크플로우가 트리거되어 불필요한 빌드 실행을 방지할 수 있습니다.


49-50: Panda CSS 생성 단계의 적절한 배치입니다.

새로운 "Generate Panda CSS" 단계는 의존성 설치(Install Dependencies) 이후, Chromatic 배포(Publish to Chromatic) 이전에 위치하고 있습니다. 이러한 실행 순서는 논리적으로 올바르며, 생성된 CSS가 스토리북 빌드 및 Chromatic 배포 시 사용되도록 보장합니다. 명령어도 package.json의 새로운 panda:codegen 스크립트와 일치합니다.

apps/web/src/shared/components/FloatingActionButton/FloatingActionButton.tsx (1)

1-1: "use client" 지시문 추가가 적절합니다.

이 컴포넌트는 useState 훅과 인터랙티브 이벤트 핸들러를 사용하므로 Next.js App Router에서 클라이언트 컴포넌트로 마킹하는 것이 필수적입니다. 변경사항이 올바릅니다.

@github-actions
Copy link

📚 Storybook is ready for review!
🔗 Preview: https://68ceb72871701a8b3beb1d8f-nvpflssgvw.chromatic.com/

@github-actions
Copy link

📚 Storybook is ready for review!
🔗 Preview: https://68ceb72871701a8b3beb1d8f-dauyuxhzko.chromatic.com/

Copy link

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

🧹 Nitpick comments (2)
apps/web/src/views/home/components/FloatingActionButton/FloatingActionButton.tsx (2)

79-83: 스타일 적용 방식의 일관성을 유지하세요.

컴포넌트 내에서 css() 헬퍼와 인라인 style 객체를 혼용하고 있습니다. 가능하면 css() 시스템으로 통일하는 것이 일관성과 유지보수성 측면에서 좋습니다.

다음과 같이 리팩토링을 고려하세요:

        {actions?.map((action, index) => (
          <div
            key={index.toString()}
-           style={{
-             transform: isOpen ? "translateY(0)" : `translateY(${40 - index * 20}px)`,
-             transition: "transform 0.3s ease-in-out",
-             transitionDelay: isOpen ? `${(index + 1) * 0.1}s` : "0s",
-           }}
+           className={css({
+             transform: isOpen ? "translateY(0)" : `translateY(${40 - index * 20}px)`,
+             transition: "transform 0.3s ease-in-out",
+             transitionDelay: isOpen ? `${(index + 1) * 0.1}s` : "0s",
+           })}
          >

단, 동적 값(index 기반 계산)이 많아서 인라인 스타일이 더 적합할 수도 있습니다. 팀의 스타일 가이드를 따르세요.


23-27: 위치 스타일 설정을 개선할 수 있습니다.

현재 inline style 객체를 수동으로 구성하고 있는데, 이를 더 간결하게 작성할 수 있습니다.

다음과 같이 리팩토링할 수 있습니다:

-  const style: React.CSSProperties = { position };
-  if (top !== undefined) style.top = top;
-  if (left !== undefined) style.left = left;
-  if (bottom !== undefined) style.bottom = bottom;
-  if (right !== undefined) style.right = right;
+  const style: React.CSSProperties = {
+    position,
+    ...(top !== undefined && { top }),
+    ...(left !== undefined && { left }),
+    ...(bottom !== undefined && { bottom }),
+    ...(right !== undefined && { right }),
+  };
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f139928 and 5daa3c8.

📒 Files selected for processing (6)
  • apps/web/src/views/home/components/BottomNavigation.tsx (1 hunks)
  • apps/web/src/views/home/components/FloatingActionButton/FloatingActionButton.tsx (1 hunks)
  • apps/web/src/views/home/components/FloatingActionButton/FloatingActionButton.type.ts (1 hunks)
  • apps/web/src/views/home/components/FloatingActionButton/index.ts (1 hunks)
  • apps/web/src/views/home/components/Tabs.tsx (1 hunks)
  • apps/web/src/views/home/home.stories.tsx (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
apps/web/src/views/home/home.stories.tsx (3)
apps/web/src/views/home/components/BottomNavigation.tsx (1)
  • BottomNavigation (31-93)
apps/web/src/views/home/types.ts (1)
  • TabsState (47-47)
apps/web/src/views/home/components/Tabs.tsx (1)
  • Tabs (12-88)
apps/web/src/views/home/components/FloatingActionButton/FloatingActionButton.type.ts (1)
apps/web/src/views/home/components/FloatingActionButton/index.ts (1)
  • FloatingActionButtonProps (2-2)
apps/web/src/views/home/components/FloatingActionButton/FloatingActionButton.tsx (2)
apps/web/src/views/home/components/FloatingActionButton/index.ts (2)
  • FloatingActionButton (1-1)
  • FloatingActionButtonProps (2-2)
apps/web/src/views/home/components/FloatingActionButton/FloatingActionButton.type.ts (1)
  • FloatingActionButtonProps (7-20)
apps/web/src/views/home/components/Tabs.tsx (1)
apps/web/src/views/home/types.ts (1)
  • TabsState (47-47)
🔇 Additional comments (4)
apps/web/src/views/home/components/FloatingActionButton/FloatingActionButton.type.ts (1)

1-20: 타입 정의가 명확합니다.

FloatingActionButton의 타입 정의가 잘 구조화되어 있으며, position과 CSS 속성을 유연하게 지원합니다.

apps/web/src/views/home/components/FloatingActionButton/index.ts (1)

1-2: 공개 API가 명확합니다.

컴포넌트와 타입을 적절하게 re-export하고 있습니다.

apps/web/src/views/home/home.stories.tsx (2)

62-71: Navigation_Bar 스토리가 잘 구현되었습니다.

BottomNavigation 컴포넌트를 적절하게 렌더링하고 있습니다.


91-102: 탭 스토리의 상태 관리가 적절합니다.

useState를 사용하여 activeTab 상태를 관리하고 있으며, Storybook의 render 함수 내에서 사용하는 것이 올바릅니다.

})}
>
{NAV_ITEMS.map(item => {
const isActive = pathname === item.href;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

중첩 라우트에서 활성 상태 감지가 작동하지 않을 수 있습니다.

현재 pathname === item.href는 정확히 일치하는 경우만 활성 상태로 표시합니다. 예를 들어 /schedule/detail과 같은 중첩 라우트에서는 일정 탭이 활성 상태로 표시되지 않습니다.

다음과 같이 수정하여 하위 경로도 포함하도록 개선할 수 있습니다:

-          const isActive = pathname === item.href;
+          const isActive = item.href === "/" 
+            ? pathname === item.href 
+            : pathname.startsWith(item.href);
📝 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
const isActive = pathname === item.href;
const isActive = item.href === "/"
? pathname === item.href
: pathname.startsWith(item.href);
🤖 Prompt for AI Agents
In apps/web/src/views/home/components/BottomNavigation.tsx around line 66, the
active check uses strict equality (pathname === item.href) which fails for
nested routes; change it to treat a tab as active when the current pathname
equals the href OR the pathname is a subpath of the href (e.g.
pathname.startsWith(item.href + '/') ), and handle the root '/' href specially
so it only matches exactly; update the isActive assignment accordingly.

Comment on lines 69 to 87
<Link
key={item.href}
href={item.href}
className={css({
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
width: "48px",
height: "48px",
})}
>
<Image
src={`/icons/${item.icon}${isActive ? "-active" : ""}.png`}
alt={item.label}
width={48}
height={48}
/>
</Link>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

접근성 속성이 누락되었습니다.

네비게이션 링크에 다음 접근성 개선사항이 필요합니다:

  • 활성 상태를 나타내는 aria-current="page" 속성 누락
  • 스크린 리더를 위한 텍스트 레이블 부재 (이미지만으로는 불충분)

다음과 같이 수정하여 접근성을 개선하세요:

            <Link
              key={item.href}
              href={item.href}
+             aria-label={item.label}
+             aria-current={isActive ? "page" : undefined}
              className={css({
                display: "flex",
                flexDirection: "column",
                alignItems: "center",
                justifyContent: "center",
                width: "48px",
                height: "48px",
              })}
            >
              <Image
                src={`/icons/${item.icon}${isActive ? "-active" : ""}.png`}
                alt={item.label}
                width={48}
                height={48}
+               aria-hidden="true"
              />
            </Link>
📝 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
<Link
key={item.href}
href={item.href}
className={css({
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
width: "48px",
height: "48px",
})}
>
<Image
src={`/icons/${item.icon}${isActive ? "-active" : ""}.png`}
alt={item.label}
width={48}
height={48}
/>
</Link>
<Link
key={item.href}
href={item.href}
aria-label={item.label}
aria-current={isActive ? "page" : undefined}
className={css({
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
width: "48px",
height: "48px",
})}
>
<Image
src={`/icons/${item.icon}${isActive ? "-active" : ""}.png`}
alt={item.label}
width={48}
height={48}
aria-hidden="true"
/>
</Link>
🤖 Prompt for AI Agents
In apps/web/src/views/home/components/BottomNavigation.tsx around lines 69 to
87, the Link items are missing accessibility attributes: add aria-current="page"
to the Link when the item is active (isActive true) and provide a
screen-reader-friendly label by adding aria-label={item.label} to the Link (or
include a visually-hidden span with the label inside the Link) so assistive tech
can identify the destination; keep the Image alt but ensure it doesn’t duplicate
verbose text.

Comment on lines 42 to 62
<button
className={css({
display: "flex",
alignItems: "center",
justifyContent: "center",
borderRadius: "50%",
width: "56px",
height: "56px",
cursor: "pointer",
border: "none",
backgroundColor: "#3A8DFF",
})}
disabled={disabled}
type="button"
onClick={() => {
setIsOpen(!isOpen);
onClick?.();
}}
>
{isOpen ? (closeIcon ?? icon) : (openIcon ?? icon)}
</button>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

접근성 속성을 추가하세요.

FloatingActionButton에 다음 접근성 속성이 누락되었습니다:

  • aria-expanded: 액션 패널의 열림/닫힘 상태 표시
  • aria-label: 버튼의 용도 설명

다음과 같이 수정하세요:

      <button
        className={css({...})}
        disabled={disabled}
        type="button"
+       aria-expanded={isOpen}
+       aria-label={isOpen ? "액션 메뉴 닫기" : "액션 메뉴 열기"}
        onClick={() => {
          setIsOpen(!isOpen);
          onClick?.();
        }}
      >
        {isOpen ? (closeIcon ?? icon) : (openIcon ?? icon)}
      </button>
📝 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
<button
className={css({
display: "flex",
alignItems: "center",
justifyContent: "center",
borderRadius: "50%",
width: "56px",
height: "56px",
cursor: "pointer",
border: "none",
backgroundColor: "#3A8DFF",
})}
disabled={disabled}
type="button"
onClick={() => {
setIsOpen(!isOpen);
onClick?.();
}}
>
{isOpen ? (closeIcon ?? icon) : (openIcon ?? icon)}
</button>
<button
className={css({
display: "flex",
alignItems: "center",
justifyContent: "center",
borderRadius: "50%",
width: "56px",
height: "56px",
cursor: "pointer",
border: "none",
backgroundColor: "#3A8DFF",
})}
disabled={disabled}
type="button"
aria-expanded={isOpen}
aria-label={isOpen ? "액션 메뉴 닫기" : "액션 메뉴 열기"}
onClick={() => {
setIsOpen(!isOpen);
onClick?.();
}}
>
{isOpen ? (closeIcon ?? icon) : (openIcon ?? icon)}
</button>
🤖 Prompt for AI Agents
In
apps/web/src/views/home/components/FloatingActionButton/FloatingActionButton.tsx
around lines 42 to 62, the button is missing accessibility attributes; add
aria-expanded set to the current isOpen state (aria-expanded={isOpen}) so
assistive tech knows the panel state, and add an aria-label describing the
button’s purpose (use a provided prop if available or a sensible default like
"Open actions"/"Close actions" depending on state). Ensure aria-label is stable
and localized if your app supports i18n.

Comment on lines +76 to +83
{actions?.map((action, index) => (
<div
key={index.toString()}
style={{
transform: isOpen ? "translateY(0)" : `translateY(${40 - index * 20}px)`,
transition: "transform 0.3s ease-in-out",
transitionDelay: isOpen ? `${(index + 1) * 0.1}s` : "0s",
}}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

배열 인덱스를 key로 사용하지 마세요.

index를 key로 사용하면 actions 배열이 재정렬되거나 변경될 때 React의 reconciliation이 비효율적으로 작동하고 애니메이션 상태가 잘못될 수 있습니다.

각 action에 고유한 id 속성을 추가하거나, action의 불변 속성을 조합하여 key를 생성하세요:

Option 1: FloatingActionButtonAction 타입에 id 추가

FloatingActionButton.type.ts를 수정:

export type FloatingActionButtonAction = {
+  id: string;
  icon: React.ReactNode;
  onClick?: () => void;
  disabled?: boolean;
};

그리고 key 사용:

-        {actions?.map((action, index) => (
+        {actions?.map((action) => (
          <div
-           key={index.toString()}
+           key={action.id}

Option 2 (임시 방편): stable한 식별자가 없다면

-           key={index.toString()}
+           key={`action-${index}`}

단, Option 1이 권장되며 actions 배열이 동적으로 변경될 가능성이 있다면 반드시 고유 id를 사용해야 합니다.

📝 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
{actions?.map((action, index) => (
<div
key={index.toString()}
style={{
transform: isOpen ? "translateY(0)" : `translateY(${40 - index * 20}px)`,
transition: "transform 0.3s ease-in-out",
transitionDelay: isOpen ? `${(index + 1) * 0.1}s` : "0s",
}}
{actions?.map((action, index) => (
<div
key={`action-${index}`}
style={{
transform: isOpen ? "translateY(0)" : `translateY(${40 - index * 20}px)`,
transition: "transform 0.3s ease-in-out",
transitionDelay: isOpen ? `${(index + 1) * 0.1}s` : "0s",
}}

Comment on lines +7 to +10
type TabsProps = {
activeTab: TabsState;
setActiveTab: (tab: TabsState) => void;
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

TabsProps 타입 중복 정의를 제거하세요.

TabsProps 타입을 로컬에서 정의하지 말고 types.ts에서 export하여 재사용하는 것이 좋습니다. 이는 타입 일관성을 유지하고 중복을 방지합니다.

apps/web/src/views/home/types.ts에 다음을 추가하세요:

export type TabsProps = {
  activeTab: TabsState;
  setActiveTab: (tab: TabsState) => void;
};

그리고 이 파일에서 import하세요:

-type TabsProps = {
-  activeTab: TabsState;
-  setActiveTab: (tab: TabsState) => void;
-};
+import type { TabsProps } from "@/views/home/types";
🤖 Prompt for AI Agents
In apps/web/src/views/home/components/Tabs.tsx around lines 7 to 10, the
TabsProps type is defined locally and duplicates the type that should be shared;
remove the local TabsProps definition and instead import TabsProps from
apps/web/src/views/home/types.ts, and in apps/web/src/views/home/types.ts add
and export the TabsProps type with the shape { activeTab: TabsState;
setActiveTab: (tab: TabsState) => void; } so the component reuses the
centralized type.

Comment on lines 14 to 87
<ul
className={css({
display: "flex",
flexDirection: "row",
alignItems: "center",
width: "100%",
px: "20px",
bg: "white",
})}
>
<li
onClick={() => setActiveTab(TAB_ITEMS.DAILY)}
className={css({
cursor: "pointer",
flex: 1,
height: "35px",
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
gap: "12px",
})}
>
<Image
src={`/icons/check${activeTab === TAB_ITEMS.DAILY ? "-active" : ""}.png`}
alt={TAB_ITEMS.DAILY}
width={18}
height={18}
/>
<p
className={css({
fontSize: "14px",
lineHeight: "1.4",
letterSpacing: "-0.28px",
fontWeight: "400",
color: activeTab === TAB_ITEMS.DAILY ? "black" : "#B7C2D0",
})}
>
일일 업무
</p>
</li>
<li
onClick={() => setActiveTab(TAB_ITEMS.MEETING)}
className={css({
cursor: "pointer",
flex: 1,
height: "35px",
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
gap: "12px",
})}
>
<Image
src={`/icons/message${activeTab === TAB_ITEMS.MEETING ? "-active" : ""}.png`}
alt={TAB_ITEMS.MEETING}
width={20}
height={20}
/>
<p
className={css({
fontSize: "14px",
lineHeight: "1.4",
letterSpacing: "-0.28px",
fontWeight: "400",
color: activeTab === TAB_ITEMS.MEETING ? "black" : "#B7C2D0",
})}
>
회의 일정
</p>
</li>
</ul>
);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

접근성 및 시맨틱 HTML 개선이 필요합니다.

현재 구현의 문제점:

  1. <li> 요소에 onClick을 사용하는 것은 시맨틱하지 않습니다
  2. WAI-ARIA 탭 패턴 속성이 누락되었습니다 (role="tab", role="tablist", aria-selected)
  3. 키보드 네비게이션 지원이 없습니다 (화살표 키, Enter, Space)

다음과 같이 수정하여 접근성을 준수하세요:

    <ul
+     role="tablist"
      className={css({
        display: "flex",
        flexDirection: "row",
        alignItems: "center",
        width: "100%",
        px: "20px",
        bg: "white",
      })}
    >
-     <li
-       onClick={() => setActiveTab(TAB_ITEMS.DAILY)}
+     <li role="presentation">
+       <button
+         role="tab"
+         aria-selected={activeTab === TAB_ITEMS.DAILY}
+         onClick={() => setActiveTab(TAB_ITEMS.DAILY)}
+         onKeyDown={(e) => {
+           if (e.key === 'ArrowRight') setActiveTab(TAB_ITEMS.MEETING);
+         }}
          className={css({
            cursor: "pointer",
            flex: 1,
            height: "35px",
            display: "flex",
            flexDirection: "row",
            alignItems: "center",
            justifyContent: "center",
            gap: "12px",
+           border: "none",
+           background: "transparent",
          })}
        >
          {/* 기존 내용 */}
+       </button>
-     </li>
+     </li>

두 번째 탭에도 동일하게 적용하되 ArrowLeft로 첫 번째 탭으로 이동하도록 설정하세요.

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 24 to 85
<li
onClick={() => setActiveTab(TAB_ITEMS.DAILY)}
className={css({
cursor: "pointer",
flex: 1,
height: "35px",
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
gap: "12px",
})}
>
<Image
src={`/icons/check${activeTab === TAB_ITEMS.DAILY ? "-active" : ""}.png`}
alt={TAB_ITEMS.DAILY}
width={18}
height={18}
/>
<p
className={css({
fontSize: "14px",
lineHeight: "1.4",
letterSpacing: "-0.28px",
fontWeight: "400",
color: activeTab === TAB_ITEMS.DAILY ? "black" : "#B7C2D0",
})}
>
일일 업무
</p>
</li>
<li
onClick={() => setActiveTab(TAB_ITEMS.MEETING)}
className={css({
cursor: "pointer",
flex: 1,
height: "35px",
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
gap: "12px",
})}
>
<Image
src={`/icons/message${activeTab === TAB_ITEMS.MEETING ? "-active" : ""}.png`}
alt={TAB_ITEMS.MEETING}
width={20}
height={20}
/>
<p
className={css({
fontSize: "14px",
lineHeight: "1.4",
letterSpacing: "-0.28px",
fontWeight: "400",
color: activeTab === TAB_ITEMS.MEETING ? "black" : "#B7C2D0",
})}
>
회의 일정
</p>
</li>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

탭 아이템 렌더링 로직을 추출하여 코드 중복을 제거하세요.

두 탭 간에 구조가 거의 동일하여 코드 중복이 발생하고 있습니다. 배열을 순회하는 방식으로 리팩토링하는 것이 유지보수성을 향상시킵니다.

다음과 같이 리팩토링할 수 있습니다:

const TAB_CONFIG = [
  {
    id: TAB_ITEMS.DAILY,
    icon: "check",
    label: "일일 업무",
    iconSize: { width: 18, height: 18 },
  },
  {
    id: TAB_ITEMS.MEETING,
    icon: "message",
    label: "회의 일정",
    iconSize: { width: 20, height: 20 },
  },
] as const;

export function Tabs({ activeTab, setActiveTab }: TabsProps) {
  return (
    <ul role="tablist" className={css({...})}>
      {TAB_CONFIG.map((tab) => (
        <li key={tab.id} role="presentation">
          <button
            role="tab"
            aria-selected={activeTab === tab.id}
            onClick={() => setActiveTab(tab.id)}
            className={css({...})}
          >
            <Image
              src={`/icons/${tab.icon}${activeTab === tab.id ? "-active" : ""}.png`}
              alt={tab.label}
              width={tab.iconSize.width}
              height={tab.iconSize.height}
            />
            <p className={css({
              fontSize: "14px",
              lineHeight: "1.4",
              letterSpacing: "-0.28px",
              fontWeight: "400",
              color: activeTab === tab.id ? "black" : "#B7C2D0",
            })}>
              {tab.label}
            </p>
          </button>
        </li>
      ))}
    </ul>
  );
}
🤖 Prompt for AI Agents
In apps/web/src/views/home/components/Tabs.tsx around lines 24-85, duplicate JSX
for the two tab items should be extracted into a config-driven render to remove
repetition; create a TAB_CONFIG array containing id, icon, label, and iconSize
for each tab, map over it to render each item, replace the <li> clickable
elements with accessible <button role="tab"> inside list items, wire onClick to
setActiveTab(tab.id), compute icon src and active styles from activeTab ===
tab.id, and keep existing css styles by reusing the style objects so behavior
and appearance remain identical.

@github-actions
Copy link

📚 Storybook is ready for review!
🔗 Preview: https://68ceb72871701a8b3beb1d8f-ilnhnnpthu.chromatic.com/

Copy link

@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

♻️ Duplicate comments (4)
apps/web/src/views/home/components/FloatingActionButton/FloatingActionButton.tsx (2)

39-59: 접근성 속성을 추가하세요.

이전 리뷰에서 지적된 문제가 여전히 해결되지 않았습니다. FloatingActionButton에 다음 접근성 속성이 누락되었습니다:

  • aria-expanded={isOpen}: 액션 패널의 열림/닫힘 상태 표시
  • aria-label: 버튼의 용도 설명

다음과 같이 수정하세요:

      <button
        style={{...}}
        disabled={disabled}
        type="button"
+       aria-expanded={isOpen}
+       aria-label={isOpen ? "액션 메뉴 닫기" : "액션 메뉴 열기"}
        onClick={() => {
          setIsOpen(!isOpen);
          onClick?.();
        }}
      >

73-80: 배열 인덱스를 key로 사용하지 마세요.

이전 리뷰에서 지적된 문제가 여전히 해결되지 않았습니다. index를 key로 사용하면 actions 배열이 재정렬되거나 변경될 때 React의 reconciliation이 비효율적으로 작동하고 애니메이션 상태가 잘못될 수 있습니다.

각 action에 고유한 id 속성을 추가하세요:

FloatingActionButton.type.ts 수정:

export type FloatingActionButtonAction = {
+  id: string;
  icon: React.ReactNode;
  onClick?: () => void;
  disabled?: boolean;
};

컴포넌트에서 key 사용:

-        {actions?.map((action, index) => (
+        {actions?.map((action) => (
          <div
-           key={index.toString()}
+           key={action.id}
apps/web/src/views/home/components/BottomNavigation.tsx (2)

59-59: 중첩 라우트에서 활성 상태 감지가 작동하지 않을 수 있습니다.

현재 pathname === item.href는 정확히 일치하는 경우만 활성 상태로 표시합니다. 예를 들어 /schedule/detail과 같은 중첩 라우트에서는 일정 탭이 활성 상태로 표시되지 않습니다.

다음과 같이 수정하여 하위 경로도 포함하도록 개선할 수 있습니다:

-          const isActive = pathname === item.href;
+          const isActive = item.href === "/" 
+            ? pathname === item.href 
+            : pathname.startsWith(item.href);

62-80: 접근성 속성이 누락되었습니다.

네비게이션 링크에 다음 접근성 개선사항이 필요합니다:

  • 활성 상태를 나타내는 aria-current="page" 속성 누락
  • 스크린 리더를 위한 텍스트 레이블 부재 (이미지만으로는 불충분)

다음과 같이 수정하여 접근성을 개선하세요:

            <Link
              key={item.href}
              href={item.href}
+             aria-label={item.label}
+             aria-current={isActive ? "page" : undefined}
-             style={{
+             className={css({
                display: "flex",
                flexDirection: "column",
                alignItems: "center",
                justifyContent: "center",
                width: "48px",
                height: "48px",
-             }}
+             })}
            >
              <Image
                src={`/icons/${item.icon}${isActive ? "-active" : ""}.png`}
                alt={item.label}
                width={48}
                height={48}
+               aria-hidden="true"
              />
            </Link>
🧹 Nitpick comments (6)
apps/web/src/views/home/components/FloatingActionButton/FloatingActionButton.tsx (4)

58-58: 아이콘 표시 로직이 혼란스럽습니다.

현재 로직에서 isOpen이 true일 때 closeIcon ?? icon을, false일 때 openIcon ?? icon을 표시합니다. 이는 prop 이름과 실제 동작이 일치하지 않아 혼란을 줄 수 있습니다:

  • openIcon은 버튼이 닫혀있을 때(열기 전) 표시됨
  • closeIcon은 버튼이 열려있을 때(닫기 전) 표시됨

의도가 맞다면 prop 이름을 더 명확하게 변경하는 것을 권장합니다.

Option 1: Prop 이름 변경 (권장)

타입 정의를 다음과 같이 수정:

export type FloatingActionButtonProps = {
  // ...
-  openIcon?: React.ReactNode;
-  closeIcon?: React.ReactNode;
+  collapsedIcon?: React.ReactNode;  // 닫힌 상태에서 표시할 아이콘
+  expandedIcon?: React.ReactNode;   // 열린 상태에서 표시할 아이콘

컴포넌트 코드:

-  openIcon,
-  closeIcon,
+  collapsedIcon,
+  expandedIcon,
}: FloatingActionButtonProps) {
  // ...
-  {isOpen ? (closeIcon ?? icon) : (openIcon ?? icon)}
+  {isOpen ? (expandedIcon ?? icon) : (collapsedIcon ?? icon)}

Option 2: 로직 수정 (현재 이름 유지)

만약 openIcon이 "열기 동작을 나타내는 아이콘"이어야 한다면:

-  {isOpen ? (closeIcon ?? icon) : (openIcon ?? icon)}
+  {isOpen ? (openIcon ?? icon) : (closeIcon ?? icon)}

77-77: 매직 넘버를 상수로 추출하세요.

Transform 계산식 translateY(${40 - index * 20}px)에서 사용된 4020은 의미를 파악하기 어렵습니다.

컴포넌트 상단에 상수로 추출:

export function FloatingActionButton({...}: FloatingActionButtonProps) {
  const [isOpen, setIsOpen] = useState(false);
+  const ANIMATION_BASE_OFFSET = 40;
+  const ANIMATION_STAGGER_OFFSET = 20;

  // ...
  
            style={{
-             transform: isOpen ? "translateY(0)" : `translateY(${40 - index * 20}px)`,
+             transform: isOpen ? "translateY(0)" : `translateY(${ANIMATION_BASE_OFFSET - index * ANIMATION_STAGGER_OFFSET}px)`,

39-106: 키보드 네비게이션 지원을 추가하세요.

현재 구현에는 키보드 사용자를 위한 다음 기능이 누락되어 있습니다:

  • Escape 키로 메뉴 닫기
  • Tab/Shift+Tab으로 액션 간 이동
  • 메뉴 닫힐 때 포커스가 메인 버튼으로 복귀

다음과 같이 키보드 이벤트 핸들러를 추가하세요:

export function FloatingActionButton({...}: FloatingActionButtonProps) {
  const [isOpen, setIsOpen] = useState(false);
+  const mainButtonRef = useRef<HTMLButtonElement>(null);

+  useEffect(() => {
+    const handleEscape = (e: KeyboardEvent) => {
+      if (e.key === 'Escape' && isOpen) {
+        setIsOpen(false);
+        mainButtonRef.current?.focus();
+      }
+    };
+    document.addEventListener('keydown', handleEscape);
+    return () => document.removeEventListener('keydown', handleEscape);
+  }, [isOpen]);

  return (
    <div...>
      <button
+       ref={mainButtonRef}
        ...

39-103: 디자인 토큰 사용을 고려하세요.

현재 색상값(#3A8DFF, #ffffff)과 그림자 값이 하드코딩되어 있습니다. 향후 유지보수성과 일관성을 위해 디자인 시스템이나 토큰을 사용하는 것을 권장합니다.

예시 (프로젝트의 디자인 토큰 시스템에 맞게 조정):

import { token } from 'styled-system/tokens';

// 메인 버튼
backgroundColor: token('colors.primary.500'),  // #3A8DFF

// 액션 버튼
backgroundColor: token('colors.white'),
boxShadow: token('shadows.sm'),
apps/web/src/views/home/components/BottomNavigation.tsx (2)

7-28: 타입 안전성을 위해 명시적 타입 정의를 고려하세요.

NAV_ITEMS에 타입을 추가하면 타입 안전성과 IDE 자동완성이 개선됩니다.

다음과 같이 타입을 정의할 수 있습니다:

+type NavItem = {
+  href: string;
+  label: string;
+  icon: string;
+};
+
-const NAV_ITEMS = [
+const NAV_ITEMS: readonly NavItem[] = [
   {
     href: "/",
     label: "홈",
     icon: "home",
   },
   // ...
 ];

33-57: 스타일링 일관성을 위해 Panda CSS 사용을 고려하세요.

프로젝트의 다른 컴포넌트들이 Panda CSS의 css() 함수를 사용하고 있습니다. 일관성을 위해 인라인 스타일 대신 styled-system을 사용하는 것이 좋습니다.

다음과 같이 변경할 수 있습니다:

+"use client";
+
+import { css } from "@/styled-system/css";
 import Image from "next/image";
 import Link from "next/link";
 import { usePathname } from "next/navigation";

 // ...

   return (
     <nav
-      style={{
-        position: "fixed",
-        bottom: "0",
-        left: "0",
-        right: "0",
-        zIndex: 50,
-        display: "flex",
-        justifyContent: "center",
-        padding: "0 16px",
-        backgroundColor: "white",
-        paddingBottom: "env(safe-area-inset-bottom)",
-      }}
+      className={css({
+        position: "fixed",
+        bottom: "0",
+        left: "0",
+        right: "0",
+        zIndex: 50,
+        display: "flex",
+        justifyContent: "center",
+        padding: "0 16px",
+        backgroundColor: "white",
+        paddingBottom: "env(safe-area-inset-bottom)",
+      })}
     >
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5daa3c8 and 15e19f1.

📒 Files selected for processing (3)
  • apps/web/src/views/home/components/BottomNavigation.tsx (1 hunks)
  • apps/web/src/views/home/components/FloatingActionButton/FloatingActionButton.tsx (1 hunks)
  • apps/web/src/views/home/components/Tabs.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/views/home/components/Tabs.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/src/views/home/components/FloatingActionButton/FloatingActionButton.tsx (1)
apps/web/src/views/home/components/FloatingActionButton/FloatingActionButton.type.ts (1)
  • FloatingActionButtonProps (7-20)
🔇 Additional comments (1)
apps/web/src/views/home/components/BottomNavigation.tsx (1)

1-6: "use client" 지시문과 import가 올바르게 구성되었습니다.

usePathname hook을 사용하므로 "use client" 지시문이 필수이며, 필요한 Next.js 모듈들이 적절히 import되었습니다.

Comment on lines +74 to +79
<Image
src={`/icons/${item.icon}${isActive ? "-active" : ""}.png`}
alt={item.label}
width={48}
height={48}
/>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

아이콘 파일의 존재 여부를 확인하세요.

각 네비게이션 아이템에 대해 일반 버전과 활성화 버전의 아이콘 파일이 필요합니다. 누락된 아이콘이 있을 경우 이미지 로딩 오류가 발생합니다.

다음 스크립트로 필요한 아이콘 파일들이 모두 존재하는지 확인하세요:


🏁 Script executed:

#!/bin/bash
# Description: 필요한 아이콘 파일들이 public/icons 디렉토리에 존재하는지 확인

icons=("home" "schedule" "documents" "profile")
missing=0

for icon in "${icons[@]}"; do
  if [ ! -f "public/icons/${icon}.png" ]; then
    echo "❌ Missing: public/icons/${icon}.png"
    missing=1
  fi
  if [ ! -f "public/icons/${icon}-active.png" ]; then
    echo "❌ Missing: public/icons/${icon}-active.png"
    missing=1
  fi
done

if [ $missing -eq 0 ]; then
  echo "✅ All required icon files exist"
else
  echo "⚠️  Some icon files are missing"
  exit 1
fi

Length of output: 347


추가 검증이 필요합니다. BottomNavigation 컴포넌트의 항목 정의와 실제 public 디렉토리의 구조를 확인하겠습니다.


🏁 Script executed:

#!/bin/bash
# Check the BottomNavigation component to see what icons are actually defined
cat apps/web/src/views/home/components/BottomNavigation.tsx

Length of output: 1823


🏁 Script executed:

#!/bin/bash
# Check what files exist in the public directory
find public -type f -name "*.png" 2>/dev/null | head -20

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Check the structure of public/icons directory
ls -la public/icons/ 2>/dev/null || echo "Directory does not exist or is empty"

Length of output: 36


공개 디렉토리에 필수 아이콘 파일이 모두 누락되어 있습니다.

public/icons/ 디렉토리가 없으며, 다음 8개의 아이콘 파일이 필요합니다:

  • home.png, home-active.png
  • schedule.png, schedule-active.png
  • documents.png, documents-active.png
  • profile.png, profile-active.png

현재 상태로는 이미지 로딩이 실패하여 하단 네비게이션이 표시되지 않습니다.

🤖 Prompt for AI Agents
In apps/web/src/views/home/components/BottomNavigation.tsx around lines 74 to
79, the Image component expects icon files under public/icons but that directory
and the eight required PNGs are missing; create a public/icons directory and add
the following files at that path: home.png, home-active.png, schedule.png,
schedule-active.png, documents.png, documents-active.png, profile.png,
profile-active.png (ensure correct filenames and PNG format), commit them to the
repository so the Image src `/icons/{icon}{-active}.png` resolves correctly, and
verify image dimensions/quality match UI expectations.

Comment on lines +82 to +103
<button
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
borderRadius: "50%",
width: "56px",
height: "56px",
cursor: "pointer",
border: "none",
backgroundColor: "#ffffff",
boxShadow: "0 0 5.6px 0 rgba(0, 0, 0, 0.04)",
}}
disabled={action.disabled}
type="button"
onClick={() => {
action.onClick?.();
setIsOpen(false);
}}
>
{action.icon}
</button>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

액션 버튼에 접근성 레이블을 추가하세요.

각 액션 버튼에 aria-label이 누락되어 스크린 리더 사용자가 버튼의 용도를 알 수 없습니다.

FloatingActionButtonAction 타입에 label 추가:

export type FloatingActionButtonAction = {
  id: string;
  icon: React.ReactNode;
+  label: string;  // 접근성을 위한 버튼 설명
  onClick?: () => void;
  disabled?: boolean;
};

버튼에 aria-label 적용:

            <button
              style={{...}}
              disabled={action.disabled}
              type="button"
+             aria-label={action.label}
              onClick={() => {
                action.onClick?.();
                setIsOpen(false);
              }}
            >

Committable suggestion skipped: line range outside the PR's diff.

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.

[due date: 10/22] [✨ Feature] 홈 컴포넌트 개발

1 participant