| name | scc-admin-add-page |
| description | Next.js 어드민에 새 페이지를 추가합니다. App Router, Panda CSS, React Query 패턴을 따릅니다. |
SCC Admin - 새 페이지 추가
입력 정보
- 페이지 이름 - kebab-case (예: my-new-page)
- 페이지 설명 - 페이지의 목적과 기능
- API 엔드포인트 (선택) - 사용할 Admin API
Step 1: 페이지 디렉토리 생성
app/(private)/
└── {page-name}/
├── page.tsx # 메인 페이지
├── components/ # (선택) 페이지 전용 컴포넌트
└── [id]/ # (선택) 동적 라우트
└── page.tsx
Step 2: 페이지 컴포넌트 구현
기본 템플릿 (목록 페이지)
'use client';
import { useQuery } from '@tanstack/react-query';
import { css } from '@/styles/css';
import { flex, stack } from '@/styles/patterns';
import { defaultApi } from '@/lib/apis/api';
export default function MyNewPage() {
const { data, isLoading, error } = useQuery({
queryKey: ['my-data'],
queryFn: () => defaultApi.getMyData(),
});
if (isLoading) return <div>로딩 중...</div>;
if (error) return <div>에러가 발생했습니다</div>;
return (
<div className={stack({ gap: '16px' })}>
<h1 className={css({ fontSize: '24px', fontWeight: 'bold' })}>
페이지 제목
</h1>
<div className={flex({ gap: '8px', wrap: 'wrap' })}>
{data?.items.map((item) => (
<ItemCard key={item.id} item={item} />
))}
</div>
</div>
);
}
기본 템플릿 (폼 페이지)
'use client';
import { useForm } from 'react-hook-form';
import { useMutation } from '@tanstack/react-query';
import { css } from '@/styles/css';
import { stack } from '@/styles/patterns';
import { defaultApi } from '@/lib/apis/api';
interface FormData {
name: string;
description?: string;
}
export default function MyFormPage() {
const { register, handleSubmit, formState: { errors } } = useForm<FormData>();
const mutation = useMutation({
mutationFn: (data: FormData) => defaultApi.createItem(data),
onSuccess: () => {
alert('저장되었습니다');
},
onError: () => {
alert('에러가 발생했습니다');
},
});
return (
<form onSubmit={handleSubmit((data) => mutation.mutate(data))}>
<div className={stack({ gap: '16px' })}>
<label className={css({ display: 'flex', flexDirection: 'column', gap: '4px' })}>
이름
<input
{...register('name', { required: '이름을 입력해주세요' })}
className={css({ padding: '8px', border: '1px solid #ccc', borderRadius: '4px' })}
/>
{errors.name && <span className={css({ color: 'red' })}>{errors.name.message}</span>}
</label>
<button
type="submit"
disabled={mutation.isPending}
className={css({
padding: '12px 24px',
backgroundColor: '#007bff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
_disabled: { opacity: 0.5 },
})}
>
{mutation.isPending ? '저장 중...' : '저장'}
</button>
</div>
</form>
);
}
Step 3: API 연동
API 함수 추가 (필요한 경우)
lib/apis/api.ts에 API 호출 함수 추가:
export const getMyData = async () => {
const { data } = await defaultApi.getMyData();
return data;
};
Step 4: 네비게이션 링크 추가
사이드바나 헤더에 새 페이지 링크 추가 (해당하는 경우).
Panda CSS 패턴
주요 패턴
import { css } from '@/styles/css';
import { flex, stack, grid } from '@/styles/patterns';
<div className={css({ padding: '16px', backgroundColor: 'white' })}>
// Flex 레이아웃
<div className={flex({ gap: '8px', align: 'center', justify: 'space-between' })}>
// Stack (수직 정렬)
<div className={stack({ gap: '16px' })}>
// Grid 레이아웃
<div className={grid({ columns: 3, gap: '16px' })}>
반응형 스타일
css({
padding: { base: '8px', md: '16px', lg: '24px' },
fontSize: { base: '14px', md: '16px' },
})
필수 체크리스트
페이지 구조
스타일링
API 연동
폼 처리
검증
pnpm panda
pnpm lint
pnpm typecheck
참조