페이지네이션 (Pagination)
모든 리스트 뷰의 표준 페이지네이션
데이터 표·목록 화면에는 항상 같은 형태의 페이지네이션이 들어갑니다. 페이지 크기는 30개 고정, 버튼은 전부 고스트 .ds-page-btn, 번호 윈도우는 항상 최대 10개예요. 유틸리티 조합으로 버튼을 새로 만들지 말고 반드시 이 클래스를 쓰세요.
규칙 (강제) · 코딩 에이전트용 — 사람은 접힌 채로 두어도 됩니다
모든 리스트/표의 표준 페이지네이션 강제 규칙. 상세·마크업 예시는 아래 본문.
- 페이지 크기 30개 고정, 번호 윈도우 항상 최대 10개. 버튼은 전부 고스트
.ds-page-btn— 유틸 조합으로 새로 만들지 말 것. - 컨테이너
flex items-center justify-between+x-show="totalCount > 0"(1페이지여도 표시, 완전히 비면 숨김). - 좌측 요약
총 <span class="font-medium">N</span>건 중 a-b건(text-xs text-neutral-500).a=(page-1)*limit+1,b=min(page*limit,totalCount). - 우측 그룹 순서
«‹[번호]›»(flex items-center gap-1).«/»=처음/끝,‹/›=±1. - 화살표 =
.ds-page-btn, 번호 =.ds-page-btn ds-page-btn--num+:class="p === page && 'is-current'". .is-current= neutral-200 배경 + neutral-900 + bold(700), outline·언더라인 없음. 비활성은 배경·opacity 안 건드리고 글자만 disable 색(disabled:유틸 넣지 말 것 — CSS가 처리).- 버튼 룩: 28×28px,
border-radius:4px,border:0, 배경 transparent, 글자--color-neutral-60012px, hover 배경--color-neutral-100. - 윈도우 슬라이딩(양끝 클램프):
end=Math.min(total,Math.max(1,cur-4)+9); start=Math.max(1,end-9);. - 배치: admin 풀-높이 탭은 스크롤 컨테이너 안
</table>다음sticky left-0 mt-3(고정 하단 띠 금지); 카드 흐름 앱은 표 아래mt-4sibling. - CSS 스펙 변경 시 admin·team-manager·general-affairs·inventory
main.css4곳 동시 수정(admin이 원본). - 비대상(넣지 말 것): 집계·피벗 화면(전체 로드), 검색 콤보박스·자동완성(limit 20), 급여 워크북(hr-system 전체 렌더). 트리 목록은 최상위 노드 기준 30개씩.
라이브 미리보기
실제 동작과 동일해요. 번호·화살표를 눌러 페이지를 옮겨 보고, 총 건수를 바꿔 10개 윈도우가 슬라이딩·클램프되는 걸 확인하세요.
총 건수
좌측 = 건수 요약, 우측 = 버튼 그룹. 현재 페이지는 neutral-200 배경 + 검정 bold, 나머지·화살표는 고스트(투명 배경, hover 시 neutral-100), 더 이동할 수 없는 화살표(
«‹)는 글자만 disable 색.
단일 출처
| 파트 | 파일 |
|---|---|
| canonical 마크업 | apps/admin/src/views/tabs/employees-content.html (Pagination 블록) |
| CSS 정의 | apps/admin/src/styles/main.css (.ds-page-btn) |
| 페이지 로직 | 각 앱 app.ts의 pageRange() / goToPage() (admin) · goPage() (team-manager·general-affairs) · pageSlice() (inventory) |
CSS는 admin
main.css가 원본이고 team-manager·general-affairs·inventorymain.css에 복제본이 있습니다. 스펙을 바꾸면 4곳을 동시에 수정하세요.
핵심 스펙
레이아웃
- 컨테이너:
flex items-center justify-between,x-show="...totalCount > 0"— 1페이지뿐이어도 항상 표시하고 목록이 완전히 비었을 때만 숨깁니다. - admin 풀-높이 탭(표가 화면을 채우는 레이아웃): 스크롤 컨테이너 안쪽,
</table>바로 다음에sticky left-0 mt-3. 표가 넘치면 같이 가려졌다가 스크롤 끝에서 표 아래 여백과 함께 노출돼요(sticky left-0은 가로 스크롤 시 화면 고정용). 고정 하단 띠는 쓰지 않습니다. - 카드 흐름 레이아웃(team-manager·general-affairs 등): 표 아래
mt-4sibling으로 배치.
좌측 — 건수 요약
총 <span class="font-medium">N</span>건 중 a-b건형식,text-xs text-neutral-500.a = (page-1)*limit + 1,b = min(page*limit, totalCount).
우측 — 버튼 그룹
- 순서:
«‹[번호들]›», 그룹은flex items-center gap-1. - 화살표 의미:
«/»= 처음/끝,‹/›= 이전/다음(±1). - 화살표 =
class="ds-page-btn", 번호 =class="ds-page-btn ds-page-btn--num"+:class="p === page && 'is-current'". - 현재 페이지
.is-current: neutral-200 배경 + neutral-900 글자 + bold(700). outline·언더라인 없음. - 비활성: 배경·opacity는 건드리지 않고 글자만 disable 색 + hover 미발동 — CSS가 처리하므로 마크업에
disabled:유틸을 넣지 않습니다.
번호 윈도우 — 항상 최대 10개
양끝 클램프 슬라이딩 윈도우로 현재 페이지 주변 10개를 보여줍니다.
js
// pageRange 로직
end = Math.min(total, Math.max(1, cur - 4) + 9);
start = Math.max(1, end - 9);
// 1페이지 → 1..10, 마지막 페이지 → (N-9)..N클래스 & 룩
| 항목 | 값 |
|---|---|
| 버튼 크기 | 28 × 28px, border-radius: 4px, border: 0, 배경 transparent |
| 기본 글자 | --color-neutral-600, font-size: 12px |
| hover (현재 아님·비활성 아님) | 배경 --color-neutral-100 |
현재 (.is-current) | 배경 --color-neutral-200 · 글자 --color-neutral-900 · font-weight: 700 |
비활성 (:disabled) | 글자 --color-label-disable (hover 배경 미발동) |
html
<!-- canonical 마크업 (admin 직원관리) -->
<div x-show="paging.employees.totalCount > 0" class="sticky left-0 mt-3 flex items-center justify-between">
<span class="text-xs text-neutral-500">
총 <span x-text="paging.employees.totalCount" class="font-medium"></span>건 중
<span x-text="((paging.employees.page - 1) * paging.employees.limit) + 1"></span>-<span
x-text="Math.min(paging.employees.page * paging.employees.limit, paging.employees.totalCount)"></span>건
</span>
<div class="flex items-center gap-1">
<button @click="goToPage('employees', 1)" :disabled="paging.employees.page <= 1" class="ds-page-btn">«</button>
<button @click="goToPage('employees', paging.employees.page - 1)" :disabled="paging.employees.page <= 1" class="ds-page-btn">‹</button>
<template x-for="p in pageRange('employees')" :key="p">
<button @click="goToPage('employees', p)" class="ds-page-btn ds-page-btn--num"
:class="p === paging.employees.page && 'is-current'" x-text="p"></button>
</template>
<button @click="goToPage('employees', paging.employees.page + 1)" :disabled="paging.employees.page >= paging.employees.totalPages" class="ds-page-btn">›</button>
<button @click="goToPage('employees', paging.employees.totalPages)" :disabled="paging.employees.page >= paging.employees.totalPages" class="ds-page-btn">»</button>
</div>
</div>로직 패턴 (앱별)
- admin — 중앙
paging[tab]객체({ page, limit: 30, totalCount, totalPages }) +goToPage(tab, page)/resetPage(tab)/pageRange(tab). 필터 변경 시resetPage('탭')호출. - team-manager / general-affairs —
pageSize: 30+pageRange(p)+goPage(p, page, () => loadX())헬퍼. 목록별xPagination: { page, totalPages, totalCount }. - inventory (API가 전체 배열 반환 → 클라이언트 페이지네이션) —
pageSlice(arr, page)/pageCount(arr)/pageRange(page, total). 필터 변경 시 페이지 1로 리셋.
트리 목록 (admin 조직 탭)
트리도 페이지네이션을 달되 페이지 단위 = 최상위 노드: 최상위 노드를 30개씩 자르고 각 하위 트리는 같은 페이지에 통째로 렌더 → 계층이 페이지 경계에서 끊기지 않습니다. "총 N건" = 최상위 노드 수.
페이지네이션 비대상 (넣지 말 것)
- 집계·피벗 화면: 잘리면 합계가 틀어짐 (예: 치료 인센티브 월 합계) — 전체 로드.
- 검색 콤보박스·자동완성: limit 20 유지 (직원 검색 등).
- 급여 워크북(hr-system): 스프레드시트형, 의도된 전체 렌더.
표 자체 스펙은 Data table (Table 1)을 참고하세요.
사용처
- admin — 직원 관리·역할 관리·연차/외근 현황·전자서명 등 모든 목록
- team-manager / general-affairs / inventory — 각 목록 뷰 (CSS 복제본 사용)
변경 이력
| 날짜 | 내용 |
|---|---|
| 2026-06-19 | 라이브 미리보기를 페이지 최상단으로 이동(소개 tip 바로 아래, 단일 출처 위) (사용자 요청) |
| 2026-06-19 | 페이지네이션 패턴 페이지 신규 작성 — 30개/페이지·.ds-page-btn 고스트 버튼·10개 윈도우·앱별 로직·비대상 목록 |