
작업 과정을 되돌아보는 것도 성장에 도움이 될 것 같아서 오랜만에 작성해본다. 배열을 통해 UI를 보여주는 것은 맥락을 불문하고 프론트엔드의 대표적인 day-to-day job이다. 내 머릿속에는 어떤 게 무의식 속에 남아 이 작업을 하고 있을까?
먼저 callback
함수 안쪽 스코프에서 JSX를 만드는 것과 <Item />
을 만들어 JSX를 만드는 것 사이에서 나는 먼저 전자로 작업을 하고 반드시 그래야 할 이유가 생길 때만 후자를 생각한다. 예를 들어서 다음과 같은 Props
를 받는 <Item />
이 있다고 해보자.
// 도메인과 관련된 내용을 props에 넣지 않는다는 것을 가정한다.
type Props = {
id: string;
title: string;
description: number;
};
<Item />
은 본연의 책임을 다하고 있다. 이제 시간이 흘러 UI를 확장한다고 해보자. 내가 모종의 이유로 시선이 컴포넌트 안에서만 돌다 보면 결국 다음과 같은 Props
를 만든다.
type Props = {
id: string;
title: string;
subTitle?: string;
description?: string;
onClick?: () => void;
};
// 아니면
type Props = {
id: string;
title: string;
subTitle?: React.ReactNode;
description?: React.ReactNode;
button?: React.ReactNode;
};
이러한 형태의 컴포넌트를 만들다 보면 따라오는 맥락이 속도 중심의 개발인 것 같다. 나는 속도 레버리지를 올려야 할 때는 다음과 같이 만드는 걸 선호한다. (물론 리뷰에 따라 기각당할 수 있지만)
declare const onClick: (item: Item) => async () => Promise<void>
{items.map((item) => {
const showSubTitle = /*...*/
const showDescription = /*...*/
const showButton = /*...*/
return (
<article key={item.id}>
<h2>{item.title}</h2>
{showSubTitle && <h3>{item.subTitle}</h3>}
{showDescription && <p>{item.description}</p>}
{showButton && <button onClick={onClick(item)}>Show more</button>}
</article>
)
})}
{items.map((item) => {
const description = /*...*/ ? <p>{item.description}</p> : null
const subTitle = /*...*/ ? <h3>{item.subTitle}</h3> : null
const button = /*...*/ ? <button onClick={onClick(item)}>Show more</button> : null
return (
<article key={item.id}>
<h2>{item.title}</h2>
{subTitle}
{description}
{button}
</article>
)
})}
왜 난 이런 스타일을 더 좋아할까? 먼저 파일 2개를 왔다 갔다 하면서 코드를 읽는 것보다 한 파일에서 위아래로 코드를 읽는 것이 더 편하다. 특히 LLM이 짠 코드를 검토할 때는 만들어진 맥락이 무엇인지 더 편히 읽을 수 있었다.
확장할 때도 <Item />
에 optional한 어떤 값을 추가하면 나중에 다시 이 컴포넌트를 작업하러 돌아왔을 때 인지적으로 더 부하가 왔었다.
재사용은 어떨까? 보이는 대로 위 UI 마크업은 재사용을 할 수 없다. 이게 재사용될 수 있다고 생각하는가? 내 경험상 이 생각은 오버엔지니어링으로 이어졌다. 그거 생각할 시간에 도메인 로직에 집중을 더 하는 게 나은 것 같다.
만약에 재사용이 필요한, 그러니까 다른 곳에서도 반드시 이걸 써야 한다면 나는 (감당 가능한 수준 하에) 적극적으로 중복해서 코드를 작성한다. UI가 자체적인 맥락을 가지고 tweak되는 케이스를 많이 봐서 그렇다.
당연하게도 이 방식은 저 UI 안에 고유한 상태를 만들 수 없다는 한계를 가진다. 근데 그 경우에는 오히려 리팩토링을 하기 쉬웠다.
무의식적으로 이렇게 작업하는 것 이면에는 2개의 키워드, LLM
과 속도
가 있는 것 같다. 나는 품질과 속도를 놓고 봤을 때 3.5:6.5 정도로 비중을 두고 있는 것 같다. 분명히 품질이 올라야 할 때가 올 것이고, 그때에는 composition
과 함께 글을 써야겠다.