Files
sam-react-prod/sam-docs/frontend/v1/02-routing-and-pages.md
유병철 c309ac479f feat: [vehicle] 법인차량 관리 모듈 + MES 분석 보고서 + 프론트엔드 문서
- 법인차량 관리 3개 페이지 (차량등록, 운행일지, 정비이력)
- MES 데이터 정합성 분석 보고서 v1/v2
- sam-docs 프론트엔드 기술문서 v1 (9개 챕터)
- claudedocs 가이드/테스트URL 업데이트
2026-03-13 17:52:57 +09:00

4.2 KiB

라우팅 및 페이지 패턴

라우팅 구조

/[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)

'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)

'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} />;
}

네비게이션

// 목록 → 등록
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 추가 금지 (이중 패딩 방지)

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

<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 취소 💾 저장
<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부터)데이터 컬럼작업 컬럼

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

작업 버튼

  • 체크박스 선택 시에만 표시

i18n

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