[계획서] 페이징 - 페이지 번호 직접 입력 네비게이션
관련 문서: 맥락노트 | 체크리스트
개요
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페이지로 이동 |
아키텍처
데이터 흐름
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를 호출하여 부모/백엔드 동기화