- 개발팀 전용 폴더 dev/ 생성 (standards, guides, quickstart, changes, deploys, data, history, dev_plans 이동) - 프론트엔드 전용 폴더 frontend/ 생성 (api/ → frontend/api-specs/) - 기획팀 폴더 requests/ 생성 - plans/ → dev/dev_plans/ 이름 변경 - README.md 신규 (사람용 안내), INDEX.md 재작성 (Claude Code용) - resources.md 신규 (노션 링크용, assets/brochure 이관 예정) - CURRENT_WORKS.md 삭제, TODO.md → dev/ 이동 - 전체 참조 경로 업데이트 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4.8 KiB
4.8 KiB
SAM API 페이지네이션 정책
작성일: 2025-12-09 수정일: 2025-12-30 적용 범위: 모든 목록 조회 API
1. 응답 구조
1.1 표준 페이지네이션 응답 (Laravel 기본)
{
"success": true,
"message": "조회되었습니다.",
"data": {
"current_page": 1,
"data": [
{ "id": 1, "name": "항목1" },
{ "id": 2, "name": "항목2" }
],
"first_page_url": "http://sam.kr/api/v1/items?page=1",
"from": 1,
"last_page": 5,
"last_page_url": "http://sam.kr/api/v1/items?page=5",
"links": [
{ "url": null, "label": "« Previous", "active": false },
{ "url": "http://sam.kr/api/v1/items?page=1", "label": "1", "active": true },
{ "url": "http://sam.kr/api/v1/items?page=2", "label": "Next »", "active": false }
],
"next_page_url": "http://sam.kr/api/v1/items?page=2",
"path": "http://sam.kr/api/v1/items",
"per_page": 20,
"prev_page_url": null,
"to": 20,
"total": 100
}
}
1.2 응답 구조 설명
| 키 | 타입 | 설명 |
|---|---|---|
success |
boolean | 요청 성공 여부 |
message |
string | 응답 메시지 (i18n 키) |
data |
object | 페이지네이션 객체 (Laravel 기본) |
1.3 data 객체 필드
| 필드 | 타입 | 설명 |
|---|---|---|
current_page |
int | 현재 페이지 번호 (1부터 시작) |
data |
array | 실제 데이터 배열 |
per_page |
int | 페이지당 항목 수 |
total |
int | 전체 항목 수 |
last_page |
int | 마지막 페이지 번호 |
from |
int|null | 현재 페이지 첫 번째 항목 번호 |
to |
int|null | 현재 페이지 마지막 항목 번호 |
first_page_url |
string | 첫 페이지 URL |
last_page_url |
string | 마지막 페이지 URL |
next_page_url |
string|null | 다음 페이지 URL |
prev_page_url |
string|null | 이전 페이지 URL |
path |
string | 기본 경로 |
links |
array | 페이지 링크 배열 |
2. 요청 파라미터
2.1 표준 파라미터
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
page |
int | 1 | 페이지 번호 |
size |
int | 20 | 페이지당 항목 수 (max: 100) |
2.2 요청 예시
GET /api/v1/items?page=1&size=20
GET /api/v1/items?page=2&size=50&search=스크린
3. 구현 가이드
3.1 Controller 구현
public function index(IndexRequest $request)
{
$items = $this->service->getItems($request->validated());
return ApiResponse::success($items, __('message.fetched'));
}
3.2 Service 구현
public function getItems(array $params): LengthAwarePaginator
{
$size = min($params['size'] ?? 20, 100); // 최대 100개
return Model::query()
->when($params['search'] ?? null, fn($q, $s) => $q->where('name', 'like', "%{$s}%"))
->orderByDesc('created_at')
->paginate($size);
}
3.3 최대 페이지 크기 제한
$size = min($params['size'] ?? 20, 100); // 최대 100개
- 기본값: 20
- 최대값: 100
- 서버 부하 방지 및 응답 시간 최적화
4. 프론트엔드 사용 가이드
4.1 TypeScript 타입 정의
interface PaginatedResponse<T> {
success: boolean;
message: string;
data: {
current_page: number;
data: T[];
first_page_url: string;
from: number | null;
last_page: number;
last_page_url: string;
links: Array<{
url: string | null;
label: string;
active: boolean;
}>;
next_page_url: string | null;
path: string;
per_page: number;
prev_page_url: string | null;
to: number | null;
total: number;
};
}
4.2 React 사용 예시
const fetchItems = async (page: number, size: number) => {
const response = await api.get<PaginatedResponse<Item>>('/items', {
params: { page, size }
});
// 데이터 접근
const items = response.data.data.data;
// 페이지네이션 정보
const { current_page, total, last_page } = response.data.data;
return { items, pagination: response.data.data };
};
4.3 간편 접근 패턴
// 구조 분해로 간결하게 사용
const { data: pagination } = response.data;
const items = pagination.data;
const { current_page, total, last_page, per_page } = pagination;
5. 설계 원칙
5.1 Laravel 기본 형식 유지
- 프레임워크 기본 동작 활용
- 추가 변환 로직 불필요
- 생태계 호환성 유지
5.2 일관성 우선
- 기존 API와 동일한 형식 유지
- 프론트엔드 코드 재사용
- 학습 비용 최소화
5.3 최대 페이지 크기 제한
- 서버 부하 방지
- 응답 시간 최적화
- DoS 공격 방어