재사용 가능한 컴포넌트 만들기 (1) - map

D uuu·2024년 1월 2일

React

목록 보기
6/10

문제점 파악

리액트로 개발을 하다 보면 컴포넌트내에 자바스크립트 코드가 자주 쓰인다. 예를들어 배열의 각 요소를 map 을 이용해 렌더링하거나 && 연산자, 삼항 연산자 등 정말 자주 사용하게 된다.

필요에 의해 사용하지만 문제는 조금만 복잡해져도 코드가 지저분해지고 코드를 이해하기 어렵다는 점이다.

아래는 DropDown 을 구현한 코드이다.

  • '클릭' 을 누르면 handleFolder 함수가 동작해 isFolded 값을 바꿔준다.
  • isFolded 값이 true 일때만 ul 태그가 보여진다.
  • 이때 timeList 라는 배열을 받아와 map 을 이용해 각 요소를 렌더링 해준다.
const DropDown = ({timeList}) => {
  const [isFolded, setIsFolded] = useState(false);

  const handleFolder = () => {
    setIsFolded((prev) => !prev);
  };

  const handleTime = (time) => {
    console.log(time);
  };

  return (
    <>
      <div onClick={handleFolder}>클릭</div>

      {isFolded && (
        <ul>
          {timeList.map((time) => (
            <li key={time.label} onClick={() => handleTime(time.label)}>
              {time.label}
            </li>
          ))}
        </ul>
      )}
    </>
  );
};

export default DropDown;
    	

👉 간단한 코드임에도 불구하고 코드가 지저분하고 코드를 이해하기 위해서는 하나하나 살펴봐야 하는 수고가 따른다.


해결 방안 찾기 - Antd


전에 Antd 을 사용하면서 인상 깊었던 부분이 Table 을 만들때 prop 으로 배열을 받아서 처리를 하던 방법이었다.

예를들어 <Table> 컴포넌트에서는 dateSource 와 columns 를 prop 으로 받는다.

dataSource 는 배열 데이터를 받고, columns 는 dataSource 의 key 값을 dataIndex 에 넣어주면 테이블 형식으로 만들어준다.

dataSource : 서버로부터 받아온 데이터 리스트
columns : 화면에 띄우고 싶은 데이터

<Antd 사용 예시>

const dataSource = [
  {
    key: '1',
    name: 'John Brown',
    age: 32,
    address: 'New York No.1 Lake Park',
  },
  {
    key: '2',
    name: 'Jim Green',
    age: 42,
    address: 'London No.1 Lake Park',
  },
    {
    key: '3',
    name: 'Joe Black',
    age: 32,
    address: 'Sydney No.1 Lake Park',
  }
];

const columns = [
  {
    title: 'Name',
    dataIndex: 'name',
    key: 'name',
  },
  {
    title: 'Age',
    dataIndex: 'age',
    key: 'age',
  },
  {
    title: 'Address',
    dataIndex: 'address',
    key: 'address',
  },
];

<Table dataSource={dataSource} columns={columns} />;
 

🔻구현 화면

이런식으로 구현하니 컴포넌트 내에 자바스크립트 문법을 사용하지 않아도 되고 Table 이라는 컴포넌트 명만 봐도 어떤 역할을 하는지 명시적으로 알 수 있어서 좋다고 생각했다.


DyanamicRender 컴포넌트 만들기

Ant Design에서 영감을 받아, 매번 배열을 화면에 그리는 코드를 반복해서 작성하지 않고도 재사용 가능한 컴포넌트를 만들어보기로 했다.

DynamicRender 컴포넌트의 구현 방법

  • Props 정의: DynamicRender 컴포넌트는 data와 renderItem 두 개의 props를 받는다. data는 배열이어야 하며, renderItem은 각 항목을 렌더링하는 함수이다.

  • 타입 지정: 제네릭 타입을 사용하여 data와 renderItem의 타입을 유연하게 받을 수 있다.

  • 데이터 검사: 받은 데이터가 배열 형태가 아니거나 없는 경우, null을 반환한다.

  • 매핑 및 렌더링: map 메서드를 사용하여 받은 데이터를 매핑하고, 각 항목을 renderItem 함수에 전달하여 화면에 렌더링한다.


🔸실제 코드🔸

type DynamicRenderProps<T> = {
  data: T[];
  renderItem: (item: T) => JSX.Element | void;
};

const DynamicRender = <T,>({ data, renderItem }: DynamicRenderProps<T>) => {
  if (!data || !Array.isArray(data)) return null;

  return <>{data.map((item: T) => renderItem(item))}</>;
};

export default DynamicRender;

비교 전 & 후

DynamicRender 라는 컴포넌트를 만들어서 실제로 프로젝트에 적용해보고 느낀점을 적어보면 아래와 같다.

  • 재사용성 증가 : 다른 곳에서도 동일한 방식으로 배열을 렌더링할 때 컴포넌트를 그대로 사용해 코드의 재사용성이 높아졌다.

  • 가독성 향상 : map 메서드로 인한 반복 코드를 줄일 수 있으며, 코드가 더 간결해지고 가독성이 향상됐다.

  • 코드 추상화 : DynamicRender 컴포넌트를 사용하면 배열 데이터를 매핑하는 로직을 추상화하여 컴포넌트 내부에서 구현할 필요가 없어졌다.



🔸컴포넌트 내에 자바스크립트로 작성한 코드

return (
   <ul>
    {timeList.map((time) => (
   	  <li key={time.label} onClick={() => handleTime(time.label)}>
       {time.label}
      </li>
  	 ))} 
   </ul>
)

🔸DynamicRender 컴포넌트를 활용한 코드

const renderTime = (time) => (
   <li key={time.label} onClick={() => handleTime(time.label)}>
     {time.label}
   </li>
 )

return (
  <ul>
    <DynamicRender data={timeList} renderItem={renderTime} />
  </ul>
)

profile
배우고 느낀 걸 기록하는 공간

0개의 댓글