129 lines
6.6 KiB
Markdown
129 lines
6.6 KiB
Markdown
# [맥락노트] 페이징 단락(그룹) 번호 네비게이션 - PageGroupNav 공통 컴포넌트
|
|
|
|
> 관련 문서: [계획서](./PGN[계획]-페이징-단락이동.md) | [체크리스트](./PGN[체크]-페이징-단락이동.md)
|
|
|
|
---
|
|
|
|
## 왜 이 작업을 하는가
|
|
|
|
- 현재 페이지네이션은 `1 / 38` 텍스트만 표시하고 `< >`로 한 페이지씩 이동
|
|
- 수십 페이지가 있을 때 원하는 페이지로 빠르게 이동할 수 없음
|
|
- 페이지 번호를 직접 클릭할 수 있어야 UX가 개선됨
|
|
|
|
---
|
|
|
|
## 핵심 결정 사항과 근거
|
|
|
|
### 1. 공통 컴포넌트로 분리 (C안)
|
|
|
|
- **결정**: `PageGroupNav.tsx`라는 순수 컨트롤 컴포넌트를 별도 파일로 생성
|
|
- **근거**: 프로젝트에 페이징이 15곳 이상 존재. 인라인 수정하면 같은 로직을 복사해야 함
|
|
- **대안 검토 A**: v2-table-list 인라인만 수정 → 기각 (미래 확장 시 복사-붙여넣기 기술 부채)
|
|
- **대안 검토 B**: 기존 `Pagination.tsx` 업그레이드 → 기각 (전체 행 레이아웃이 포함되어 v2-table-list와 레이아웃 충돌)
|
|
- **대안 검토 D**: 전체 한번에 적용 → 기각 (12파일 동시 수정은 블래스트 반경이 큼)
|
|
|
|
### 2. 레이아웃 무관 설계
|
|
|
|
- **결정**: PageGroupNav는 `<< < [번호들] > >>`만 렌더링. 외부 레이아웃(페이지크기, 내보내기 등)을 포함하지 않음
|
|
- **근거**: 사용처마다 레이아웃이 다름. v2-table-list는 좌측(페이지크기)+중앙(컨트롤)+우측(내보내기), Pagination.tsx는 좌측(페이지정보)+우측(크기선택+컨트롤). 레이아웃을 강제하면 props 분기가 증가하여 복잡해짐
|
|
|
|
### 3. 10개 단위 단락(그룹)
|
|
|
|
- **결정**: 페이지를 10개씩 묶어 하나의 단락으로 취급
|
|
- **근거**: 사용자에게 익숙한 패턴 (네이버, 구글 등). 5개는 너무 적고, 20개는 너무 많음
|
|
- **확장성**: `groupSize` props로 기본값 10을 변경 가능하게 설계
|
|
|
|
### 4. `< >` = 단락 이동, `<< >>` = 첫/끝 단락
|
|
|
|
- **결정**: `<`는 이전 단락 첫 페이지, `>`는 다음 단락 첫 페이지. `<<`는 1페이지, `>>`는 마지막 단락 첫 페이지
|
|
- **근거**: 사용자 요청. 기존의 "한 페이지씩 이동"은 번호 클릭으로 대체됨
|
|
- **주의**: `>>`는 마지막 **페이지**가 아닌 마지막 **단락의 첫 페이지**로 이동. 예: 총 38페이지일 때 `>>` 클릭 → 31페이지 선택 (38이 아님)
|
|
|
|
### 5. 고정 슬롯 + 고정 너비
|
|
|
|
- **결정**: 항상 10개 슬롯을 렌더링하고, 모든 버튼은 동일한 고정 너비(`w-8 sm:w-9`)
|
|
- **근거**: `< >` 버튼을 연속 클릭할 때 번호 자릿수(1자리→2자리)나 페이지 수(10개→8개) 변화로 버튼 위치가 흔들리면 안 됨
|
|
- **구현**: 마지막 단락에서 페이지가 10개 미만이면 남은 슬롯은 동일 크기의 빈 `<div>`로 채움
|
|
|
|
### 6. 단계적 적용 (1단계: v2-table-list만)
|
|
|
|
- **결정**: 이번 작업은 v2-table-list에만 적용. 나머지는 별도 작업으로 점진 적용
|
|
- **근거**: 15곳 동시 수정은 리스크가 높음. v2-table-list가 가장 많이 사용되므로 여기서 검증 후 확산
|
|
|
|
### 7. 비활성화 기준은 단락 기준
|
|
|
|
- **결정**: `<< <`는 첫 번째 단락일 때 비활성화 (currentPage === 1이 아님). `> >>`는 마지막 단락일 때 비활성화
|
|
- **근거**: 기존은 currentPage 기준이었지만, 단락 이동이므로 단락 기준으로 변경이 자연스러움. 첫 단락 안에서 5페이지에 있어도 `<`는 비활성화
|
|
|
|
---
|
|
|
|
## 관련 파일 위치
|
|
|
|
| 구분 | 파일 경로 | 설명 |
|
|
|------|----------|------|
|
|
| 생성 | `frontend/components/common/PageGroupNav.tsx` | 페이지 그룹 네비게이션 공통 컴포넌트 |
|
|
| 수정 | `frontend/lib/registry/components/v2-table-list/TableListComponent.tsx` | paginationJSX 중앙 영역 교체 (5139~5182행) |
|
|
| 참고 | `frontend/components/common/Pagination.tsx` | 기존 공통 페이지네이션 (이번에 수정 안 함) |
|
|
|
|
---
|
|
|
|
## 기술 참고
|
|
|
|
### 단락 계산 공식
|
|
|
|
```
|
|
groupSize = 10 (기본값)
|
|
currentGroupIndex = Math.floor((currentPage - 1) / groupSize)
|
|
groupStartPage = currentGroupIndex * groupSize + 1
|
|
groupEndPage = Math.min(groupStartPage + groupSize - 1, totalPages)
|
|
|
|
lastGroupIndex = Math.floor((totalPages - 1) / groupSize)
|
|
lastGroupStartPage = lastGroupIndex * groupSize + 1
|
|
|
|
isFirstGroup = currentGroupIndex === 0
|
|
isLastGroup = currentGroupIndex === lastGroupIndex
|
|
```
|
|
|
|
### 고정 슬롯 배열 생성
|
|
|
|
```
|
|
slots = [groupStart, groupStart+1, ..., groupEnd, null, null, ...] (총 groupSize개)
|
|
예: 단락 31~38 → [31, 32, 33, 34, 35, 36, 37, 38, null, null]
|
|
```
|
|
|
|
### handlePageChange 호출 흐름
|
|
|
|
```
|
|
PageGroupNav onPageChange(page)
|
|
→ TableListComponent handlePageChange(newPage)
|
|
→ setCurrentPage(newPage)
|
|
→ useEffect 트리거 → 백엔드 API 재호출 (page 파라미터 변경)
|
|
```
|
|
|
|
- handlePageChange는 `setCurrentPage`만 호출. `onConfigChange` 전파는 제거됨 (pageSize/currentPage는 세션 전용)
|
|
- handlePageChange는 기존 함수 그대로 사용. PageGroupNav가 올바른 page 값을 전달하기만 하면 됨
|
|
|
|
---
|
|
|
|
## 추가 결정: 표시갯수(pageSize) 캐시 정책
|
|
|
|
### 8. pageSize는 세션 전용, DB에 저장 안 함
|
|
|
|
- **결정**: pageSize를 `onConfigChange`로 부모/DB에 전파하지 않음. sessionStorage에만 탭별로 저장
|
|
- **근거**: pageSize는 일시적 탐색 설정이지 영구 화면 설정이 아님. DB에 저장하면 다른 사용자에게도 영향이 가고, 새로고침 시 의도치 않은 값이 남음
|
|
- **F5 정책**: 활성 탭은 캐시 삭제 → 기본값 20으로 fresh start. 비활성 탭은 캐시 유지
|
|
|
|
### 9. 테이블 캐시는 탭별 격리 (탭 ID 스코프)
|
|
|
|
- **결정**: `tableState_*`, `pageSize_*`, `filterSettings_*`, `groupSettings_*` 키를 `{prefix}_{tabId}_{tableName}` 구조로 변경
|
|
- **근거**: 같은 테이블이 여러 탭에서 열릴 수 있음. 탭 구분 없으면 "활성 탭 캐시만 삭제" 불가능
|
|
- **구현**: `useTabId()` 훅으로 현재 탭 ID 접근. `clearTabCache(tabId)`에서 해당 탭의 모든 관련 키 일괄 삭제
|
|
|
|
### 10. localStorage vs sessionStorage 분류
|
|
|
|
- **결정**: 탭별 캐시는 sessionStorage, 사용자 설정은 localStorage
|
|
- **근거**: 탭별 캐시(컬럼 너비 캐시, 필터, 그룹, pageSize)는 탭 닫으면 무의미. 사용자 설정(컬럼 가시성, 순서, 정렬)은 사용자가 의도적으로 변경한 환경설정이므로 세션 간 보존
|
|
- **분류**:
|
|
- sessionStorage: `tableState_*`, `pageSize_*`, `filterSettings_*`, `groupSettings_*`
|
|
- localStorage: `table_column_visibility_*`, `table_sort_state_*`, `table_column_order_*`
|