# [맥락노트] 페이징 - 페이지 번호 직접 입력 네비게이션 > 관련 문서: [계획서](./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 내부에 인라인으로 구현 - **근거**: 변경이 `` → `` + 핸들러 약 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 호출 ```