diff --git a/docs/conventions/component-convention.md b/docs/conventions/component-convention.md
index 401208a..6d048bf 100644
--- a/docs/conventions/component-convention.md
+++ b/docs/conventions/component-convention.md
@@ -40,6 +40,7 @@ interface RentalItemCardProps {
- 예: Figma `Chip/Chip` → `import { Chip } from '@wanteddev/wds'`
- 아이콘은 `@wanteddev/wds-icon`에서 가져온다.
- **WDS 컴포넌트 내부를 임의로 오버라이드하지 않는다.** 간격·배치 같은 레이아웃 조정은 감싸는 wrapper에서 한다.
+- **화면 헤더는 화면에서 `TopNavigation`을 직접 새로 조립하지 않고 `src/components/ui/ScreenHeader.tsx`를 거친다.** Figma의 Top Navigation 패턴(타이틀 정렬, leading/trailing 조합, 게시판류의 토글형 타이틀 등)을 `ScreenHeader`의 props(`variant`/`title`/`leading`/`trailing`)로 고르게 돼 있다 — 화면마다 손으로 다시 조립하면 컨벤션이 흩어진다. 새 헤더 패턴이 필요하면 `ScreenHeader`부터 확장한다(`docs/plans/unified-screen-header.md` 참고).
## 4. Stream 고유 UI (신규 컴포넌트)
diff --git a/src/components/ui/ScreenHeader.tsx b/src/components/ui/ScreenHeader.tsx
index 18e3877..a67881f 100644
--- a/src/components/ui/ScreenHeader.tsx
+++ b/src/components/ui/ScreenHeader.tsx
@@ -1,33 +1,88 @@
-import { TopNavigation, TopNavigationButton } from "@wanteddev/wds";
-import { IconBell, IconSearch } from "@wanteddev/wds-icon";
+import { TopNavigation, Typography } from "@wanteddev/wds";
+import type { ReactNode } from "react";
-interface ScreenHeaderProps {
- title: string;
+interface ScreenHeaderToggleTitle {
+ options: string[];
+ activeIndex: number;
+ onChange?: (index: number) => void;
}
-// Figma: Top Navigation/Resource/Contents의 타이틀 + 검색/알림 아이콘 부분 — 행사·빌릴게 등
-// 여러 화면에서 완전히 동일하게 반복되는 진짜 공통 패턴이라 재사용 컴포넌트로 뺐다.
-// "Tool" 슬롯(세그먼트 토글 등)은 화면마다 값·동작이 달라서(대여/반납 vs 행사/신청내역)
-// 여기 포함하지 않고 각 화면이 자기 본문에서 직접 그린다.
-// background 기본값(true)은 iOS 반투명 스타일이라 뒤 배경이 비쳐 보인다. Figma는 별도 배경 없이
-// 화면 배경을 그대로 쓴다.
-function ScreenHeader({ title }: ScreenHeaderProps) {
+type ScreenHeaderTitle = string | ScreenHeaderToggleTitle;
+
+function isToggleTitle(
+ title: ScreenHeaderTitle,
+): title is ScreenHeaderToggleTitle {
+ return typeof title !== "string";
+}
+
+// Figma: 게시판류 화면의 "공지 | 열린피드백" 같은 2단 탭 타이틀(nodeId 1256:81792 "Board Title").
+// 활성 옵션은 Label/Strong(검정), 비활성은 Label/Disable(흐림) — 둘 다 같은 Title 3/Bold(24px).
+function ScreenHeaderToggleTitle({
+ options,
+ activeIndex,
+ onChange,
+}: ScreenHeaderToggleTitle) {
+ return (
+
+ {options.map((option, index) => {
+ const active = index === activeIndex;
+ return (
+
+ );
+ })}
+
+ );
+}
+
+type ScreenHeaderProps =
+ | {
+ variant?: "display";
+ title?: ScreenHeaderTitle;
+ trailing?: ReactNode;
+ }
+ | {
+ variant: "normal";
+ title?: ScreenHeaderTitle;
+ leading?: ReactNode;
+ trailing?: ReactNode;
+ };
+
+// Figma: Top Navigation/Resource/Contents — 화면마다 따로 조립하던 헤더를 여기 하나로 모았다.
+// WDS `TopNavigation`을 감싸는 얇은 조합 레이어일 뿐, 내부 스타일은 오버라이드하지 않는다
+// (component-convention.md "WDS 컴포넌트 내부를 임의로 오버라이드하지 않는다").
+//
+// variant="display"(기본값)에서는 leading을 받지 않는다 — WDS 쪽 스타일 자체가 display일 때
+// leading/trailing 포지셔닝(topNavigationLeftIconStyle/RightIconStyle)을 안 줘서 레이아웃이
+// 깨진다. leading이 필요한 화면(뒤로가기·닫기 버튼 등)은 variant="normal"을 쓴다.
+//
+// title이 문자열이면 그대로 렌더링하고, { options, activeIndex } 형태(활성 상태가 있는 경우)면
+// 게시판류의 토글형 2단 타이틀로 렌더링한다.
+//
+// search variant(타이틀 자리가 검색 필드로 바뀌는 패턴)는 이번 범위에서 뺐다 —
+// docs/plans/unified-screen-header.md 참고. 화면이 실제로 생기면 그때 추가한다.
+function ScreenHeader(props: ScreenHeaderProps) {
+ const { title, trailing } = props;
+ const leading = props.variant === "normal" ? props.leading : undefined;
+
return (
-
-
-
-
-
-
- >
- }
- variant="display"
+ leadingContent={leading}
+ trailingContent={trailing}
+ variant={props.variant ?? "display"}
>
- {title}
+ {title !== undefined &&
+ (isToggleTitle(title) ? : title)}
);
}
diff --git a/src/features/bililge/BililgeListScreen.tsx b/src/features/bililge/BililgeListScreen.tsx
index 17c716a..a6725e6 100644
--- a/src/features/bililge/BililgeListScreen.tsx
+++ b/src/features/bililge/BililgeListScreen.tsx
@@ -1,4 +1,9 @@
-import { SegmentedControl, SegmentedControlItem } from "@wanteddev/wds";
+import {
+ SegmentedControl,
+ SegmentedControlItem,
+ TopNavigationButton,
+} from "@wanteddev/wds";
+import { IconBell, IconSearch } from "@wanteddev/wds-icon";
import { startTransition, useState } from "react";
import ScreenHeader from "@/components/ui/ScreenHeader";
@@ -18,7 +23,21 @@ function BililgeListScreen() {
const [rentalItem, setRentalItem] = useState(null);
const [rentalSheetOpen, setRentalSheetOpen] = useState(false);
- useScreenHeader();
+ useScreenHeader(
+
+
+
+
+
+
+
+ >
+ }
+ />,
+ );
return (
diff --git a/src/features/home/HomeScreen.tsx b/src/features/home/HomeScreen.tsx
index 350b856..3f44520 100644
--- a/src/features/home/HomeScreen.tsx
+++ b/src/features/home/HomeScreen.tsx
@@ -1,3 +1,5 @@
+import { TopNavigationButton } from "@wanteddev/wds";
+import { IconBell, IconSearch } from "@wanteddev/wds-icon";
import { Link } from "react-router-dom";
import ScreenHeader from "@/components/ui/ScreenHeader";
@@ -5,7 +7,21 @@ import { useScreenHeader } from "@/components/ui/useScreenHeader";
// 홈 화면 콘텐츠는 아직 없어서, 라우팅이 실제로 동작하는지 확인할 placeholder만 둔다.
function HomeScreen() {
- useScreenHeader(
);
+ useScreenHeader(
+
+
+
+
+
+
+
+ >
+ }
+ />,
+ );
return (