ComponentsP3 본문

Pagination

페이지 내비게이션 — 비제어(defaultPage) + onPageChange 알림. 줄임 로직(1 … 9 10 11 … 20), 경계에서 이전/다음 disabled.

마지막 업데이트 2026-06-11

한눈에#

목록·검색 결과를 페이지 단위로 끊어 이동하는 내비게이션입니다 — 현재 페이지 ±1과 양끝만 남기고 사이를 줄임표(…)로 접습니다.

  • 줄임 로직(1 … 9 10 11 … 20)
  • 비제어 + onPageChange
  • numeric·dots
  • 44px 히트 영역

경계에서 이전/다음이 자동 비활성 — 현재 페이지는 aria-current=page

직접 클릭해 보세요 — 현재 페이지 ±1과 양끝(1, 20)만 남기고 사이를 줄임표(…)로 접습니다.

사용 시점#

목록·검색 결과를 페이지 단위로 끊어 이동하면 Pagination — 편집·정렬이 있는 운영 표는 DataGrid가 합성합니다.

쓴다

목록·검색 결과를 페이지 단위로 끊어 이동(URL이 진실의 원천이면 page로 제어)

대신 DataGrid

편집·정렬이 있는 운영 표 — pageSize로 Pagination을 합성

플레이그라운드#

컨트롤로 props를 조작하면 미리보기와 코드가 실시간 갱신됩니다.

import { Pagination } from '@wds/ui-web';

<Pagination totalPages={10} />

변형#

페이지가 적으면 전체 표시 — 줄임 없음

비제어(defaultPage)와 제어(page)를 모두 지원합니다. 비제어는 현재 페이지를 내부에서 소유하고 변경만 알립니다. URL이 진실의 원천이어야 하는 경우 page로 제어하세요 — 클릭은 onPageChange로만 통지하고 현재 페이지는 외부 값을 따릅니다(함수 prop은 서버 MDX로 전달 불가라 코드로 보입니다):

'use client';

// 비제어 — 컴포넌트가 페이지를 소유, 라우터는 부수효과
function ProductListPagination({ totalPages }: { totalPages: number }) {
  const router = useRouter();
  return (
    <Pagination
      totalPages={totalPages}
      defaultPage={1}
      onPageChange={(page) => router.push(`?page=${page}`)}
    />
  );
}

// 제어 — URL search param이 진실의 원천(page가 권위)
function ControlledPagination({ totalPages }: { totalPages: number }) {
  const params = useSearchParams();
  const router = useRouter();
  const page = Number(params.get('page') ?? 1);
  return (
    <Pagination
      totalPages={totalPages}
      page={page}
      onPageChange={(next) => router.push(`?page=${next}`)}
    />
  );
}

크기#

size prop이 없습니다 — 버튼은 시각 32px 정사각형(space.8)에 44px 히트 영역이 확장된 단일 크기입니다.

상태#

  • 현재 페이지primary-subtle 배경 + primary-text + semibold (aria-current="page")
  • 경계 disabled — 1페이지에서 이전, 마지막에서 다음 버튼이 비활성
  • hoversurface-hover 배경, focus — 2px focus-ring
  • 이미 현재인 페이지를 클릭하면 onPageChange가 호출되지 않습니다

Props#

Prop타입기본값설명
totalPagesnumber전체 페이지 수 (1 이상, 필수)
pagenumber제어 현재 페이지 — 주면 page가 권위(onPageChange로만 통지) · 범위 밖 값은 클램프
defaultPagenumber1초기 현재 페이지 (비제어) — 범위 밖 값은 클램프
onPageChange(page: number) => void페이지 변경 알림 — 변경이 있을 때만 호출
labelstring'페이지네이션'nav의 aria-label
previousLabelstring'이전 페이지'이전 버튼 aria-label
nextLabelstring'다음 페이지'다음 버튼 aria-label
variant'numeric' | 'dots''numeric'numeric: 숫자 버튼+줄임 · dots: 점 페이지 컨트롤(소량 페이지·모바일, Apple page control) (ADR-012 B-P1)
…restOmit<HTMLAttributes<nav>, 'aria-label'>className 등 전달 (aria-label은 label prop으로만)

접근성#

  • <nav aria-label="페이지네이션"> + ul/li — 랜드마크로 점프 가능합니다
  • 현재 페이지는 aria-current="page", 각 페이지 버튼은 aria-label="N 페이지"
  • 이전/다음은 아이콘 버튼이라 previousLabel/nextLabel이 접근성 이름입니다
  • 줄임표(…)는 장식(aria-hidden) — 낭독되지 않습니다
  • 경계 비활성은 disabled 속성 — 포커스 순서에서 자연스럽게 제외됩니다

토큰#

component 토큰 없이 semantic을 직접 소비합니다(신설 기준 §4 미충족).

속성토큰
버튼 기본color.text-muted · font-size.body-2 + font-weight.medium
버튼 hovercolor.surface-hover + color.text
버튼 눌림(:active)color.surface-pressed (현재 페이지는 유지)
현재 페이지color.primary-subtle + color.primary-text + font-weight.semibold
disabledcolor.text-placeholder
줄임표color.text-placeholder
형태space.8(32px 셀) · space.1/2 · radius.md · icon.size-md
모션duration.fast + ease.standard