Skip to content

페이지네이션 (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-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개씩.

라이브 미리보기

실제 동작과 동일해요. 번호·화살표를 눌러 페이지를 옮겨 보고, 총 건수를 바꿔 10개 윈도우가 슬라이딩·클램프되는 걸 확인하세요.

총 건수
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.tspageRange() / 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).

우측 — 버튼 그룹

  • 순서: « [번호들] », 그룹은 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-affairspageSize: 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개 윈도우·앱별 로직·비대상 목록