# 라우팅 및 페이지 패턴

## 라우팅 구조

```
/[locale]/(auth)/login            # 로그인
/[locale]/(protected)/dashboard   # 대시보드
/[locale]/(protected)/{domain}/{feature}          # 목록
/[locale]/(protected)/{domain}/{feature}?mode=new # 등록
/[locale]/(protected)/{domain}/{feature}/[id]     # 상세(view)
/[locale]/(protected)/{domain}/{feature}/[id]?mode=edit  # 수정
```

## 레이아웃 계층

```
Root Layout ([locale]/layout.tsx) - Server Component
  ├── i18n 설정 (NextIntlClientProvider)
  ├── 폰트 로드 (PretendardVariable)
  ├── Toaster (sonner)
  └── Protected Layout ((protected)/layout.tsx) - Client Component
       ├── useAuthGuard() - 인증 보호
       ├── RootProvider - 전역 상태
       ├── ApiErrorProvider - 401 에러 처리
       ├── FCMProvider - 푸시 알림
       ├── PermissionGate - 권한 제어
       └── AuthenticatedLayout
            ├── Sidebar - 메뉴
            ├── Header - 회사선택, 검색, 알림
            ├── HeaderFavoritesBar - 즐겨찾기
            └── {children} - 페이지 컨텐츠
```

## 페이지 모드 패턴 (mode 쿼리파라미터)

### 규칙
- **별도 `/new`, `/edit` 경로 금지** → `?mode=new`, `?mode=edit` 사용
- 목록과 등록을 **같은 page.tsx에서 분기**

### 목록 + 등록 (page.tsx)

```tsx
'use client';
import { useSearchParams } from 'next/navigation';

export default function ItemsPage() {
  const searchParams = useSearchParams();
  const mode = searchParams.get('mode');

  // mode=new → 등록 폼
  if (mode === 'new') {
    return <ItemDetail mode="new" />;
  }

  // 기본 → 목록
  return <ItemList />;
}
```

### 상세 + 수정 ([id]/page.tsx)

```tsx
'use client';
import { useParams, useSearchParams } from 'next/navigation';

export default function ItemDetailPage() {
  const params = useParams();
  const searchParams = useSearchParams();
  const id = params.id as string;
  const mode = searchParams.get('mode') === 'edit' ? 'edit' : 'view';

  return <ItemDetail id={id} mode={mode} />;
}
```

### 네비게이션

```tsx
// 목록 → 등록
router.push('/master-data/items?mode=new');

// 목록 → 상세
router.push(`/master-data/items/${id}`);

// 상세 → 수정
router.push(`/master-data/items/${id}?mode=edit`);

// 수정 → 상세 (저장 후)
router.push(`/master-data/items/${id}`);

// → 목록으로
router.push('/master-data/items');
```

## 페이지 레이아웃 표준

### PageLayout 패딩 규칙
- `AuthenticatedLayout`의 `<main>`에는 패딩 없음
- `PageLayout` 컴포넌트가 `p-3 md:p-6` 패딩 담당
- **page.tsx에서 패딩 wrapper 추가 금지** (이중 패딩 방지)

### 등록/수정/상세 페이지 헤더

```tsx
<div className="flex items-center justify-between">
  <h1 className="text-xl font-bold">페이지 제목</h1>
  <Button variant="link" className="text-muted-foreground"
    onClick={() => router.push(listPath)}>
    ← 목록으로
  </Button>
</div>
```

### 하단 Sticky 액션 바 (필수)

폼 페이지 하단에 sticky bar로 버튼 배치:

| 모드 | 좌측 | 우측 |
|------|------|------|
| 등록 (new) | `X 취소` | `💾 저장` |
| 상세 (view) | `X 취소` (목록으로) | `✏️ 수정` |
| 수정 (edit) | `X 취소` | `💾 저장` |

```tsx
<div className="sticky bottom-0 bg-white border-t shadow-sm">
  <div className="px-3 py-3 md:px-6 md:py-4 flex items-center justify-between">
    <Button variant="outline" onClick={() => router.push(listPath)}>
      <X className="h-4 w-4 mr-1" />
      취소
    </Button>
    <Button onClick={handleSubmit} disabled={isSubmitting}>
      {isSubmitting
        ? <Loader2 className="h-4 w-4 mr-1 animate-spin" />
        : <Save className="h-4 w-4 mr-1" />}
      저장
    </Button>
  </div>
</div>
```

## 테이블 표준

### 필수 컬럼 구조
**체크박스** → **번호(1부터)** → **데이터 컬럼** → **작업 컬럼**

```tsx
// 번호 계산 (페이지네이션 고려)
const globalIndex = (currentPage - 1) * pageSize + index + 1;
```

### 작업 버튼
- 체크박스 선택 시에만 표시

## i18n

```
지원 언어: ko (기본), en, ja
경로: /ko/..., /en/..., /ja/...
```
