리액트로 개발을 하다 보면 컴포넌트내에 자바스크립트 코드가 자주 쓰인다. 예를들어 배열의 각 요소를 map 을 이용해 렌더링하거나 && 연산자, 삼항 연산자 등 정말 자주 사용하게 된다.
필요에 의해 사용하지만 문제는 조금만 복잡해져도 코드가 지저분해지고 코드를 이해하기 어렵다는 점이다.
아래는 DropDown 을 구현한 코드이다.
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;
👉 간단한 코드임에도 불구하고 코드가 지저분하고 코드를 이해하기 위해서는 하나하나 살펴봐야 하는 수고가 따른다.
예를들어 <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 이라는 컴포넌트 명만 봐도 어떤 역할을 하는지 명시적으로 알 수 있어서 좋다고 생각했다.
Ant Design에서 영감을 받아, 매번 배열을 화면에 그리는 코드를 반복해서 작성하지 않고도 재사용 가능한 컴포넌트를 만들어보기로 했다.
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>
)