steven

요즘 내가 리액트로 리스트를 렌더링 한다면

다이산 가는 길에 찍었던 사진

작업 과정을 되돌아보는 것도 성장에 도움이 될 것 같아서 오랜만에 작성해본다. 배열을 통해 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과 함께 글을 써야겠다.