129 lines
5.6 KiB
Markdown
129 lines
5.6 KiB
Markdown
# [계획서] 페이징 - 페이지 번호 직접 입력 네비게이션
|
|
|
|
> 관련 문서: [맥락노트](./PGN[맥락]-페이징-직접입력.md) | [체크리스트](./PGN[체크]-페이징-직접입력.md)
|
|
|
|
## 개요
|
|
|
|
v2-table-list 컴포넌트의 하단 페이지네이션 중앙 영역에서, 현재 페이지 번호를 **읽기 전용 텍스트**에서 **입력 가능한 필드**로 변경합니다.
|
|
사용자가 원하는 페이지 번호를 키보드로 직접 입력하여 빠르게 이동할 수 있게 합니다.
|
|
|
|
### 이전 설계(10개 번호 버튼 그룹) 폐기 사유
|
|
|
|
- 10개 버튼은 공간을 많이 차지하고, 모바일에서 렌더링이 어려움
|
|
- 고정 슬롯/고정 너비 등 복잡한 레이아웃 제약이 발생
|
|
- 입력 필드 방식이 더 직관적이고 공간 효율적
|
|
|
|
---
|
|
|
|
## 변경 전 → 변경 후
|
|
|
|
### 페이지네이션 UI
|
|
|
|
```
|
|
변경 전: [<<] [<] 1 / 38 [>] [>>] ← 읽기 전용 텍스트
|
|
변경 후: [<<] [<] [ 15 ] / 49 [>] [>>] ← 입력 가능 필드
|
|
```
|
|
|
|
| 버튼 | 동작 (변경 없음) |
|
|
|------|-----------------|
|
|
| `<<` | 첫 페이지(1)로 이동 |
|
|
| `<` | 이전 페이지(`currentPage - 1`)로 이동 |
|
|
| 중앙 | **입력 필드** `/` **총 페이지** — 사용자가 원하는 페이지 번호를 직접 입력 |
|
|
| `>` | 다음 페이지(`currentPage + 1`)로 이동 |
|
|
| `>>` | 마지막 페이지(`totalPages`)로 이동 |
|
|
|
|
### 입력 필드 동작 규칙
|
|
|
|
| 동작 | 설명 |
|
|
|------|------|
|
|
| 클릭 | 입력 필드에 포커스, 기존 숫자 전체 선택(select all) |
|
|
| 숫자 입력 | 자유롭게 타이핑 가능 (입력 중에는 페이지 이동 안 함) |
|
|
| Enter | 입력한 페이지로 이동 + 포커스 해제 |
|
|
| 포커스 아웃 (blur) | 입력한 페이지로 이동 |
|
|
| 유효 범위 보정 | 1 미만 → 1, totalPages 초과 → totalPages, 빈 값/비숫자 → 현재 페이지 유지 |
|
|
| `< >` 클릭 | 기존대로 한 페이지씩 이동 (입력 필드 값도 갱신) |
|
|
| `<< >>` 클릭 | 기존대로 첫/끝 페이지 이동 (입력 필드 값도 갱신) |
|
|
|
|
### 비활성화 조건 (기존과 동일)
|
|
|
|
- `<<` `<` : `currentPage === 1`
|
|
- `>` `>>` : `currentPage >= totalPages`
|
|
|
|
---
|
|
|
|
## 시각적 동작 예시
|
|
|
|
총 49페이지 기준:
|
|
|
|
| 사용자 동작 | 입력 필드 표시 | 결과 |
|
|
|------------|---------------|------|
|
|
| 초기 상태 | `1 / 49` | 1페이지 표시 |
|
|
| 입력 필드 클릭 | `[1]` 전체 선택됨 | 타이핑 대기 |
|
|
| `28` 입력 후 Enter | `28 / 49` | 28페이지로 이동 |
|
|
| `0` 입력 후 Enter | `1 / 49` | 1로 보정 |
|
|
| `999` 입력 후 Enter | `49 / 49` | 49로 보정 |
|
|
| 빈 값으로 blur | `28 / 49` | 이전 페이지(28) 유지 |
|
|
| `abc` 입력 후 Enter | `28 / 49` | 이전 페이지(28) 유지 |
|
|
| `>` 클릭 | `29 / 49` | 29페이지로 이동 |
|
|
|
|
---
|
|
|
|
## 아키텍처
|
|
|
|
### 데이터 흐름
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A["currentPage (state, 단일 소스)"] --> B["입력 필드 표시값 (pageInputValue)"]
|
|
B -->|"사용자 타이핑"| C["pageInputValue 갱신 (표시만)"]
|
|
C -->|"Enter 또는 blur"| D["유효 범위 보정 (1~totalPages)"]
|
|
D -->|"보정된 값"| E[handlePageChange]
|
|
E --> F["setCurrentPage → useEffect → fetchTableDataDebounced"]
|
|
F --> G[백엔드 API 호출]
|
|
G --> H[데이터 갱신]
|
|
H --> A
|
|
|
|
I["<< < > >> 클릭"] --> E
|
|
J["페이지크기 변경"] --> K["setCurrentPage(1) + setLocalPageSize + onConfigChange"]
|
|
K --> F
|
|
```
|
|
|
|
### 페이징 바 레이아웃
|
|
|
|
```
|
|
┌──────────────────────────────────────────────────────────────┐
|
|
│ [페이지크기 입력] │ << < [__입력__] / n > >> │ [내보내기][새로고침] │
|
|
│ 좌측(유지) │ 중앙(입력필드 교체) │ 우측(유지) │
|
|
└──────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 변경 대상 파일
|
|
|
|
| 구분 | 파일 | 변경 내용 |
|
|
|------|------|----------|
|
|
| 수정 | `TableListComponent.tsx` | (1) `pageInputValue` 상태 + `useEffect` 동기화 + `commitPageInput` 핸들러 추가 |
|
|
| | | (2) paginationJSX 중앙 `<span>` → `<input>` + `/` + `<span>` 교체 |
|
|
| | | (3) `handlePageSizeChange`에 `onConfigChange` 호출 추가 |
|
|
| | | (4) `fetchTableDataInternal`에서 `currentPage`를 단일 소스로 사용 |
|
|
| | | (5) `useMemo` 의존성에 `pageInputValue` 추가 |
|
|
| 삭제 | `PageGroupNav.tsx` | 이전 설계 산출물 삭제 (이미 삭제됨) |
|
|
|
|
- 신규 파일 생성 없음
|
|
- 백엔드 변경 없음, DB 변경 없음
|
|
- v2-table-list를 사용하는 **모든 동적 화면**에 자동 적용
|
|
|
|
---
|
|
|
|
## 설계 원칙
|
|
|
|
- **최소 변경**: `<span>` 1개를 `<input>` + 유효성 검증으로 교체. 나머지 전부 유지
|
|
- **기존 버튼 동작 무변경**: `<< < > >>` 4개 버튼의 onClick/disabled 로직은 그대로
|
|
- **`handlePageChange` 재사용**: 기존 함수를 그대로 호출
|
|
- **입력 중 페이지 이동 안 함**: onChange는 표시만 변경, Enter/blur로 실제 적용
|
|
- **유효 범위 자동 보정**: 1 미만 → 1, totalPages 초과 → totalPages, 비숫자 → 현재 값 유지
|
|
- **포커스 시 전체 선택**: 클릭하면 바로 타이핑 가능
|
|
- **`currentPage`가 단일 소스**: fetch 시 `tableConfig.pagination?.currentPage` 대신 로컬 `currentPage`만 사용 (비동기 전파 문제 방지)
|
|
- **페이지크기 변경 시 1페이지로 리셋**: `handlePageSizeChange`가 `onConfigChange`를 호출하여 부모/백엔드 동기화
|