1. 배경

charkra, radix-ui, toss-emotion-utils 등 UI Library에는 Flex 컴포넌트가 있습니다. css 속성 중 하나인 flex인데 굳이 Flex컴포넌트를 만들어서 사용하는 이유가 무엇인지 곰곰히 생각해보았습니다.

아래는 flex스타일 속성을 적용한 컴포넌트와 flex스타일 속성을 추상화한 Flex 컴포넌트를 활용한 컴포넌트입니다.

tailwind를 사용하였습니다.

<div className="flex gap-10">
  {noticeList.map(({ title, onClick }) => (
    <NoticeItem key={title} title={title} onClick={onClick} />
  ))}
</div>
<Flex className="gap-10">
  {noticeList.map(({ title, onClick }) => (
    <NoticeItem key={title} title={title} onClick={onClick} />
  ))}
</Flex>

위 두 코드 중 어떤 것이 더 좋은 코드라고 할 수 있을까요? 가독성과 선언성의 측면에서 생각해보았습니다.

  1. 가독성 : Flex는 이름 그 자체로 목적을 전달하기에, 코드를 읽는 사람이 Flex컴포넌트를 보고 해당 부분이 flex 레이아웃을 위한 것임을 즉시 알 수 있습니다.
  2. 선언성 : Flex는 flex 레이아웃을 위한 것임을 선언하는 것처럼 보입니다. 그렇기에 코드를 읽는 사람에게 내부 구현의 세부 사항 없이도 의도를 명확하게 전달할 수 있습니다.

2. 선언적인 코드

선언적인 코드는 프론트엔드 개발을 하다보면 자주 만나게 되는 개념입니다. 특히 React 생태계에서 웹 서비스를 개발하다보면 선언적인 코드에 대해 고민하게 되는데요.

선언적인 코드는 다른 말로하면 ‘추상화 레벨이 높아진’코드라고 할 수 있습니다.

// A
sum([1,2,3]);
// B
function sum(nums:number[]){
	let result = 0;
	for (const num of nums){
		result += num;
	}
	return result;
}

여기에서 sum함수는 초기값이 0이고, 배열이 가지는 각각의 원소를 순회하면서 결과값에 더하는 작업을 추상화합니다. 덕분에 sum을 다루는 사람을 복잡한 제어 흐름을 이해할 필요 없이, “배열의 합을 구한다”라고 하는 동작에 집중하여 함수를 사용할 수 있습니다.

더 나아가서, sum함수 내부의 for ..of문도 선언적인 코드로 볼 수 있습니다.

for (const num of nums){
  /* 동작 ... */
}

nums배열을 순회하며 요소를 순회하는 동작을 추상화하고 있기 때문입니다.