Table

행과 열로 데이터를 비교하고 정렬·필터·선택을 제공합니다.

Live preview
컴포넌트를 조작하면 여기에 결과가 표시됩니다.
코드 보기
HTML
<sy-table
  aria-label="팀 작업 현황"
  selection-mode="none"
  variant="horizontal"
  line-style="solid"
  id="sample"
></sy-table>

<script type="module">
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "columns": [{
        "key": "name",
        "label": "이름",
        "sortable": true,
        "filterable": true
      },
      {
        "key": "team",
        "label": "팀",
        "sortable": true,
        "filterable": true
      },
      {
        "key": "tasks",
        "label": "완료",
        "sortable": true,
        "align": "right"
      }
    ],
    "rows": [{
        "id": "alex",
        "name": "Alex",
        "team": "Design",
        "tasks": 12
      },
      {
        "id": "jin",
        "name": "Jin",
        "team": "Design",
        "tasks": 8
      },
      {
        "id": "min",
        "name": "Min",
        "team": "Engineering",
        "tasks": 21
      },
      {
        "id": "sam",
        "name": "Sam",
        "team": "Engineering",
        "tasks": 15
      }
    ]
  });
</script>

사용 가이드

#

가벼운 목록의 조회·정렬·선택에 사용합니다. 대량 데이터의 가상화가 필요하면 Grid를 사용하세요.

  1. 열과 데이터 연결

    columns에 key·label을, rows에 JavaScript 배열을 전달합니다.

  2. 행 식별자 지정

    rowKey에 변하지 않는 고유한 키를 지정하세요.

  3. 필요한 조작 켜기

    열별 sortable·filterable과 selectionMode의 none·single·multiple을 선택하고 이벤트를 앱 상태에 연결합니다.

예제

#

정렬과 필터

열 이름은 정렬, 필터 버튼은 해당 열의 검색 조건을 엽니다.

코드 보기
HTML
<sy-table aria-label="팀 작업 현황" id="sample"></sy-table>

<script type="module">
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "columns": [{
        "key": "name",
        "label": "이름",
        "sortable": true,
        "filterable": true
      },
      {
        "key": "team",
        "label": "팀",
        "sortable": true,
        "filterable": true
      },
      {
        "key": "tasks",
        "label": "완료",
        "sortable": true,
        "align": "right"
      }
    ],
    "rows": [{
        "id": "alex",
        "name": "Alex",
        "team": "Design",
        "tasks": 12
      },
      {
        "id": "jin",
        "name": "Jin",
        "team": "Design",
        "tasks": 8
      },
      {
        "id": "min",
        "name": "Min",
        "team": "Engineering",
        "tasks": 21
      },
      {
        "id": "sam",
        "name": "Sam",
        "team": "Engineering",
        "tasks": 15
      }
    ]
  });
</script>

여러 행 선택

체크박스로 선택하고 선택된 데이터를 조회합니다.

코드 보기
HTML
<sy-table
  aria-label="팀 작업 현황"
  selection-mode="multiple"
  id="sample"
></sy-table>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">선택 행 조회</sy-button>
  <sy-button id="action-1" size="small">선택 해제</sy-button>
  <sy-button id="action-2" size="small">Alex 선택</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? '완료';
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "columns": [{
        "key": "name",
        "label": "이름",
        "sortable": true,
        "filterable": true
      },
      {
        "key": "team",
        "label": "팀",
        "sortable": true,
        "filterable": true
      },
      {
        "key": "tasks",
        "label": "완료",
        "sortable": true,
        "align": "right"
      }
    ],
    "rows": [{
        "id": "alex",
        "name": "Alex",
        "team": "Design",
        "tasks": 12
      },
      {
        "id": "jin",
        "name": "Jin",
        "team": "Design",
        "tasks": 8
      },
      {
        "id": "min",
        "name": "Min",
        "team": "Engineering",
        "tasks": 21
      },
      {
        "id": "sam",
        "name": "Sam",
        "team": "Engineering",
        "tasks": 15
      }
    ]
  });
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.getSelectedRows();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget1 = document.querySelector("#sample");
  document.querySelector('#action-1').addEventListener('click', async () => {
    const result = await actionTarget1.clearSelection();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget2 = document.querySelector("#sample");
  document.querySelector('#action-2').addEventListener('click', async () => {
    const result = await actionTarget2.setSelectedKeys([
      "alex"
    ]);
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
</script>

한 행 선택

한 번에 하나의 행을 선택합니다.

코드 보기
HTML
<sy-table
  aria-label="팀 작업 현황"
  selection-mode="single"
  id="sample"
></sy-table>

<script type="module">
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "columns": [{
        "key": "name",
        "label": "이름",
        "sortable": true,
        "filterable": true
      },
      {
        "key": "team",
        "label": "팀",
        "sortable": true,
        "filterable": true
      },
      {
        "key": "tasks",
        "label": "완료",
        "sortable": true,
        "align": "right"
      }
    ],
    "rows": [{
        "id": "alex",
        "name": "Alex",
        "team": "Design",
        "tasks": 12
      },
      {
        "id": "jin",
        "name": "Jin",
        "team": "Design",
        "tasks": 8
      },
      {
        "id": "min",
        "name": "Min",
        "team": "Engineering",
        "tasks": 21
      },
      {
        "id": "sam",
        "name": "Sam",
        "team": "Engineering",
        "tasks": 15
      }
    ]
  });
</script>

외부에서 정렬과 필터

앱의 필터 도구와 테이블을 연결할 수 있습니다.

코드 보기
HTML
<sy-table aria-label="팀 작업 현황" id="sample"></sy-table>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">완료 많은 순</sy-button>
  <sy-button id="action-1" size="small">Design만</sy-button>
  <sy-button id="action-2" size="small">필터 조회</sy-button>
  <sy-button id="action-3" size="small">필터 해제</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? '완료';
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "columns": [{
        "key": "name",
        "label": "이름",
        "sortable": true,
        "filterable": true
      },
      {
        "key": "team",
        "label": "팀",
        "sortable": true,
        "filterable": true
      },
      {
        "key": "tasks",
        "label": "완료",
        "sortable": true,
        "align": "right"
      }
    ],
    "rows": [{
        "id": "alex",
        "name": "Alex",
        "team": "Design",
        "tasks": 12
      },
      {
        "id": "jin",
        "name": "Jin",
        "team": "Design",
        "tasks": 8
      },
      {
        "id": "min",
        "name": "Min",
        "team": "Engineering",
        "tasks": 21
      },
      {
        "id": "sam",
        "name": "Sam",
        "team": "Engineering",
        "tasks": 15
      }
    ]
  });
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.setSort("tasks", "desc");
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget1 = document.querySelector("#sample");
  document.querySelector('#action-1').addEventListener('click', async () => {
    const result = await actionTarget1.setColumnFilter("team", "Design");
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget2 = document.querySelector("#sample");
  document.querySelector('#action-2').addEventListener('click', async () => {
    const result = await actionTarget2.getColumnFilters();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget3 = document.querySelector("#sample");
  document.querySelector('#action-3').addEventListener('click', async () => {
    const result = await actionTarget3.clearFilters();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
</script>

연속 셀 병합

팀이 같은 연속 행을 하나의 셀로 표시합니다.

코드 보기
HTML
<sy-table aria-label="팀 작업 현황" id="sample"></sy-table>

<script type="module">
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "columns": [{
        "key": "team",
        "label": "팀",
        "autoMerge": true
      },
      {
        "key": "name",
        "label": "이름",
        "sortable": true,
        "filterable": true
      },
      {
        "key": "tasks",
        "label": "완료",
        "sortable": true,
        "align": "right"
      }
    ],
    "rows": [{
        "id": "alex",
        "name": "Alex",
        "team": "Design",
        "tasks": 12
      },
      {
        "id": "jin",
        "name": "Jin",
        "team": "Design",
        "tasks": 8
      },
      {
        "id": "min",
        "name": "Min",
        "team": "Engineering",
        "tasks": 21
      },
      {
        "id": "sam",
        "name": "Sam",
        "team": "Engineering",
        "tasks": 15
      }
    ]
  });
</script>

열 너비 조절

헤더 오른쪽 경계를 드래그하거나 메서드로 너비를 지정합니다.

코드 보기
HTML
<sy-table
  aria-label="팀 작업 현황"
  resizable
  id="sample"
></sy-table>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">이름 열 160px</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? '완료';
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "columns": [{
        "key": "name",
        "label": "이름",
        "sortable": true,
        "filterable": true
      },
      {
        "key": "team",
        "label": "팀",
        "sortable": true,
        "filterable": true
      },
      {
        "key": "tasks",
        "label": "완료",
        "sortable": true,
        "align": "right"
      }
    ],
    "rows": [{
        "id": "alex",
        "name": "Alex",
        "team": "Design",
        "tasks": 12
      },
      {
        "id": "jin",
        "name": "Jin",
        "team": "Design",
        "tasks": 8
      },
      {
        "id": "min",
        "name": "Min",
        "team": "Engineering",
        "tasks": 21
      },
      {
        "id": "sam",
        "name": "Sam",
        "team": "Engineering",
        "tasks": 15
      }
    ]
  });
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.setColumnWidth("name", 160);
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
</script>

빈 결과

표시할 행이 없을 때 안내를 제공합니다.

코드 보기
HTML
<sy-table
  aria-label="팀 작업 현황"
  empty-text="조건에 맞는 팀원이 없습니다."
  id="sample"
></sy-table>

<script type="module">
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "columns": [{
        "key": "name",
        "label": "이름",
        "sortable": true,
        "filterable": true
      },
      {
        "key": "team",
        "label": "팀",
        "sortable": true,
        "filterable": true
      },
      {
        "key": "tasks",
        "label": "완료",
        "sortable": true,
        "align": "right"
      }
    ],
    "rows": []
  });
</script>

이벤트와 전달 값

컴포넌트를 조작하고 최근 이벤트의 이름과 detail 값을 확인하세요. 코드에는 이 컴포넌트와 하위 구성 요소가 보내는 이벤트의 구독 방법이 모두 포함되어 있습니다.

코드 보기
HTML
<sy-table
  aria-label="팀 작업 현황"
  selection-mode="none"
  variant="horizontal"
  line-style="solid"
  id="sample"
></sy-table>
<output id="demo-events" aria-live="polite">컴포넌트를 조작하면 최근 이벤트와 전달 값이 표시됩니다.
</output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? '완료';
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "columns": [{
        "key": "name",
        "label": "이름",
        "sortable": true,
        "filterable": true
      },
      {
        "key": "team",
        "label": "팀",
        "sortable": true,
        "filterable": true
      },
      {
        "key": "tasks",
        "label": "완료",
        "sortable": true,
        "align": "right"
      }
    ],
    "rows": [{
        "id": "alex",
        "name": "Alex",
        "team": "Design",
        "tasks": 12
      },
      {
        "id": "jin",
        "name": "Jin",
        "team": "Design",
        "tasks": 8
      },
      {
        "id": "min",
        "name": "Min",
        "team": "Engineering",
        "tasks": 21
      },
      {
        "id": "sam",
        "name": "Sam",
        "team": "Engineering",
        "tasks": 15
      }
    ]
  });
  for (const name of ["sortChanged", "selectionChanged", "rowClicked", "filterChanged",
      "columnResized"
    ]) {
    document.addEventListener(name, event => {
      document.querySelector('#demo-events').textContent =
        `${event.target.localName} · ${name}: ${describeResult(event.detail)}`;
    });
  }
</script>

촘촘한 행 간격

compact는 행 간격을 줄여 같은 공간에 더 많은 정보를 표시합니다.

코드 보기
HTML
<sy-table
  aria-label="팀 작업 현황"
  selection-mode="none"
  variant="horizontal"
  line-style="solid"
  compact
  id="sample"
></sy-table>

<script type="module">
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "columns": [{
        "key": "name",
        "label": "이름",
        "sortable": true,
        "filterable": true
      },
      {
        "key": "team",
        "label": "팀",
        "sortable": true,
        "filterable": true
      },
      {
        "key": "tasks",
        "label": "완료",
        "sortable": true,
        "align": "right"
      }
    ],
    "rows": [{
        "id": "alex",
        "name": "Alex",
        "team": "Design",
        "tasks": 12
      },
      {
        "id": "jin",
        "name": "Jin",
        "team": "Design",
        "tasks": 8
      },
      {
        "id": "min",
        "name": "Min",
        "team": "Engineering",
        "tasks": 21
      },
      {
        "id": "sam",
        "name": "Sam",
        "team": "Engineering",
        "tasks": 15
      }
    ]
  });
  const component2 = document.querySelector("#sample");
  Object.assign(component2, {
    "compact": true
  });
</script>

data로 행 전달하기

data를 통해 행 배열을 전달할 수도 있습니다. 같은 예제에 rows와 data를 중복 설정하지 않고 한 가지 입력 경로를 사용하세요.

코드 보기
HTML
<sy-table
  aria-label="팀 작업 현황"
  selection-mode="none"
  variant="horizontal"
  line-style="solid"
  id="sample"
></sy-table>

<script type="module">
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "columns": [{
        "key": "name",
        "label": "이름",
        "sortable": true,
        "filterable": true
      },
      {
        "key": "team",
        "label": "팀",
        "sortable": true,
        "filterable": true
      },
      {
        "key": "tasks",
        "label": "완료",
        "sortable": true,
        "align": "right"
      }
    ],
    "data": [{
        "id": "alex",
        "name": "Alex",
        "team": "Design",
        "tasks": 12
      },
      {
        "id": "jin",
        "name": "Jin",
        "team": "Design",
        "tasks": 8
      },
      {
        "id": "min",
        "name": "Min",
        "team": "Engineering",
        "tasks": 21
      },
      {
        "id": "sam",
        "name": "Sam",
        "team": "Engineering",
        "tasks": 15
      }
    ]
  });
</script>

API reference

#

이름에 마우스를 올리거나 키보드로 선택하면 적용 코드를 확인할 수 있습니다. 터치 화면에서는 이름을 누르세요.

<sy-table>

Properties

Attribute / PropertyTypeDefaultDescription
SyTableColumn[] | string[]key·label·sortable·filterable 등의 열 정의 배열입니다.

객체·배열 값은 JavaScript 프로퍼티로 전달합니다.

SyTableRow[] | string[]행 데이터 배열입니다. 각 행의 키가 고유해야 합니다.

객체·배열 값은 JavaScript 프로퍼티로 전달합니다.

SyTableRow[] | string''Alias of `rows`; takes precedence when set so consumers can bind data without choosing a name.

객체·배열 값은 JavaScript 프로퍼티로 전달합니다.

string'id'Field name used to identify a row uniquely. Spec name: `row-key`.
'none' | 'single' | 'multiple''none'Selection mode. Spec name: `selection-mode`.
'none' | 'horizontal' | 'vertical' | 'full' | 'outline''horizontal'행·열·외곽 테두리를 표시하는 방식입니다.
'solid' | 'dashed' | 'dotted''solid'Border line style for every divider in the table — outer frame, column dividers (`variant: 'vertical' | 'full'`), and row dividers (`variant: 'horizontal' | 'full'`). Width and colour stay on the design-system tokens; only the dash pattern changes. Spec name: `line-style`.
booleanfalseAlternate row background colour.
booleanfalseReduce row height.
booleantruePin the header inside the scroll container. Spec name: `sticky-header`.
booleanfalseHide the column header (`<thead>`). Use when the table has columns but the visual design omits the header — e.g. the row-header pattern where the leftmost column already identifies each row, or when the header lives outside the table. Spec name: `hide-header`.
booleanfalseRender the first data column as a row header (`<th scope="row">`). Useful for grids where the leftmost column identifies the row (think spreadsheet row labels). Spec name: `row-header`.
booleantruePin the row header to the left edge while the body scrolls horizontally. Has no effect unless `row-header` is also set. Spec name: `sticky-row-header`.
string'No data'Empty-state text when no rows are present. Spec name: `empty-text`.
string''Currently sorted column key. Spec name: `sort-key`.
SyTableSortDirection'none'Active sort direction. Spec name: `sort-direction`.
string[] | string[]현재 선택한 행 키 배열입니다.

객체·배열 값은 JavaScript 프로퍼티로 전달합니다.

booleanfalse처리 중임을 표시합니다.
string''Optional accessible label applied to the `<table>` element.
booleanfalseAllow the user to drag a column's right edge to resize it. The new width is committed on pointer-up and a `columnResized` event fires. Resize state is keyed by `column.key` so initial `column.width` and explicit changes coexist. Drag handles appear on every cell (header + body) so the user can grab the column edge from any row.

Events

EventDetailDescription
CustomEvent<{ key: string; direction: SyTableSortDirection }>이벤트의 detail에서 결과를 확인하세요.
CustomEvent<{ selectedKeys: string[]; selectedRows: SyTableRow[] }>이벤트의 detail에서 결과를 확인하세요.
CustomEvent<{ row: SyTableRow; index: number; key: string; originalEvent: MouseEvent | KeyboardEvent }>이벤트의 detail에서 결과를 확인하세요.
CustomEvent<{ key: string; value: string; filters: Record<string, string> }>Fires after a column filter is applied or cleared. `value` is the committed text (empty string = filter cleared); `filters` is a snapshot of every active filter so consumers can serialize the entire filter set.
CustomEvent<{ key: string; width: number; widths: Record<string, number> }>Fires when a resize gesture commits the new width on pointer-up.

Methods

MethodReturnsDescription
Promise<SyTableRow[]>선택된 행 데이터 배열을 반환합니다.
Promise<void>모든 행 선택을 해제합니다.
Promise<void>지정한 키의 행을 선택합니다.
Promise<void>열의 정렬 방향을 설정합니다.
Promise<void>로딩 표시를 켜거나 끕니다.
Promise<void>표의 스크롤을 맨 위로 이동합니다.
Promise<void>Programmatically set or clear a column filter. Pass an empty string to clear. Bypasses the popover UI so consumers can drive filters from external controls (saved views, query string, etc.). Fires `filterChanged`.
Promise<Record<string, string>>Returns a snapshot of every active filter keyed by column key.
Promise<void>Clears every active filter at once. Fires `filterChanged` with `key: ''`.
Promise<void>Programmatically set the rendered width (in pixels) of a column. Pass `0` to revert to the column's intrinsic width.

Slots

SlotDescription
컴포넌트의 본문 또는 자식 항목입니다.