4.5 KiB
4.5 KiB
Widgets Layer
Widgets는 큰 규모의 독립적인 UI 블록을 위한 레이어입니다. 여러 features와 entities를 조합하여 완전한 기능을 제공하는 복합 컴포넌트를 담습니다.
목적
- 큰 규모의 독립적인 UI 블록 구성
- 여러 features와 entities를 하나의 응집력 있는 UI로 결합
- 여러 페이지에서 재사용되는 복잡한 UI 제공
- 페이지 레벨 레이아웃 및 섹션 관리
Widget으로 만들어야 하는 경우
Widget은 다음 조건에 해당할 때 사용하세요:
✅ Widget으로 만들기 좋은 경우:
- 여러 페이지에서 재사용되는 큰 UI 블록
- 페이지에 여러 개의 큰 독립적 블록이 있고, 이것이 그 중 하나인 경우
- 페이지 레벨 레이아웃 컴포넌트 (nested routing 시스템 사용 시)
❌ Widget으로 만들지 말아야 하는 경우:
- UI 블록이 페이지의 대부분을 차지하고 재사용되지 않는 경우 → 페이지 슬라이스 내부에 직접 배치
- 작은 재사용 컴포넌트 →
shared/ui에 배치
구조
각 widget 슬라이스는 다음 세그먼트로 구성됩니다:
widgets/
└── [widget-name]/
├── ui/ # 위젯의 메인 UI 컴포넌트
├── model/ # 위젯 내부 상태 관리 (선택적)
├── api/ # 위젯 전용 API 호출 (선택적)
├── lib/ # 위젯 내부 유틸리티 (선택적)
└── index.ts # Public API
세그먼트 설명
- ui: 여러 features와 entities를 조합한 위젯 UI
- model: 위젯 내부 상태 및 로직 (선택적)
- api: 위젯이 필요로 하는 데이터 fetching (선택적)
- lib: 위젯 내부에서만 사용되는 헬퍼 함수 (선택적)
- index.ts: 슬라이스의 Public API (외부에서 사용할 수 있는 것만 export)
예시
// widgets/header/ui/header.tsx
import { Logo } from 'shared/ui/logo';
import { Navigation } from 'shared/ui/navigation';
import { UserMenu } from 'features/user-menu';
import { NotificationBell } from 'features/notifications';
export function Header() {
return (
<header>
<Logo />
<Navigation />
<NotificationBell />
<UserMenu />
</header>
);
}
// widgets/product-list/ui/product-list.tsx
import { useProducts } from '../api/use-products';
import { ProductCard } from 'entities/product';
import { AddToCartButton } from 'features/cart-add-item';
export function ProductList() {
const { data: products, isLoading } = useProducts();
if (isLoading) return <div>로딩 중...</div>;
return (
<div>
{products.map((product) => (
<div key={product.id}>
<ProductCard product={product} />
<AddToCartButton productId={product.id} />
</div>
))}
</div>
);
}
// widgets/product-list/api/use-products.ts
import { useQuery } from '@tanstack/react-query';
import { getProducts } from 'entities/product';
export function useProducts() {
return useQuery({
queryKey: ['products'],
queryFn: getProducts,
});
}
// widgets/product-list/index.ts
export { ProductList } from './ui/product-list';
레이어 의존성 규칙
✅ 허용되는 의존성:
shared레이어의 모듈entities레이어의 모듈features레이어의 모듈- 같은 widget 슬라이스 내의 다른 세그먼트
❌ 금지된 의존성:
- 다른
widgets슬라이스 (widget 간 직접 의존 금지) pages레이어app레이어
명명 규칙
- 소문자와 대시 사용:
header,sidebar,product-list - 명사 형태 권장: UI 블록의 성격을 나타내는 이름 사용
- 구체적으로: 위젯의 역할이 명확하게 드러나도록
Nested Routing 사용 시
Remix 같은 nested routing 시스템을 사용한다면:
- Widgets 레이어를 flat routing의 Pages 레이어처럼 사용 가능
- 완전한 라우터 블록 생성 (데이터 fetching, loading states, error boundaries 포함)
- 페이지 레이아웃을 이 레이어에 저장 가능
주의사항
- 독립성 유지: 각 위젯은 독립적으로 작동 가능해야 합니다
- 적절한 크기: 너무 작은 컴포넌트는
shared/ui에, 재사용되지 않으면pages에 배치 - 응집력: 관련된 features와 entities를 의미 있게 조합
- 재사용 최적화: 여러 페이지에서 동일한 방식으로 사용될 수 있도록 설계