2025-12-09 20:28:58 +09:00
|
|
|
# SAM API 페이지네이션 정책
|
|
|
|
|
|
|
|
|
|
**작성일**: 2025-12-09
|
2025-12-30 22:55:09 +09:00
|
|
|
**수정일**: 2025-12-30
|
2025-12-09 20:28:58 +09:00
|
|
|
**적용 범위**: 모든 목록 조회 API
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 1. 응답 구조
|
|
|
|
|
|
2025-12-30 22:55:09 +09:00
|
|
|
### 1.1 표준 페이지네이션 응답 (Laravel 기본)
|
2025-12-09 20:28:58 +09:00
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"success": true,
|
|
|
|
|
"message": "조회되었습니다.",
|
2025-12-30 22:55:09 +09:00
|
|
|
"data": {
|
2025-12-09 20:28:58 +09:00
|
|
|
"current_page": 1,
|
2025-12-30 22:55:09 +09:00
|
|
|
"data": [
|
|
|
|
|
{ "id": 1, "name": "항목1" },
|
|
|
|
|
{ "id": 2, "name": "항목2" }
|
|
|
|
|
],
|
|
|
|
|
"first_page_url": "http://sam.kr/api/v1/items?page=1",
|
2025-12-09 20:28:58 +09:00
|
|
|
"from": 1,
|
2025-12-30 22:55:09 +09:00
|
|
|
"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
|
2025-12-09 20:28:58 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 1.2 응답 구조 설명
|
|
|
|
|
|
|
|
|
|
| 키 | 타입 | 설명 |
|
|
|
|
|
|----|------|------|
|
|
|
|
|
| `success` | boolean | 요청 성공 여부 |
|
|
|
|
|
| `message` | string | 응답 메시지 (i18n 키) |
|
2025-12-30 22:55:09 +09:00
|
|
|
| `data` | object | **페이지네이션 객체** (Laravel 기본) |
|
2025-12-09 20:28:58 +09:00
|
|
|
|
2025-12-30 22:55:09 +09:00
|
|
|
### 1.3 data 객체 필드
|
2025-12-09 20:28:58 +09:00
|
|
|
|
|
|
|
|
| 필드 | 타입 | 설명 |
|
|
|
|
|
|------|------|------|
|
|
|
|
|
| `current_page` | int | 현재 페이지 번호 (1부터 시작) |
|
2025-12-30 22:55:09 +09:00
|
|
|
| `data` | array | **실제 데이터 배열** |
|
2025-12-09 20:28:58 +09:00
|
|
|
| `per_page` | int | 페이지당 항목 수 |
|
|
|
|
|
| `total` | int | 전체 항목 수 |
|
|
|
|
|
| `last_page` | int | 마지막 페이지 번호 |
|
|
|
|
|
| `from` | int\|null | 현재 페이지 첫 번째 항목 번호 |
|
|
|
|
|
| `to` | int\|null | 현재 페이지 마지막 항목 번호 |
|
2025-12-30 22:55:09 +09:00
|
|
|
| `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 | 페이지 링크 배열 |
|
2025-12-09 20:28:58 +09:00
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 2. 요청 파라미터
|
|
|
|
|
|
|
|
|
|
### 2.1 표준 파라미터
|
|
|
|
|
|
|
|
|
|
| 파라미터 | 타입 | 기본값 | 설명 |
|
|
|
|
|
|----------|------|--------|------|
|
|
|
|
|
| `page` | int | 1 | 페이지 번호 |
|
|
|
|
|
| `size` | int | 20 | 페이지당 항목 수 (max: 100) |
|
|
|
|
|
|
|
|
|
|
### 2.2 요청 예시
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
GET /api/v1/items?page=1&size=20
|
|
|
|
|
GET /api/v1/items?page=2&size=50&search=스크린
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 3. 구현 가이드
|
|
|
|
|
|
|
|
|
|
### 3.1 Controller 구현
|
|
|
|
|
|
|
|
|
|
```php
|
2025-12-30 22:55:09 +09:00
|
|
|
public function index(IndexRequest $request)
|
2025-12-09 20:28:58 +09:00
|
|
|
{
|
2025-12-30 22:55:09 +09:00
|
|
|
$items = $this->service->getItems($request->validated());
|
|
|
|
|
|
|
|
|
|
return ApiResponse::success($items, __('message.fetched'));
|
2025-12-09 20:28:58 +09:00
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 3.2 Service 구현
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2025-12-30 22:55:09 +09:00
|
|
|
### 3.3 최대 페이지 크기 제한
|
2025-12-09 20:28:58 +09:00
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
$size = min($params['size'] ?? 20, 100); // 최대 100개
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- 기본값: 20
|
|
|
|
|
- 최대값: 100
|
|
|
|
|
- 서버 부하 방지 및 응답 시간 최적화
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2025-12-30 22:55:09 +09:00
|
|
|
## 4. 프론트엔드 사용 가이드
|
2025-12-09 20:28:58 +09:00
|
|
|
|
2025-12-30 22:55:09 +09:00
|
|
|
### 4.1 TypeScript 타입 정의
|
2025-12-09 20:28:58 +09:00
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
interface PaginatedResponse<T> {
|
|
|
|
|
success: boolean;
|
|
|
|
|
message: string;
|
2025-12-30 22:55:09 +09:00
|
|
|
data: {
|
2025-12-09 20:28:58 +09:00
|
|
|
current_page: number;
|
2025-12-30 22:55:09 +09:00
|
|
|
data: T[];
|
|
|
|
|
first_page_url: string;
|
2025-12-09 20:28:58 +09:00
|
|
|
from: number | null;
|
2025-12-30 22:55:09 +09:00
|
|
|
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;
|
2025-12-09 20:28:58 +09:00
|
|
|
to: number | null;
|
2025-12-30 22:55:09 +09:00
|
|
|
total: number;
|
2025-12-09 20:28:58 +09:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2025-12-30 22:55:09 +09:00
|
|
|
### 4.2 React 사용 예시
|
2025-12-09 20:28:58 +09:00
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
const fetchItems = async (page: number, size: number) => {
|
|
|
|
|
const response = await api.get<PaginatedResponse<Item>>('/items', {
|
|
|
|
|
params: { page, size }
|
|
|
|
|
});
|
|
|
|
|
|
2025-12-30 22:55:09 +09:00
|
|
|
// 데이터 접근
|
|
|
|
|
const items = response.data.data.data;
|
2025-12-09 20:28:58 +09:00
|
|
|
|
|
|
|
|
// 페이지네이션 정보
|
2025-12-30 22:55:09 +09:00
|
|
|
const { current_page, total, last_page } = response.data.data;
|
2025-12-09 20:28:58 +09:00
|
|
|
|
2025-12-30 22:55:09 +09:00
|
|
|
return { items, pagination: response.data.data };
|
2025-12-09 20:28:58 +09:00
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
2025-12-30 22:55:09 +09:00
|
|
|
### 4.3 간편 접근 패턴
|
2025-12-09 20:28:58 +09:00
|
|
|
|
2025-12-30 22:55:09 +09:00
|
|
|
```typescript
|
|
|
|
|
// 구조 분해로 간결하게 사용
|
|
|
|
|
const { data: pagination } = response.data;
|
|
|
|
|
const items = pagination.data;
|
|
|
|
|
const { current_page, total, last_page, per_page } = pagination;
|
2025-12-09 20:28:58 +09:00
|
|
|
```
|
|
|
|
|
|
2025-12-30 22:55:09 +09:00
|
|
|
---
|
2025-12-09 20:28:58 +09:00
|
|
|
|
2025-12-30 22:55:09 +09:00
|
|
|
## 5. 설계 원칙
|
2025-12-09 20:28:58 +09:00
|
|
|
|
2025-12-30 22:55:09 +09:00
|
|
|
### 5.1 Laravel 기본 형식 유지
|
2025-12-09 20:28:58 +09:00
|
|
|
|
2025-12-30 22:55:09 +09:00
|
|
|
- 프레임워크 기본 동작 활용
|
|
|
|
|
- 추가 변환 로직 불필요
|
|
|
|
|
- 생태계 호환성 유지
|
2025-12-09 20:28:58 +09:00
|
|
|
|
2025-12-30 22:55:09 +09:00
|
|
|
### 5.2 일관성 우선
|
|
|
|
|
|
|
|
|
|
- 기존 API와 동일한 형식 유지
|
|
|
|
|
- 프론트엔드 코드 재사용
|
|
|
|
|
- 학습 비용 최소화
|
2025-12-09 20:28:58 +09:00
|
|
|
|
2025-12-30 22:55:09 +09:00
|
|
|
### 5.3 최대 페이지 크기 제한
|
2025-12-09 20:28:58 +09:00
|
|
|
|
2025-12-30 22:55:09 +09:00
|
|
|
- 서버 부하 방지
|
|
|
|
|
- 응답 시간 최적화
|
|
|
|
|
- DoS 공격 방어
|
2025-12-09 20:28:58 +09:00
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2025-12-30 22:55:09 +09:00
|
|
|
## 6. 관련 문서
|
2025-12-09 20:28:58 +09:00
|
|
|
|
2025-12-30 22:55:09 +09:00
|
|
|
- [API 개발 규칙](./api-rules.md)
|