React 컴포넌트 설계 가이드: 어떻게 나눠야 관리하기 쉬울까
Web

React 컴포넌트 설계 가이드: 어떻게 나눠야 관리하기 쉬울까


리액트를 공부할 때 가장 먼저 부딪히는 질문 중 하나는 “컴포넌트를 어디까지 나눠야 하지?”입니다. 처음에는 JSX를 한 파일에 몰아넣기 쉽고, 반대로 익숙해지면 너무 잘게 쪼개서 오히려 읽기 어려운 구조를 만들기도 합니다.

컴포넌트 설계는 문법보다 사고방식의 문제에 가깝습니다. 화면을 어떻게 나눌지, 데이터는 어디까지 내려보낼지, 어떤 부분이 재사용될지를 먼저 생각해야 나중에 상태 관리와 성능 문제도 덜 복잡해집니다.

이 글에서는 아래 내용을 정리합니다.

  • UI를 컴포넌트로 나누는 기본 기준
  • props 경계를 잡는 실용적인 방법
  • 초보자가 자주 하는 과분해 실수

결론부터 말하면 컴포넌트는 “재사용”보다 먼저 “역할이 분리되는가”를 기준으로 나누는 편이 안정적입니다.

React 컴포넌트 설계란 무엇인가

React 컴포넌트는 화면 조각이면서 동시에 동작 단위입니다. 단순히 HTML을 분리하는 것이 아니라, 특정 책임을 가진 UI 블록을 독립적으로 다루는 방식이라고 보는 편이 좋습니다.

예를 들어 상품 목록 페이지가 있다면 아래처럼 나눠볼 수 있습니다.

  • ProductListPage: 페이지 데이터와 전체 흐름 담당
  • FilterBar: 정렬, 카테고리 필터 담당
  • ProductList: 목록 렌더링 담당
  • ProductCard: 개별 상품 카드 담당

이 구조가 좋은 이유는 각 컴포넌트가 “무엇을 책임지는지” 설명하기 쉽기 때문입니다. 반대로 한 파일이 검색, 필터, 목록, 카드, 빈 상태 UI까지 전부 처리하면 수정 지점이 빠르게 늘어납니다.

언제 컴포넌트를 분리해야 할까

아래 질문 중 두세 개 이상에 “예”라고 답하면 분리할 시점일 가능성이 큽니다.

  • 이 UI 블록의 역할을 한 문장으로 설명할 수 있는가?
  • 같은 파일에서 너무 멀리 떨어진 상태와 이벤트를 계속 오가고 있는가?
  • 비슷한 구조가 두 번 이상 반복되는가?
  • 조건문이 많아져서 JSX 흐름을 읽기 어려운가?
  • 이 부분만 따로 테스트하거나 교체하고 싶은가?

반대로 재사용 가능성이 아직 없어도 역할이 분명하면 분리할 가치가 있습니다. “나중에 재사용할 것 같아서”라는 이유만으로 쪼개면 오히려 추상화가 너무 빨라질 수 있습니다.

가장 쉬운 분리 기준은 역할 나누기다

초보자에게 가장 안전한 기준은 디자인 단위가 아니라 역할 단위입니다.

예를 들어 아래처럼 작성된 페이지가 있다고 해봅시다.

export default function ProfilePage() {
  return (
    <section>
      <h1>My Profile</h1>
      <div>
        <img src="/avatar.png" alt="avatar" />
        <p>Hobokai</p>
        <button>Edit</button>
      </div>
      <div>
        <h2>Recent Posts</h2>
        <ul>
          <li>React notes</li>
          <li>Astro tips</li>
        </ul>
      </div>
    </section>
  );
}

처음에는 괜찮아 보이지만 기능이 늘어나면 구조가 금방 섞입니다. 이럴 때는 시각적 크기보다 역할을 기준으로 나누는 편이 좋습니다.

function ProfileHeader() {
  return (
    <div>
      <img src="/avatar.png" alt="avatar" />
      <p>Hobokai</p>
      <button>Edit</button>
    </div>
  );
}

function RecentPosts() {
  return (
    <div>
      <h2>Recent Posts</h2>
      <ul>
        <li>React notes</li>
        <li>Astro tips</li>
      </ul>
    </div>
  );
}

이렇게 나누면 나중에 ProfileHeader에 편집 상태를 넣거나, RecentPosts에 로딩 상태를 추가할 때 영향 범위가 줄어듭니다.

props 경계는 어떻게 잡아야 할까

컴포넌트를 나눈 뒤 바로 생기는 문제가 props입니다. 보통 처음에는 부모가 모든 데이터를 쥐고 있고, 자식에게 필요한 값을 하나씩 넘기게 됩니다. 이 자체는 자연스럽지만, props가 지나치게 많아지면 신호로 받아들이는 편이 좋습니다.

예를 들어 자식 컴포넌트가 아래처럼 너무 많은 값을 요구한다면 구조를 다시 봐야 할 수 있습니다.

<ProductCard
  title={title}
  price={price}
  thumbnail={thumbnail}
  isSoldOut={isSoldOut}
  onAddToCart={onAddToCart}
  onFavorite={onFavorite}
  discountRate={discountRate}
  reviewCount={reviewCount}
/>

이럴 때는 관련 데이터를 객체로 묶거나, 정말 이 컴포넌트가 그만큼 많은 책임을 가져야 하는지 다시 확인해보면 좋습니다.

<ProductCard
  product={product}
  onAddToCart={onAddToCart}
  onFavorite={onFavorite}
/>

중요한 건 props 개수 자체보다 “이 컴포넌트가 어떤 입력을 받아 어떤 역할을 수행하는가”가 선명한지입니다.

초보자가 자주 하는 실수

1. 너무 작은 태그 단위까지 분리하기

Title, Subtitle, Wrapper처럼 의미보다 모양만 가진 컴포넌트를 너무 빨리 만들면 오히려 읽는 사람이 전체 구조를 따라가기 어려워집니다.

2. 공통화 시점을 너무 앞당기기

비슷해 보이는 카드 두 개를 바로 하나로 합치면 조건문만 많은 난해한 컴포넌트가 되기 쉽습니다. 두세 번 충분히 반복된 뒤 공통화해도 늦지 않습니다.

3. 상태 위치를 함께 고민하지 않기

컴포넌트만 분리하고 상태는 아무 데나 두면 props drilling이 심해집니다. 컴포넌트 설계는 결국 상태 위치와 같이 봐야 합니다. 이 부분은 React state와 props 가이드에서 이어서 보면 좋습니다.

실전에서 바로 쓰는 체크리스트

컴포넌트를 나누기 전에 아래 질문을 빠르게 확인해보세요.

  1. 이 컴포넌트의 책임을 한 문장으로 말할 수 있는가?
  2. 부모가 자식 내부 구현까지 지나치게 알고 있지는 않은가?
  3. props 이름만 봐도 데이터 의미가 드러나는가?
  4. 비슷한 조건 분기가 너무 많아지지는 않았는가?
  5. 나중에 수정할 때 영향 범위를 예상하기 쉬운가?

이 다섯 가지가 정리되면 파일 수가 조금 늘어나더라도 구조는 오히려 단순해집니다.

FAQ

Q. 재사용되지 않아도 컴포넌트로 분리해도 되나요?

그렇습니다. 재사용보다 역할 분리가 더 중요한 기준일 때가 많습니다.

Q. 몇 줄 이상이면 분리해야 하나요?

정해진 줄 수 기준은 없습니다. 줄 수보다 책임이 섞이는지가 더 중요합니다.

Q. styled component나 CSS 모듈 구조도 같이 나눠야 하나요?

보통은 컴포넌트 책임이 먼저입니다. 스타일 구조는 그 책임을 따라가게 두는 편이 유지보수에 유리합니다.

먼저 읽어볼 가이드

검색 유입이 많은 핵심 글부터 이어서 보세요.