# [계획서] 페이징 - 페이지 번호 직접 입력 네비게이션

> 관련 문서: [맥락노트](./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`를 호출하여 부모/백엔드 동기화
