커스텀 테이블 컴포넌트
테이블 UI는 빈번하게 사용되지만 구현 공수가 많이 드는 컴포넌트 중 하나다. 프로젝트에서 요구 기능이 제한적인 경우, 무거운 서드파티 라이브러리 대신 필요한 기능만 갖춘 커스텀 테이블 컴포넌트를 직접 만드는 것이 현실적인 선택이 될 수 있다.
최소 기능 정의가 출발점이다. 칼럼 데이터 키를 이용한 자동 컬럼 생성, 칼럼별 소팅(sorting) UI 및 이벤트 전달, 로딩 중/빈 데이터 상태 처리, 칼럼별 커스텀 마크업 삽입 정도를 갖추면 실무에서 충분히 재사용 가능하다.
타입 설계는 컴포넌트의 유연성을 결정한다. `tableConfig`에는 캡션, 테마, 커스텀 클래스, 컬럼 배열을 담고, 각 `Column`에는 `key`, `sortable`, `header` 렌더 함수, `cell` 렌더 함수, `align`, `size` 등을 정의한다. `header`와 `cell`을 `ReactNode`를 반환하는 함수로 설계하면 원시 데이터를 필요한 마크업으로 자유롭게 가공할 수 있다.
ColumnHelper(accessor) 패턴은 소팅 상태와 데이터 접근을 캡슐화한다. `columnHelper.accessor('key', config)` 형태로 컬럼을 선언하면, 외부에서는 키와 설정만 넘기고 소팅 가능 여부에 따른 헤더 렌더링 로직은 내부에서 처리된다. 다른 컬럼의 값도 `cell(value, item, index)` 형태로 접근 가능하도록 해두면 복합 데이터 표시가 가능하다.
소팅 상태 관리는 `reduce`로 초기값을 잡고 `useState`로 각 컬럼의 `'asc' | 'desc' | 'default'` 상태를 순환 토글한다. 소팅 불가능한 컬럼은 클릭 이벤트를 연결하지 않고, 소팅 아이콘은 `asc`/`desc` 상태일 때만 노출하는 방식이 깔끔하다.
로딩/빈 상태 분기는 컴포넌트의 완성도를 높인다. `isLoading`이 `true`면 로딩 영역을, `tableDatas.length === 0`이면 빈 상태 영역을 반환하고, 그 외에는 테이블 본체를 렌더링하는 순서로 처리한다.
핵심 내용
- 최소 기능 범위 확정 후 타입부터 설계하는 순서가 효율적
- `header`/`cell`을 함수 타입으로 정의하면 원시 데이터 → 마크업 변환이 자유로움
- ColumnHelper accessor 패턴: 키와 컬럼 설정을 선언적으로 조합
- 소팅 상태는 `default → asc → desc` 순환 토글로 관리
- 로딩·빈 데이터 상태를 얼리 리턴으로 분기하면 코드 가독성 향상
- Module SCSS + classnames 조합으로 조건부 클래스 처리
관련 개념
- React TypeScript 패턴 — 타입 설계와 컴포넌트 구조화 원칙
- 디자인 시스템 — 테이블 컴포넌트를 시스템 수준에서 관리하는 방식
- UI 컴포넌트 용어 — 테이블 관련 UI 용어 정의
출처
- Custom Table Component — hongdoyoung, UX Engineer 이야기