description: "Views 레이어 (페이지) 구조, 데이터 페칭, 서버/클라이언트 경계" paths:
페이지 단위의 UI와 로직을 관리한다. FSD 방식을 따른다.
views/
└── (페이지명)/ 예: chart, companies, tag
├── page.tsx 메인 페이지 컴포넌트 (필수, 서버 컴포넌트)
├── detail/
│ └── page.tsx 상세 페이지 (서버 컴포넌트)
├── ui/ 페이지 전용 UI 컴포넌트
└── utils/ 페이지 전용 유틸리티
serverFetch 사용)searchParams, params 를 받아 정렬/필터/날짜 등을 처리// ✅ views/tag/detail/page.tsx (서버 컴포넌트)
export default async function TagDetailPage({ tagId, sort }: Props) {
const detail = await getTagDetail(tagId, sort);
if (!detail) notFound();
return (
<div>
<TagHero detail={detail} />
<SortControl sort={sort} />
<StreamerGrid streamers={detail.streamers} />
</div>
);
}
app/ 디렉토리의 page.tsx 는 얇은 래퍼 역할만 한다.
params, searchParams 를 파싱해서 view 컴포넌트에 전달generateMetadata 를 정의해 SEO 메타데이터 제공// ✅ app/tags/[tagId]/page.tsx (얇은 래퍼)
export default async function Page({ params, searchParams }: Props) {
const { tagId } = await params;
const { sort } = await searchParams;
return <TagDetailPage tagId={parseInt(tagId, 10)} sortParam={sort} />;
}
'use client', ui/ 폴더)// ✅ 서버 페이지에서 클라이언트 모달 사용
// ui/tag-suggest-trigger.tsx ('use client')
export function TagSuggestTrigger() {
const [open, setOpen] = useState(false);
return (
<>
<button onClick={() => setOpen(true)}>제안하기</button>
<TagSuggestModal open={open} onClose={() => setOpen(false)} />
</>
);
}
// page.tsx (서버 컴포넌트)
import { TagSuggestTrigger } from './ui/tag-suggest-trigger';
export default async function Page() {
return <TagSuggestTrigger />;
}
ui/ 에 둔다shared/ui/ 또는 widget/ 으로 승격'use client' 직접 선언 (인터랙션은 ui/ 로 분리)아직 피드백이 없어요. 첫 번째로 의견을 남겨보세요!