ERP-node/docs/ycshin-node/PGN[맥락]-페이징-직접입력.md

116 lines
5.5 KiB
Markdown

# [맥락노트] 페이징 - 페이지 번호 직접 입력 네비게이션
> 관련 문서: [계획서](./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 호출
```