DEV Community

Sol Lee
Sol Lee

Posted on

페이지 컴포넌트를 합성 컴포넌트로 재구조화 시키기

한 줄 요약

거대한 페이지 컴포넌트를 합성 컴포넌트로 리팩토링했던 경험 공유

한 페이지 안에서 중복 코드가 너무 많다

최근 회사 코드(Svelte)를 보다가 한 페이지에서 반복되는 코드가 너무 많다는 걸 발견했다. 회사 코드를 그대로 갖다 붙이면 큰일나기 때문에, 예시를 통해 알아보자. 예를 들어, 같은 스타일과 로직을 가진 카드 UI가 여러 개 있었는데, 각각 따로 구현되어 있어서 유지보수가 어려웠다. 나는 이 문제를 해결하기 위해 합성(Composable) 컴포넌트 방식으로 리팩토링하기로 했다.

중복을 제거하고 더 재사용 가능한 구조로 만들기

내 목표는 다음과 같았다.

  1. 중복되는 UI와 로직을 컴포넌트로 분리한다.
  2. 다양한 데이터와 함께 재사용할 수 있도록 유연한 구조를 만든다.
  3. Svelte 코드와 React 코드로 비교하면서 더 나은 방식이 무엇인지 분석한다.

Svelte와 React 코드 비교하며 리팩토링하기

기존 Svelte 코드 (문제점이 있는 코드)

<Card>
  <h2>{title1}</h2>
  <p>{description1}</p>
  <button on:click={handleClick1}>메뉴1</button>
</Card>

<Card>
  <h2>{title2}</h2>
  <p>{description2}</p>
  <button on:click={handleClick2}>메뉴2</button>
</Card>

<Card>
  <h2>{title3}</h2>
  <p>{description3}</p>
  <button on:click={handleClick3}>메뉴3</button>
</Card>
Enter fullscreen mode Exit fullscreen mode

위 코드의 문제점은 단순하다.

title, description, handleClick이 각각 다른 변수로 관리되고 있다.
카드의 레이아웃과 스타일이 동일한데도 불구하고, 같은 구조가 반복되고 있다.

리팩토링한 Svelte 코드 (합성 컴포넌트 적용)

<script>
  export let title;
  export let description;
  export let onClick;
</script>

<Card>
  <h2>{title}</h2>
  <p>{description}</p>
  <button on:click={onClick}>자세히 보기</button>
</Card>
Enter fullscreen mode Exit fullscreen mode
<Card title={title1} description={description1} onClick={handleClick1} />
<Card title={title2} description={description2} onClick={handleClick2} />
<Card title={title3} description={description3} onClick={handleClick3} />
Enter fullscreen mode Exit fullscreen mode

이제 <Card> 컴포넌트는 title, description, onClick을 props로 받아 재사용할 수 있게 되었다.

React 코드로 같은 구조를 적용하면?
Svelte에서 적용한 방식을 React에서도 똑같이 적용할 수 있다.

const Card = ({ title, description, onClick }: { title: string; description: string; onClick: () => void }) => {
  return (
    <div className="p-4 border rounded-lg">
      <h2>{title}</h2>
      <p>{description}</p>
      <button onClick={onClick}>자세히 보기</button>
    </div>
  );
};

<Card title="제목1" description="설명1" onClick={() => console.log("클릭1")} />
<Card title="제목2" description="설명2" onClick={() => console.log("클릭2")} />
<Card title="제목3" description="설명3" onClick={() => console.log("클릭3")} />
Enter fullscreen mode Exit fullscreen mode

React에서는 props를 활용해서 같은 방식으로 합성 컴포넌트를 만들 수 있다.
여기서 Card 컴포넌트를 단순히 title, description, onClick을 받아서 렌더링하는 형태로 만들었다.

Result: 유지보수성이 높아지고, 코드가 간결해졌다
리팩토링 전에는 같은 UI를 여러 번 반복해서 작성해야 했지만, 리팩토링 후에는 Card 컴포넌트 하나만 만들어서 여러 곳에서 사용할 수 있게 되었다. 이 방식이 가지는 장점은 명확하다.

중복 코드 제거 → 한 번만 정의하면 어디서든 재사용 가능
유지보수 용이 → 디자인 변경이 생겨도 Card 컴포넌트만 수정하면 됨
더 나은 가독성 → 불필요한 반복이 줄어들어 읽기 쉬워짐

마무리

Svelte와 React는 문법적인 차이는 있지만, 합성 컴포넌트 개념을 적용하는 방식은 거의 비슷하다. 공통적으로 중복 코드를 최소화하고, 재사용성을 높이는 것이 중요하다. 실제로 이 방식으로 리팩토링한 이후, 코드 변경이 있을 때 수정해야 할 부분이 줄어들면서 개발 속도가 빨라졌다.

결국, 페이지 컴포넌트를 잘게 나누고, 필요한 데이터만 props로 넘겨주는 방식이 유지보수성과 확장성 측면에서 더 낫다는 걸 다시 한 번 깨닫게 되었다.

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

Top comments (0)

Billboard image

Create up to 10 Postgres Databases on Neon's free plan.

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Try Neon for Free →