Pagination#
대용량 콘텐츠를 페이지 단위로 나누어 탐색할 수 있는 컴포넌트입니다. 현재 페이지 표시, 이전/다음 이동, 첫/마지막 페이지 이동을 지원합니다.
Live Preview#
Pagination(
page: 1,
totalPages: 10,
onPageChanged: handlePageChange,
)
Pagination(
page: 1,
totalPages: 10,
onPageChanged: handlePageChange,
)
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 테이블, 검색 결과, 상품 목록처럼 많은 데이터를 페이지 단위로 나눠 표시할 때
- 사용자가 특정 페이지로 직접 이동할 수 있어야 할 때
- 전체 데이터 양이 많지 않아 페이지 번호로 탐색이 유용한 경우
대신 다른 컴포넌트를 사용하세요:
RefreshTrigger: 무한 스크롤 방식으로 데이터를 로드할 때Select: 페이지 크기 선택 등 드롭다운 옵션이 필요할 때
기본 사용법 (Basic Usage)#
// 기본 페이지네이션
Pagination(
totalPages: 10,
page: _currentPage,
onPageChanged: handlePageChanged,
)
// 첫/마지막 페이지 버튼 포함
Pagination(
totalPages: 20,
page: _currentPage,
onPageChanged: handlePageChanged,
maxPages: 7,
showSkipToFirstPage: true,
showSkipToLastPage: true,
)
// 첫 페이지에서 이전 버튼 숨김
Pagination(
totalPages: 5,
page: _currentPage,
onPageChanged: handlePageChanged,
hidePreviousOnFirstPage: true,
)
// 기본 페이지네이션 (이전/다음 버튼 포함)
Pagination(
page: currentPage,
totalPages: 10,
onPageChanged: handlePageChanged,
)
// 형제 페이지 — 7페이지 이하는 전체 표시, 초과 시 생략 처리
Pagination(
page: currentPage,
totalPages: 20,
onPageChanged: handlePageChanged,
)
// 이전/다음 버튼과 함께 현재 페이지 강조
Pagination(
page: 5,
totalPages: 15,
onPageChanged: handlePageChanged,
)
Props / Parameters#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
totalPages | int | 필수 | 전체 페이지 수 |
page | int | 필수 | 현재 페이지 (1-based) |
onPageChanged |
void Function(int) |
필수 | 페이지 변경 핸들러 |
maxPages |
int |
7 |
표시할 최대 페이지 버튼 수 |
showSkipToFirstPage |
bool |
true |
첫 페이지 이동 버튼 표시 여부 |
showSkipToLastPage |
bool |
true |
마지막 페이지 이동 버튼 표시 여부 |
hidePreviousOnFirstPage |
bool |
false |
첫 페이지에서 이전 버튼 숨김 |
hideNextOnLastPage |
bool |
false |
마지막 페이지에서 다음 버튼 숨김 |
paginationStyle |
CorePaginationStyle<Color>? / CorePaginationStyle<CoreColor>? |
null |
인스턴스 스타일 (Style 시스템 참조) |
스타일 시스템 (Style System)#
Pagination 의 모든 chrome / dimensional / nested-slot 오버라이드는 CorePaginationStyle<Clr>
단일 슬롯으로 흐릅니다 (Epic #1302 원칙 6/7/8). 시맨틱 / behaviour 필드 (page / totalPages /
onPageChanged / maxPages / showSkipToFirstPage / showSkipToLastPage
/ hidePreviousOnFirstPage / hideNextOnLastPage / showLabel) 는 위젯/컴포넌트 파라미터로 직접 전달합니다.
시맨틱 vs 스타일#
-
시맨틱 / behaviour: 위젯/컴포넌트 파라미터로 직접 (
page,totalPages,onPageChanged,maxPages,showSkipToFirstPage,showSkipToLastPage,hidePreviousOnFirstPage,hideNextOnLastPage,showLabel) -
chrome / dimensional / 슬롯 스타일:
CorePaginationStyle한 곳으로 (gap/pageButtonStyle/prevNextButtonStyle/ellipsisStyle)
Resolve chain#
design system default for pagination
→ CorePaginationTheme.style // 프로젝트 공통
→ parent component slot override
→ widget.paginationStyle // 인스턴스별
각 nested 슬롯 스타일 (pageButtonStyle / prevNextButtonStyle / ellipsisStyle) 은 자기 컴포넌트의 자체 resolve chain 으로 다시 한 번 머지됩니다.
슬롯 매핑 (CorePaginationStyle 4 필드)#
| 필드 | 타입 (Flutter) | 적용 영역 |
|---|---|---|
gap | double? | 페이지네이션 버튼 간격 (px) |
pageButtonStyle |
CoreButtonStyle<Color>? |
페이지 번호 버튼 chrome (현재 구현은
GhostButton
/
PrimaryButton
;
CoButton(variant: ghost)
asChild 통합은 #1322 이월)
|
prevNextButtonStyle |
CoreButtonStyle<Color>? |
이전/다음 버튼 chrome (위와 동일) |
ellipsisStyle |
CoreTextStyle<Color>? |
생략 표시 (…) 텍스트 스타일 |
Migration#
기존 PaginationTheme 의 평면 chrome (gap / showLabel) 은 호환을 위해 유지되지만, 새 코드는
paginationStyle 슬롯을 사용하세요:
| 기존 (legacy theme 필드) | 새 위치 |
|---|---|
PaginationTheme.gap |
paginationStyle.gap 또는 CorePaginationTheme.style.gap |
| 인스턴스 페이지 버튼 색/패딩 (불가능) | paginationStyle.pageButtonStyle |
| 인스턴스 prev/next 버튼 색/패딩 (불가능) | paginationStyle.prevNextButtonStyle |
| 인스턴스 생략 표시 텍스트 스타일 (불가능) | paginationStyle.ellipsisStyle |
사용 예 (Flutter)#
Pagination(
page: 1,
totalPages: 10,
onPageChanged: handlePageChanged,
paginationStyle: CorePaginationStyle(
gap: 4,
ellipsisStyle: CoreTextStyle(fontSize: 14, color: Colors.grey),
),
)
사용 예 (Web)#
Pagination(
page: 1,
totalPages: 10,
onPageChanged: (p) => setState(() => _page = p),
paginationStyle: CorePaginationStyle<CoreColor>(
gap: 8,
),
)
변형 (Variants)#
기본 (Default)#
숫자 버튼과 이전/다음 버튼을 표시합니다.
Pagination(
totalPages: 10,
page: 3,
onPageChanged: handlePageChanged,
)
첫/마지막 포함#
첫 페이지와 마지막 페이지로 바로 이동하는 버튼을 추가합니다.
Pagination(
totalPages: 10,
page: 3,
onPageChanged: handlePageChanged,
showSkipToFirstPage: true,
showSkipToLastPage: true,
)
넓은 범위 (Wide Pages)#
현재 페이지 주변에 더 많은 페이지 번호를 표시합니다.
Pagination(
totalPages: 10,
page: 5,
onPageChanged: handlePageChanged,
maxPages: 9,
)
동작 스펙 (Behavior)#
인터랙션#
- 페이지 버튼 클릭: 해당 페이지로 이동 (
onPageChanged(page)호출) - 이전/다음 버튼: 현재 페이지에서 1씩 감소/증가
- 첫/마지막 버튼: 1페이지 또는 totalPages로 이동
- 현재 페이지 버튼: 클릭해도 이벤트 없음 (비활성 강조)
- 경계 버튼: 첫 페이지에서 이전 버튼, 마지막 페이지에서 다음 버튼은 비활성
상태 전환#
- 현재 페이지 버튼: 강조 색상 + 비활성 클릭
- 경계 도달 시 이전/다음 버튼 disabled 처리
- 페이지 수가 많으면 현재 페이지 기준으로 중간 항목
...으로 생략
애니메이션#
- 페이지 전환 시 별도 애니메이션 없음 (콘텐츠 영역 전환은 부모가 처리)
- 버튼 호버 시 배경색 200ms 전환
사용 가이드라인 (Usage Guidelines)#
✅ Do#
많은 데이터에는 첫/마지막 버튼도 함께 표시
CouiPagination(
totalPages: 50,
currentPage: _currentPage,
onPageChanged: handlePageChanged,
showFirstLast: true,
)
50페이지 이상의 데이터에서 첫/마지막 버튼은 멀리 있는 페이지로의 빠른 이동을 지원한다.
❌ Don't#
페이지가 2개 이하일 때 Pagination 표시
// ❌ 페이지가 1~2개뿐인데 Pagination 표시
CouiPagination(
totalPages: 1,
currentPage: 1,
onPageChanged: handlePageChanged,
)
페이지가 1개뿐이면 Pagination이 의미 없고 오히려 혼란을 준다. totalPages > 1인 경우에만 표시한다.
✅ Do#
페이지 크기 선택과 함께 사용해 사용자 편의 제공
Row(
children: [
CouiSelect(
label: '페이지당 항목',
value: pageSize.toString(),
onChanged: handlePageSizeChanged,
items: ['10', '25', '50', '100'],
),
const Spacer(),
CouiPagination(
totalPages: (totalItems / pageSize).ceil(),
currentPage: _currentPage,
onPageChanged: handlePageChanged,
),
],
)
페이지 크기 옵션을 함께 제공하면 사용자가 데이터 양을 스스로 제어할 수 있다.
❌ Don't#
siblingCount를 너무 크게 설정하지 않기
// ❌ 버튼이 너무 많아 화면을 넘칠 수 있음
CouiPagination(
totalPages: 20,
currentPage: 10,
onPageChanged: handlePageChanged,
siblingCount: 5, // 너무 많은 버튼
)
siblingCount가 크면 버튼이 너무 많아 모바일 화면에서 줄바꿈이 발생한다. 1~2가 적당하다.
✅ Do#
현재 페이지와 전체 페이지 수를 함께 표시하세요.
CouiPagination(
currentPage: currentPage,
totalPages: totalPages,
onPageChanged: handlePageChanged,
showFirstLast: true,
showPageInfo: true, // '3 / 20' 형태로 표시
)
현재 위치와 전체 범위를 알면 사용자가 탐색 진행 상황을 파악하고 원하는 페이지로 빠르게 이동할 수 있습니다.
❌ Don't#
데이터가 적을 때 불필요하게 페이지네이션을 표시하지 마세요.
// ❌ 항목이 5개뿐인데 페이지네이션 표시
CouiPagination(
currentPage: 1,
totalPages: 1, // 한 페이지에 다 들어오는데도 표시
onPageChanged: handlePageChanged,
)
단일 페이지라면 페이지네이션은 불필요한 UI 요소입니다. 총 항목 수가 페이지당 개수보다 적으면 숨기세요.
접근성 (Accessibility)#
키보드 인터랙션#
| 키 | 동작 |
|---|---|
Tab | 다음 페이지 버튼으로 포커스 |
Enter / Space | 포커스된 페이지 버튼 활성화 |
Arrow Left/Right | 이전/다음 페이지 버튼으로 포커스 이동 |
스크린 리더#
- Flutter:
Semantics(label: '페이지 3, 전체 10페이지')형태로 현재/전체 페이지 전달 -
Web:
role="navigation"+aria-label="페이지 탐색"+ 현재 페이지에aria-current="page"적용
터치 타겟#
- 각 페이지 버튼 최소 크기: 44x44dp
크로스 플랫폼 차이점 (Platform Differences)#
| 항목 | Flutter | Web |
|---|---|---|
| 클래스명 | CouiPagination | Pagination |
| 페이지 기준 | 1-based | 1-based |
| 버튼 스타일 | CouiButton 기반 | <button> 기반 |
| 생략 표시 | ... 텍스트 |
<span aria-hidden>...</span> |
관련 컴포넌트 (Related Components)#
조합 예제#
// 데이터 테이블 + 페이지네이션
Column(
children: [
CouiTable(
columns: tableColumns,
rows: pagedData,
),
const Gap.md(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('총 ${totalItems}개 항목'),
CouiPagination(
totalPages: totalPages,
currentPage: _currentPage,
onPageChanged: handlePageChanged,
showFirstLast: true,
),
],
),
],
)