Get startedFoundationsComponentsPatternsRules

Dynamic Table

다이내믹 테이블은 정렬, 페이지 나누기, 로딩 상태를 내장한 데이터 표예요.

설치

pnpm dlx shadcn@latest add https://rebar.conmarket-landing.workers.dev/r/steel/dynamic-table.json

Examples

Uncontrolled

DynamicTable 은 정렬·페이지 상태를 스스로 관리해요. head.cells 가 열을 만들고, rows 의 각 행은 cells 를 같은 순서로 가져요. defaultSortKey, defaultSortOrder, defaultPage 로 처음 상태만 정해요.

머리글은 body.small 굵게(text.subtle), 본문 셀은 위아래 4px·좌우 8px 패딩이고 첫 열의 왼쪽과 마지막 열의 오른쪽 패딩은 0이에요. 머리글 아래와 표 끝에는 2px border 선이 그어지고, 행에 마우스를 올리면 background.neutral.subtle.hovered 로 바뀌어요.

Controlled

DynamicTableStateless 에 page, sortKey, sortOrder 를 넘기고 onSetPage, onSort 로 상태를 직접 바꿔요. 서버에서 정렬·페이지 데이터를 받아올 때 써요. 서버가 한 페이지씩만 준다면 totalRows 로 전체 행 수를 알려줘요.

Sorting

isSortable 인 머리글을 누르면 오름차순 → 내림차순으로 번갈아 정렬해요. 정렬 기준은 각 셀의 key 예요. 숫자 key 는 크기순, 문자열 key 는 숫자를 인식하는 사전순으로 정렬되고, 숫자와 문자열이 섞이면 숫자가 먼저 와요. 정렬 아이콘은 정렬 중인 열에서만 늘 보이고, 다른 열은 마우스를 올리거나 포커스할 때 나타나요.

Pagination

rowsPerPage 보다 행이 많으면 표 아래 가운데에 페이지 나누기가 생겨요. 페이지가 7개를 넘으면 가운데를 … 로 생략해요. 로딩 중에는 페이지 버튼도 비활성돼요.

Loading states

isLoading 을 켜면 본문이 opacity.loading(0.2)으로 흐려지고 가운데에 스피너가 떠요. 표는 inert 가 되어 조작할 수 없어요. 스피너 크기는 행이 3개 이상이면 large, 아니면 small 이고 loadingSpinnerSize 로 바꿀 수 있어요.

Empty state

행이 없으면 머리글 아래에 emptyView 를 보여줘요. 왜 비었는지와 다음에 할 일을 알려줘요.

Highlighted row

highlightedRowIndex(숫자 또는 배열)나 행의 isHighlighted 로 행 배경을 background.selected 로 강조해요. 강조는 보조 수단이라 텍스트로도 같은 뜻을 전해야 해요.

Custom column span

셀의 colSpan 으로 한 셀이 여러 열을 차지하게 해요. 시간표처럼 칸이 이어지는 데이터에 써요.

Interactive row

행은 <tr> 속성을 그대로 받아요. onClick 과 onKeyDown, tabIndex 를 함께 넣어 마우스와 키보드 모두로 열 수 있게 해요. 포커스된 행에는 안쪽 2px border.focused 외곽선이 그려져요.

Usage guidelines

행과 열로 된 데이터를 보여줄 때, 특히 많은 정보를 훑어보고 정렬하고 비교해야 할 때 써요. 단순한 정적 표라면 기본 HTML 표로도 충분해요. 계층 구조가 있는 데이터는 Table Tree 를 써요.

구성

  • 머리글: 각 열이 어떤 정보인지 알려줘요.
  • 행: 같은 항목에 속한 셀을 한 줄로 묶어요.
  • 페이지 나누기: 행이 한 페이지를 넘으면 표 끝에 나타나요.
Do· 짧고 분명한 열 이름
열 이름은 쉬운 말로 짧게 써요. 복잡한 표에는 화면에 보이는 caption 을 달아요.
Don't· 머리글에 버튼
머리글 안에 링크나 버튼을 넣지 않아요. 정렬 버튼만 들어가요.
Do· 행마다 고유한 액션 이름
"TEAM-101 편집"처럼 어떤 행의 액션인지 이름에 담아요.
Don't· 강조에만 의존
강조 행만으로 선택·포커스·의미를 전하지 않아요.
  • 화면 배치를 위해 표를 쓰지 않아요. 머리글 없는 표도 피해요.
  • 편집: 콘텐츠 종류가 하나뿐인 단순한 표엔 편집 버튼이 필요 없어요. 여러 종류를 편집해야 하면 더보기 메뉴에 편집을 넣고, 입력은 표 안이 아닌 모달이나 별도 페이지에서 받아요.
  • 삭제: 액션이 여럿이면 더보기 메뉴에, 하나뿐이면 닫기 아이콘으로 둬요. 행을 지우면 아래 행이 올라오고, 키보드 포커스는 다음 포커스 가능한 요소로 옮겨요.
  • 오류: 문제가 생긴 행이나 입력에 오류 아이콘을 붙이고 해결 방법을 함께 알려줘요.
  • 가능하면 말줄임(shouldTruncate) 보다 줄바꿈을 써요.

Accessibility

  • caption 이나 label 중 하나로 표 이름을 정해요. 둘을 함께 쓰면 충돌할 수 있어요. caption 을 숨기고 싶다면 시각적으로만 숨겨요.
  • 정렬 가능한 머리글은 버튼이라 Tab 으로 이동하고 Enter·Space 로 정렬해요. 정렬 중인 열의 <th> 에는 aria-sort 가 붙고, 버튼에는 aria-roledescription("정렬 버튼")과 툴팁이 붙어요.
  • 내용이 없는 머리글 셀은 빈 <td> 로 렌더링돼 스크린 리더가 빈 머리글을 읽지 않아요.
  • 로딩 스피너는 loadingLabel(기본 "표를 불러오는 중")로 상태를 알려줘요.
  • 페이지 나누기는 <nav> 안에 있고 현재 페이지에 aria-current="page" 가 붙어요. 이름은 paginationi18n 으로 바꿔요.

Props

DynamicTable

Prop

Type

DynamicTableStateless 는 default* props 대신 page, sortKey, sortOrder 를 받고, 서버 페이지용 totalRows 를 추가로 받아요. 드래그로 순서를 바꾸는 isRankable 은 지원하지 않아요.

HeadCellType

Prop

Type

RowType

Prop

Type

On this page