# [계획서] 페이징 - 페이지 번호 직접 입력 네비게이션 > 관련 문서: [맥락노트](./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 중앙 `` → `` + `/` + `` 교체 | | | | (3) `handlePageSizeChange`에 `onConfigChange` 호출 추가 | | | | (4) `fetchTableDataInternal`에서 `currentPage`를 단일 소스로 사용 | | | | (5) `useMemo` 의존성에 `pageInputValue` 추가 | | 삭제 | `PageGroupNav.tsx` | 이전 설계 산출물 삭제 (이미 삭제됨) | - 신규 파일 생성 없음 - 백엔드 변경 없음, DB 변경 없음 - v2-table-list를 사용하는 **모든 동적 화면**에 자동 적용 --- ## 설계 원칙 - **최소 변경**: `` 1개를 `` + 유효성 검증으로 교체. 나머지 전부 유지 - **기존 버튼 동작 무변경**: `<< < > >>` 4개 버튼의 onClick/disabled 로직은 그대로 - **`handlePageChange` 재사용**: 기존 함수를 그대로 호출 - **입력 중 페이지 이동 안 함**: onChange는 표시만 변경, Enter/blur로 실제 적용 - **유효 범위 자동 보정**: 1 미만 → 1, totalPages 초과 → totalPages, 비숫자 → 현재 값 유지 - **포커스 시 전체 선택**: 클릭하면 바로 타이핑 가능 - **`currentPage`가 단일 소스**: fetch 시 `tableConfig.pagination?.currentPage` 대신 로컬 `currentPage`만 사용 (비동기 전파 문제 방지) - **페이지크기 변경 시 1페이지로 리셋**: `handlePageSizeChange`가 `onConfigChange`를 호출하여 부모/백엔드 동기화