Skip to content

페이지네이션 (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-600 12px, 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-4 sibling.

  • CSS 스펙 변경 시 admin·team-manager·general-affairs·inventory main.css 4곳 동시 수정(admin이 원본).

  • 비대상(넣지 말 것): 집계·피벗 화면(전체 로드), 검색 콤보박스·자동완성(limit 20), 급여 워크북(hr-system 전체 렌더). 트리 목록은 최상위 노드 기준 30개씩.

  • 다크 모드(2026-10-13) — 페이지 버튼 .ds-page-btn 글자 label-alternative, hover bg-hover, 현재 페이지 bg-selected + label-normal(전 neutral-600/100/200/900 팔레트). ⚠ 이 규칙은 정본이 아니라 앱별 복사본(admin · general-affairs · inventory · management-dashboard · team-manager main.css)에 있으므로 다크 토큰도 복사본마다 맞춰야 한다. 이 페이지에는 Playground 가 없어 다크 미리보기가 없다.

Interactive playground ​

실제 동작과 동일해요. 번호·화살표를 눌러 페이지를 옮겨 보고, 총 건수를 바꿔 10개 윈도우가 슬라이딩·클램프되는 걸 확인하세요. 페이지 크기를 바꾸면 1페이지로 돌아가고, 1,240건에서 「전체」를 고르면 요약에 마우스를 올렸을 때 상한(1,000건) 툴팁이 뜹니다.

총 건수
총 248건 중 1-30건

좌측 = 건수 요약, 우측 = 버튼 그룹. 현재 페이지는 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·inventory main.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-4 sibling으로 배치.

좌측 — 건수 요약 ​

  • 총 <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개를 보여줍니다.

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 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개 윈도우·앱별 로직·비대상 목록