# [맥락노트] 페이징 - 페이지 번호 직접 입력 네비게이션

> 관련 문서: [계획서](./PGN[계획]-페이징-직접입력.md) | [체크리스트](./PGN[체크]-페이징-직접입력.md)

---

## 왜 이 작업을 하는가

- 현재 페이지네이션은 `1 / 38` 읽기 전용 텍스트만 표시
- 수십 페이지가 있을 때 원하는 페이지로 빠르게 이동할 수 없음 (`>` 연타 필요)
- 페이지 번호를 직접 입력하여 즉시 이동할 수 있어야 UX가 개선됨

---

## 핵심 결정 사항과 근거

### 1. 10개 번호 버튼 그룹 → 입력 필드로 설계 변경

- **결정**: 이전 설계(10개 페이지 번호 버튼 나열)를 폐기하고, 기존 `현재/총` 텍스트에서 현재 부분을 입력 필드로 교체
- **근거**: 10개 버튼은 공간을 많이 차지하고 고정 슬롯/고정 너비 등 복잡한 레이아웃 제약이 발생. 입력 필드 방식이 더 직관적이고 공간 효율적
- **이전 산출물**: `PageGroupNav.tsx` → 삭제 완료

### 2. `<< < > >>` 버튼 동작 유지

- **결정**: 4개 화살표 버튼의 동작은 기존과 완전히 동일하게 유지
- **근거**: 입력 필드가 "원하는 페이지로 점프" 역할을 하므로, 버튼은 기존의 순차 이동(+1/-1, 첫/끝) 그대로 유지하는 것이 자연스러움

### 3. 입력 중에는 페이지 이동 안 함

- **결정**: onChange는 입력 필드 표시만 변경. Enter 또는 blur로 실제 페이지 이동
- **근거**: `28`을 입력하려면 `2`를 먼저 치는데, `2`에서 바로 이동하면 안 됨

### 4. 포커스 시 전체 선택 (select all)

- **결정**: 입력 필드 클릭 시 기존 숫자를 전체 선택
- **근거**: 사용자가 "15페이지로 가고 싶다" → 클릭 → 바로 `15` 타이핑. 기존 값을 지우는 추가 동작 불필요

### 5. 유효 범위 자동 보정

- **결정**: 1 미만 → 1, totalPages 초과 → totalPages, 빈 값/비숫자 → 현재 페이지 유지
- **근거**: 에러 메시지보다 자동 보정이 UX에 유리
- **대안 검토**: 입력 자체를 숫자만 허용 → 기각 (백스페이스로 비울 때 불편)

### 6. `inputMode="numeric"` 사용

- **결정**: `type="text"` + `inputMode="numeric"`
- **근거**: `type="number"`는 브라우저별 스피너 UI가 추가되고, 빈 값 처리가 어려움. `inputMode="numeric"`은 모바일에서 숫자 키보드를 띄우면서 text 입력의 유연성 유지

### 7. 신규 컴포넌트 분리 안 함

- **결정**: v2-table-list의 paginationJSX 내부에 인라인으로 구현
- **근거**: 변경이 `<span>` → `<input>` + 핸들러 약 30줄 수준으로 매우 작음

### 8. `currentPage`를 fetch의 단일 소스로 사용

- **결정**: `fetchTableDataInternal`에서 `tableConfig.pagination?.currentPage || currentPage` 대신 `currentPage`만 사용
- **근거**: `handlePageSizeChange`에서 `setCurrentPage(1)` + `onConfigChange(...)` 호출 시, `onConfigChange`를 통한 부모의 `tableConfig` 갱신은 다음 렌더 사이클에서 전파됨. fetch가 실행되는 시점에 `tableConfig.pagination?.currentPage`가 아직 이전 값(예: 4)이고 truthy이므로 로컬 `currentPage`(1) 대신 4를 사용하게 되는 문제 발생. 로컬 `currentPage`는 `setCurrentPage`로 즉시 갱신되므로 이 문제가 없음
- **발견 과정**: 페이지 크기를 20→40으로 변경하면 1페이지로 설정되지만 리스트가 빈 상태로 표시되는 버그로 발견

### 9. `handlePageSizeChange`에서 `onConfigChange` 호출 필수

- **결정**: 페이지 크기 변경 시 `onConfigChange`로 `{ pageSize, currentPage: 1 }`을 부모에게 전달
- **근거**: 기존 코드는 `setLocalPageSize` + `setCurrentPage(1)`만 호출하고 `onConfigChange`를 호출하지 않았음. 이로 인해 부모 컴포넌트의 `tableConfig.pagination`이 갱신되지 않아 후속 동작에서 stale 값 참조 가능
- **발견 과정**: 위 8번과 같은 맥락에서 발견

---

## 관련 파일 위치

| 구분 | 파일 경로 | 설명 |
|------|----------|------|
| 수정 | `frontend/lib/registry/components/v2-table-list/TableListComponent.tsx` | paginationJSX 중앙 입력 필드 + fetch 소스 수정 |
| 삭제 | `frontend/components/common/PageGroupNav.tsx` | 이전 설계 산출물 (삭제 완료) |

---

## 기술 참고

### 로컬 입력 상태와 실제 페이지 상태 분리

```
pageInputValue (string) — 입력 필드에 표시되는 값 (사용자가 타이핑 중일 수 있음)
currentPage (number) — 실제 현재 페이지 (API 호출의 단일 소스)

동기화:
- currentPage 변경 시 → useEffect → setPageInputValue(String(currentPage))
- Enter/blur 시 → commitPageInput → parseInt + clamp → handlePageChange(보정된 값)
```

### handlePageChange 호출 흐름

```
입력 필드 Enter/blur
  → commitPageInput()
    → parseInt + clamp(1, totalPages)
    → handlePageChange(clampedPage)
      → setCurrentPage(clampedPage) + onConfigChange
        → useEffect 트리거 → fetchTableDataDebounced
          → fetchTableDataInternal(page = currentPage)
            → 백엔드 API 호출
```

### handlePageSizeChange 호출 흐름

```
좌측 페이지크기 입력 onChange/onBlur
  → handlePageSizeChange(newSize)
    → setLocalPageSize(newSize)
    → setCurrentPage(1)
    → sessionStorage 저장
    → onConfigChange({ pageSize: newSize, currentPage: 1 })
      → useEffect 트리거 → fetchTableDataDebounced
        → fetchTableDataInternal(page = 1, pageSize = newSize)
          → 백엔드 API 호출
```
