Skip to content

feat: 상단 네비게이션 바 / 하단 탭바 공통 컴포넌트 구현 - #28

Open
sohxxny wants to merge 8 commits into
developfrom
feat/issue-11
Open

feat: 상단 네비게이션 바 / 하단 탭바 공통 컴포넌트 구현#28
sohxxny wants to merge 8 commits into
developfrom
feat/issue-11

Conversation

@sohxxny

@sohxxny sohxxny commented Aug 10, 2026

Copy link
Copy Markdown
Member

Summary

shared/ui에 상단 네비게이션 바(TopNavBar)와 하단 탭바(BottomTabBar)를 독립 컴포넌트로 구현했습니다. 뒤로가기 버튼은 Figma의 glass 이펙트를 근사한 반투명 배경으로, 하단 탭바는 홈/유저 2개 탭에 활성 상태에 따라 아이콘(채움/윤곽선)과 pill 배경이 토글되도록 만들었습니다.

Related Issues

PR Point (To Reviewer)

  • TopNavBar의 status(Default/navigation) variant와 5-dot 진행 인디케이터는 이번 구현 범위에서 제외했습니다 (후속 이슈에서 필요 시 별도 작업).
  • 뒤로가기 버튼의 배경은 Figma 상 GLASS 네이티브 이펙트(굴절/스페큘러 하이라이트)라 CSS로 정확히 재현이 불가능해서, mix-blend-mode 기반 반투명 레이어로 근사했습니다 — 배경색에 따라 톤이 자연스럽게 반응합니다.
  • BottomTabBar는 지금은 홈/유저 2개로 고정이지만, 탭 목록을 TABS 배열로 데이터화하고 Tab 타입을 거기서 파생시켜서 나중에 탭이 늘어나도 로직 쪽은 큰 변경 없이 확장 가능합니다. 컨테이너 너비도 고정값이 아니라 내용에 맞춰 늘어나도록 했습니다.
  • 두 컴포넌트 모두 라우팅/상태를 모르는 controlled 컴포넌트로 설계했습니다 (onBack, onTabChange 콜백으로 위임) — 실제 페이지 이동은 후속 라우팅 이슈에서 연결하면 됩니다.

Screenshot

스크린샷 내용
TopNavBar - 우측 아이콘 있음 pr-nav-with-icon
TopNavBar - 우측 아이콘 없음 pr-nav-without-icon
BottomTabBar - 홈 탭 활성 pr-tab-home
BottomTabBar - 유저 탭 활성 pr-tab-user

ETC

없음

@sohxxny sohxxny self-assigned this Aug 10, 2026
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
aligner Ready Ready Preview Aug 11, 2026 10:00am

Comment thread src/shared/ui/top-nav-bar/TopNavBar.tsx Outdated
className,
}: TopNavBarProps) {
return (
<div className={cn(TOP_NAV_BAR_BASE, className)}>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

header 태그를 사용해볼 수 있을 것 같네요!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

수정했습니다! (f1b86f0)

Comment thread src/shared/ui/top-nav-bar/theme.ts Outdated

export const CENTER_SLOT = "flex min-w-0 flex-1 items-center justify-center";

export const TITLE = "typo-headline-emphasized truncate text-gray-10";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

TopNavBar를 붙여보니 화면마다 타이틀 텍스트 스타일(폰트 크기 등)이 조금씩 다르게 쓰이는 경우가 있더라구요. title: string prop으로 스타일(typo-headline-emphasized)을 고정해두기보다, children만 받아서 호출하는 쪽에서 원하는 타이포로 직접 렌더링하게 하면 더 유연할 것 같습니다!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

children만 받도록 수정했습니다~! (8366f25)

@@ -0,0 +1,20 @@
export const TOP_NAV_BAR_BASE = "flex h-[5.6rem] w-full items-center gap-[1rem] px-6";

export const BACK_BUTTON_WRAPPER =

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

BackButton도 스타일이 고정되어 있는데, 실제로는 화면마다 조금씩 다른 CSS가 적용되는 경우가 있더라구요. 디자인팀에 확인해서 스타일을 하나로 통일하거나, 그게 어렵다면 className을 열어서 화면별로 커스텀할 수 있게 해주면 좋을 것 같습니다!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

혹시 어디 부분이 다른지 알 수 있을까요?! 제가 hifi 확인했을 때는 다 같은 스타일같아서 여쭤봅니다!

Comment thread src/shared/ui/top-nav-bar/theme.ts Outdated
@@ -0,0 +1,20 @@
export const TOP_NAV_BAR_BASE = "flex h-[5.6rem] w-full items-center gap-[1rem] px-6";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

h-[5.6rem], gap-[1rem]처럼 rem을 직접 쓰는 arbitrary value와 px-6처럼 테일윈드 기본 스페이싱 단위가 한 파일 안에 섞여 있네요! 둘 중 하나로 통일하면 좋을 것 같습니다!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

고민하다가 쓸수있는건 토큰으로 썼었는데,, 통일하는게 맞을 것 같네요 수정했습니다!!

Comment thread src/shared/ui/top-nav-bar/theme.ts Outdated

export const BACK_BUTTON_WRAPPER =
"relative size-[4.4rem] shrink-0 rounded-full shadow-[0px_0.8rem_4rem_0px_color-mix(in_srgb,var(--color-black)_10%,transparent)]";
export const BACK_BUTTON_RING_CLIP =

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

BackButton 안에 absolute로 겹쳐놓은 레이어가 5개나 되네요(링, 링 반짝임, 채우기 테두리, 흰색 채우기, 대각선 그라데이션, 버튼 본체).
그리고 그 중 대각선 그라데이션 하나는 background 값이 너무 길고 복잡해서 코드만 보면 뭘 하는 스타일인지 바로 이해하기 어려운 것 같아요. 레이어를 좀 줄이거나 긴 그라데이션 값만이라도 변수로 빼서 이름을 붙여주면 나중에 읽기 훨씬 편할 것 같습니다!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

변수로 빼려고 했었는데 조금 애매해지는것 같아 최대한 레이어를 줄이고 하나로 합쳤고, 주석을 추가했습니다! (7be269a)

import TabItem from "./TabItem";
import { BOTTOM_TAB_BAR_BASE } from "./theme";

const TABS = [

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

현재 develop에 머지되어있는 route 타입에서 필요한 타입만 pick 해서 사용해볼 수 있을 것 같습니다~

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

"my"도 route에 추가했습니당 (8ec4544)


export default function BottomTabBar({ activeTab, onTabChange, className }: BottomTabBarProps) {
return (
<div className={cn(BOTTOM_TAB_BAR_BASE, className)}>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nav 태그 사용해볼 수 있을 것 같아요~

Comment thread src/shared/ui/bottom-tab-bar/theme.ts Outdated
@@ -0,0 +1,8 @@
export const BOTTOM_TAB_BAR_BASE =

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

BottomTabBar 스타일을 theme.ts로 따로 빼신 이유가 따로 있을까요?? 지금 보니 클래스가 몇 줄 안 되고, TopNavBar처럼 레이어가 복잡한 것도 아니라서 굳이 파일을 분리하지 않고 컴포넌트 안에 인라인으로 둬도 충분할 것 같아요!

그리고 shadow-[0px_0px_4rem_0px_color-mix(...)]처럼 매번 arbitrary value로 shadow를 새로 정의하시는데, 비슷한 shadow가 다른 컴포넌트에서도 반복된다면 토큰이나 공용 유틸로 빼는 것은 어떻게 생각하시나요??

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

좋습니다! 추후에도 쓰일 것 같아서 디자인 토큰으로 분리했습니다.
그리고 BottomTabBar에서 쓰이는 theme.ts도 인라인으로 붙이고 삭제했습니다! (7be269a)

@thwjddlqslek thwjddlqslek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

수고하셨습니다!

onBack: () => void;
className?: string;
} & (
| { rightIcon?: undefined; rightIconLabel?: undefined; onRightIconClick?: undefined }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

베이스 prop 과 분리해서 RightIconSlot 타입으로 별도 선언하면 가독성이 좋아질 것 같아용

Comment thread src/shared/ui/top-nav-bar/TopNavBar.tsx Outdated
return (
<div className={cn(TOP_NAV_BAR_BASE, className)}>
<BackButton onBack={onBack} />
<div className={CENTER_SLOT}>{children ?? <span className={TITLE}>{title}</span>}</div>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

제안) title/children 둘 다 옵셔널이라 사용처에서 동시에 넘기면 타입 에러 없이 title이 무시돼요. rightIcon 트리플처럼 discriminated union으로 묶어서 둘 중 하나만 넘기게 강제하는 방법 적용해도 좋을 것 같아요. 다른 방안 있으면 그것도 편하게 제안해주세요!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

동희님이 title이 여러가지 타이포를 쓴다고 알려주셔서 children으로만 받도록 수정했습니다! RightIconSlot도 분리하였습니당 (8366f25)

BottomTabBar에서 쓸 홈/유저 아이콘(채움·윤곽선)을 Figma에서 다운로드해
shared/assets/icons/mono에 추가하고 codegen으로 shared/ui/icons에 반영.
shared/ui 컴포넌트로 뒤로가기 버튼, 타이틀(또는 커스텀 children), 우측
아이콘(prop 슬롯)을 지원. 뒤로가기 버튼은 Figma의 glass 이펙트를 근사한
반투명 배경(배경색에 따라 톤이 달라짐)과 좌상단/우하단 하이라이트 링을
별도 컴포넌트(BackButton)로 분리해 구현. rightIcon 유무와 관계없이
오른쪽 슬롯 너비를 항상 예약해 타이틀 중심이 흔들리지 않도록 함.
shared/ui 컴포넌트로 홈/유저 탭, 활성 탭에 따라 배경 pill과 아이콘
(채움/윤곽선)이 토글되는 하단 탭바 구현. 탭 목록은 TABS 배열로 데이터화해
중복 없이 관리하고, 배열에서 파생한 Tab 타입을 index.ts로 공개. 컨테이너
너비는 고정값 대신 내용에 맞춰 늘어나도록 해서 탭 개수 변경에 대응 가능.
그림자 색상은 top-nav-bar와 동일하게 color-mix()로 토큰 참조.
리뷰 반영: 루트 div를 각각 header, nav로 교체
리뷰 반영:
- title/children 동시 전달 시 title이 무시되는 문제 해결 (children만 사용)
- 화면별로 다른 타이틀 타이포를 호출부가 직접 렌더링하도록 변경
- rightIcon 관련 타입을 RightIconSlot으로 분리
- 반복되는 그림자 값을 styles/tokens/shadow.css의 --shadow-* 토큰으로 추출
  (Figma에 이름 붙은 스타일은 아니라 토큰 대응표에 값 그대로 기재)
- BottomTabBar theme.ts 제거, 클래스를 컴포넌트에 인라인으로 통합
- BackButton: px-6(Tailwind 기본 토큰)과 arbitrary rem 혼용 정리,
  overflow-hidden이 자기 자신의 box-shadow는 클리핑하지 않는 점을 활용해
  RING_CLIP/FILL_WHITE를 WRAPPER에, FILL_CLIP/FILL_DIAGONAL을 FILL로
  병합 (DOM 레이어 7개 -> 4개), 각 상수 역할 설명 주석 추가
리뷰 반영: TABS의 id를 route 타입(ROUTES)에서 필요한 키만 Pick해서
검증하도록 변경. "user" id를 route 키에 맞춰 "my"로 수정.
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.

feat: 상단 네비게이션 바 / 하단 탭바(2 아이템) 공통 컴포넌트 구현

3 participants