페이지네이션 (Pagination)
모든 리스트 뷰의 표준 페이지네이션
데이터 표·목록 화면에는 항상 같은 형태의 페이지네이션이 들어갑니다. 페이지 크기는 기본 30이고 사용자가 30 / 50 / 100 / 전체(최대 1,000) 중 고를 수 있어요. 버튼은 전부 고스트 .ds-page-btn, 번호 윈도우는 항상 최대 10개예요. 유틸리티 조합으로 버튼을 새로 만들지 말고 반드시 이 클래스를 쓰세요.
규칙 (강제) · 코딩 에이전트용
모든 리스트/표의 표준 페이지네이션 강제 규칙. 상세·마크업 예시는 아래 본문.
사용자 승인 예외 — system-admin
app-versions만: S3 Bundle 목록과 표준 정책 변경 이력은 기본 10, 선택지는 10 / 20 / 30 / 50 / 100 / 전체다. 유효한 기존 선택은 유지하며 두 표의 선택을 각각 기억한다. 외관·배치·번호 윈도우·전체 상한은 공통 규칙을 따른다. 다른 화면의 기본값과 선택지는 바꾸지 않는다.페이지 크기 기본 30, 사용자가 30 / 50 / 100 / 전체 중 고를 수 있다(선택기는 좌측 요약 옆
.ds-select ds-select--28). 전체 = 최대 1,000건 — 서버paginationSchema의limit상한과 같다. 전체 건수가 1,000을 넘으면 요약에title툴팁으로 "최대 1,000건까지만 표시" 를 알린다(잘림을 조용히 넘기지 말 것). 번호 윈도우 항상 최대 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개씩.
다크 모드(2026-10-13) — 페이지 버튼
.ds-page-btn글자label-alternative, hoverbg-hover, 현재 페이지bg-selected+label-normal(전 neutral-600/100/200/900 팔레트). ⚠ 이 규칙은 정본이 아니라 앱별 복사본(admin · general-affairs · inventory · management-dashboard · team-managermain.css)에 있으므로 다크 토큰도 복사본마다 맞춰야 한다. 이 페이지에는 Playground 가 없어 다크 미리보기가 없다.
Interactive playground
실제 동작과 동일해요. 번호·화살표를 눌러 페이지를 옮겨 보고, 총 건수를 바꿔 10개 윈도우가 슬라이딩·클램프되는 걸 확인하세요. 페이지 크기를 바꾸면 1페이지로 돌아가고, 1,240건에서 「전체」를 고르면 요약에 마우스를 올렸을 때 상한(1,000건) 툴팁이 뜹니다.
좌측 = 건수 요약, 우측 = 버튼 그룹. 현재 페이지는 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).
좌측 — 페이지 크기 선택기
system-admin의 app-versions 두 표는 위 사용자 승인 예외를 적용합니다. 저장된 유효한 선택이 없을 때만 기본 10건이며, 기존 30건 선택도 유지됩니다. Bundle과 변경 이력은 각각 기억합니다.
- 요약 바로 오른쪽에
<select class="ds-select ds-select--28">하나. 옵션은 30 / 50 / 100 / 전체 네 개로 고정 — 목록마다 다른 조합을 만들지 말 것(28px 는.ds-page-btn높이와 같다). - 전체 =
limit: 1000. 서버paginationSchema의limit상한(1,000)과 같은 값이며, 그 이상은 한 번에 보여주지 않는다 — 큰 조직에서 목록 API 응답이 무한정 커지는 것을 막는다. - 잘림은 반드시 알린다. 전체를 골랐는데
totalCount > 1000이면 요약<span>에title="전체 N건 중 최대 1,000건까지만 표시돼요"를 달아 툴팁으로 보여준다. 요약 문구 자체는총 N건 중 1-1000건으로 실제 표시 범위를 정직하게 적는다. - 크기를 바꾸면 1페이지로 되돌린 뒤 다시 불러온다(현재 페이지 번호가 새 총 페이지 수를 넘길 수 있다). 선택은
localStorage에 목록별로 기억해 새로고침 후에도 유지한다. - 이 선택기는 2026-09 부터 표준이다. 아직 없는 목록은 마이그레이션 대상이지 위반이 아니다 — 손대는 파일 안에서만 붙이고 일괄 스윕은 하지 않는다. 첫 적용: admin 직원 관리 목록(CS-LOC-260909-0010).
우측 — 버튼 그룹
- 순서:
«‹[번호들]›», 그룹은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개를 보여줍니다.
// 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 배경 미발동) |
<!-- Canonical markup for the admin employee list. -->
<div x-show="paging.employees.totalCount > 0" class="sticky left-0 mt-3 flex items-center justify-between">
<div class="flex items-center gap-2">
<!-- Summary: explain the 1,000-record cap in a title tooltip when All exceeds the limit. -->
<span class="text-xs text-neutral-500" :title="pageSizeCapTitle('employees')">
총 <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>
<!-- Page size: 30/50/100/All (maximum 1,000). Changing the size reloads the first page. -->
<select class="ds-select ds-select--28" aria-label="페이지당 표시 인원"
:value="paging.employees.limit"
@change="setPageSize('employees', $event.target.value, () => loadEmployees())">
<template x-for="opt in pageSizeOptions" :key="opt.value">
<option :value="opt.value" :selected="opt.value === paging.employees.limit" x-text="opt.label"></option>
</template>
</select>
</div>
<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, totalCount, totalPages },limit초기값은readPageSize(tab)= localStorage 기억값 또는 30) +goToPage(tab, page)/resetPage(tab)/pageRange(tab)/setPageSize(tab, value, reload)/pageSizeCapTitle(tab). 옵션은pageSizeOptions(모듈 상수PAGE_SIZE_OPTIONS) 하나를 모든 목록이 공유. 필터 변경 시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-09-09 | 페이지 크기 선택기 표준화 — "30개 고정" 규칙을 "기본 30, 30/50/100/전체(최대 1,000) 선택" 으로 개정. 전체 초과 시 툴팁 고지 의무. 첫 적용 admin 직원 관리 (CS-LOC-260909-0010) |
| 2026-06-19 | 라이브 미리보기를 페이지 최상단으로 이동(소개 tip 바로 아래, 단일 출처 위) (사용자 요청) |
| 2026-06-19 | 페이지네이션 패턴 페이지 신규 작성 — 30개/페이지·.ds-page-btn 고스트 버튼·10개 윈도우·앱별 로직·비대상 목록 |