Files
vexplor_dev/docs/ycshin-node/PGN[맥락]-페이징-직접입력.md
syc0123 d9611f234e docs: Update pagination navigation documentation and remove obsolete components
- Deleted the outdated `PageGroupNav` component and its related documentation.
- Introduced a new document for the direct input navigation feature in pagination, detailing the rationale for the change and the new user experience.
- Updated the checklist to reflect the completion of the new pagination input feature and its implementation steps.

These changes enhance the clarity and usability of the pagination system in the project.
2026-03-11 14:05:38 +09:00

5.5 KiB

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

관련 문서: 계획서 | 체크리스트


왜 이 작업을 하는가

  • 현재 페이지네이션은 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를 사용하게 되는 문제 발생. 로컬 currentPagesetCurrentPage로 즉시 갱신되므로 이 문제가 없음
  • 발견 과정: 페이지 크기를 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 호출