개발 이야기/React & Next

[React/디자인 시스템] 알잘딱깔센 표 UI 인터페이스 구현하기

Heman 2023. 4. 17. 00:49

[React/디자인 시스템] 알잘딱깔센 표 UI 인터페이스 구현하기

알잘딱깔센 표 UI 인터페이스라니, 이건 또 무슨 말일까요?

알잘딱깔센은 "알아서 잘 딱 깔끔하고 센스있게" 라는 신세대 용어랍니다 ㅎㅎ

 

표 컴포넌트를 구성하는 방식

 

일반적으로 위와 같은 표를 구현할때는 아래와 같이 테이블 형태나 리스트 형태로 구현합니다.

 

// 테이블
<Table>
  <Tbody>
    <Tr>
      <Td>타이틀 1</Td>
      <Td>{data.value1}</Td>
    </Tr>
    <Tr>
      <Td>타이틀 2</Td>
      <Td>{data.value2}</Td>
    </Tr>
    <Tr>
      <Td>타이틀 3</Td>
      <Td>{data.value3}</Td>
    </Tr>
  </Tbody>
</Table>


// 리스트
<List>
  {data.map((item) => (
    <div key={item.key}>
      <Title>{item.title}</Title>
      <Content>{item.value}</Content>
    </div>
  ))}
</List>

 

데이터가 배열이라면 리스트의 형태로 반복문을 사용하면 될 것 같습니다.

하지만 위와 같은 표를 만드는데 서버에서 각각의 한글 타이틀을 저장하는 것은 비효율적이며, 저런 형태의 데이터를 굳이 배열로 내려주는 것은 더욱 비효율적일 것입니다.

 

그렇다면 데이터는 높은 확률로 한글 타이틀을 가지지 않는 key: value 로 이루어진 객체 형태로 내려오겠군요.

 

프론트엔드에서는 서버에서 내려온 데이터를 그대로 테이블 형태로 화면에 뿌려주는 방식과, 데이터를 배열로 가공하여 한글 타이틀과 key 값을 매핑해 리스트 형태로 랜더하는 방식 중 하나를 채택할 수 있습니다.

 

하지만 대부분 이런 상황에서는 굳이 데이터를 가공하지 않고 테이블 형태로 컴포넌트를 만들 것이라 생각합니다.

 

알잘딱깔센 표는 뭔가요?

제가 일하고 있는 팀에서는 매번 위와 같은 방식으로 컴포넌트를 작성하기를 원하지 않았습니다.

 

프로젝트 특성상 리스트와 테이블 형태가 다양하고 여러 페이지에서 사용되는데, 매번 컴포넌트를 저런식으로 작성할 수는 없습니다.

그래서 디자인 시스템에 표 컴포넌트(회사 내부적으로는 다른 명칭을 사용)를 추가하여 생산성을 높이기로 하였고, 프로젝트 초창기에 제게 할당된 테스크 중 하나였습니다.

 

결과물은 간단합니다.

 

아래와 같은 형태로 데이터와 컬럼에 대한 정보를 props 로 넘겨주면, 알잘딱깔센 컴포넌트가 랜더링되는것이죠..!

 

컴포넌트 이름은 대충 넘어 가주세요^^

 

<AlZalDdakTable
  {...args}
  data={data}
  columns={columns}
/>

 

구현 방법도 어렵지 않습니다.

데이터에서 각 key 값을 타이틀이 들어간 colums 의 정보와 대조하여 적절한 위치에 value 넣어주면 됩니다.

 

물론 위와 같이 단순한 표라면 말이죠! 하하

 

복잡한 형태의 표

 

네 그렇습니다.

진.짜 알잘딱깔센 표란 바로 이런 복잡한 케이스의 표를 하나의 UI 컴포넌트내에서 구성가능하도록 구현해낸 것이었습니다.

 

그렇다면 이 컴포넌트는 어떻게 데이터를 원하는 위치에 표시해주는 걸까요?

 

구현 방식

(회사 내부에서 사용하는 코드와는 prop 명이나 로직을 조금 다르게 표현하였습니다.)

 

이 글의 처음에 리스트 형태로 구현하는 방법에 대해서 언급한걸 기억하시나요?

네 맞습니다. 저는 이 복잡한 표를 리스트 형태로 표현하고자했습니다.

 

각 컬럼은 key, title, renderedComponent(랜더링 되는 컴포넌트) 정보가 들어가고, key 값을 실제 데이터와 대조하여 데이터를 랜더링합니다.

 

이 컬럼들의 배열로 표의 각 줄을 표현하는 것이죠.

 

interface Column<T> {
  key: string;
  title?: string;
  renderedComponent?: (d: T) => React.ReactNode | string;
}

 

하지만 단순한 컬럼의 배열만으로는 복잡한 표에서 데이터가 표시될 위치를 정확하게 알 수 없습니다.

 

그래서 제가 고민한 방안은 두가지였습니다.

 

 

첫번째 방식: column 에 position 정보를 함께 넣기

위 인터페이스에 colrow 정보를 갖는 position을 추가하고, position을 갖는 컬럼은 그 정보에 맞는 위치에 표현하는 방식입니다.

 

const columns = [
  {
    key: 'foo',
    title: '타이틀1',
  },
  {
    key: 'bar',
    title: '타이틀2',
    position: {
      row: 2,
      col: 3,
    },
  },
  ...
];

 

이 방식은 확실하게 원하는 데이터를 원하는 위치에 랜더시킬 수 있다는 장점이 있습니다만, 표를 구성해야하는 개발자가 매번 컬럼의 col, row를 세서 계산해야하는 단점이 존재합니다.

 

만약 위 예시보다 복잡한 UI의 표가 기획으로 나온다면, colrow 의 위치를 세는데 쓸데없는 비용이 들어가게 됩니다.

 

그렇다면 두번째 방식은 뭘까요?

 

 

두번째 방식: 복잡한 형태의 표는 2차원 배열을 사용하기

이 방식은 첫번째 방식보다 개발 생산성 면에서 훨씬 개선된 방식입니다.

 

position을 따로 설정하지 않아도 배열이 가지는 특성상 직관적으로 랜더되는 값의 위치를 표현할 수 있습니다. 하지만 UI 컴포넌트 내부적으로 아래와 같이 1차원 배열과 2차원 배열을 나누어 처리해주어야합니다.

 

  return isMultiDimension
    ? multiDimensionRows(columns as Columns<T>[])
    : singleDimensionRows(columns as Columns<T>);

 

컴포넌트 내부에서 columns prop을 받으면 2차원 배열인지 검증을 한 후 랜더되는 컴포넌트를 분기처리하기 때문에 실제로는 두가지 컴포넌트로 구성되어 있지만, 직관적으로 표현할 수 있고 개발생산성을 향상 시킬 수 있다는 점에서 장점이 더 크다고 판단했습니다.

 

columns 는 아래와 같이 구성됩니다.

 

const multiDimensionColumns = [
  [
    {
      key: 'foo',
      title: '타이틀1',
    },
  ],
  [
    {
      key: 'bar',
      title: '타이틀2',
    },
    {
      key: 'baz',
      title: '타이틀3',
    },
  ]
]

 

이제 이 columns 를 이용, map 으로 탐색하면서 각 key 에 해당하는 값을 데이터에서 조회해 타이틀과 함께 보여주면 됩니다.

이 인터페이스에 option 등 을 추가하여 한 컬럼이 어느정도 영역을 할당할지 title 의 너비는 어느정도로 설정할지 등을 정할 수 있을 것 같습니다.

 

마무리하며

프로젝트 초창기에 개발된 컴포넌트지만, 사실 이 UI 컴포넌트를 개발하면서 이번 글에 있는 문제뿐만 아니라 다른 고민도 많았습니다.

한 줄과 각 컬럼의 너비를 어떤 비율로 가져갈 것이며, 각 셀에 랜더되는 컴포넌트는 어떻게 표현할 것인지 등 여러가지 고민을 해야하는 테스크였습니다.

 

그래도 디자인 시스템에 대해 좀 더 깊이 고민해볼 수 있었고, 예외 상황과 다양한 케이스를 고려하며 개발했던 좋은 경험이었습니다.