# 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) ## 예시 ```typescript // 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 (
); } // 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
로딩 중...
; return (
{products.map((product) => (
))}
); } // 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 포함) - 페이지 레이아웃을 이 레이어에 저장 가능 ## 주의사항 1. **독립성 유지**: 각 위젯은 독립적으로 작동 가능해야 합니다 2. **적절한 크기**: 너무 작은 컴포넌트는 `shared/ui`에, 재사용되지 않으면 `pages`에 배치 3. **응집력**: 관련된 features와 entities를 의미 있게 조합 4. **재사용 최적화**: 여러 페이지에서 동일한 방식으로 사용될 수 있도록 설계