학습 자료

컴포넌트 — 어디서 잘라야 하나


Article

2강에서 컴포넌트는 그냥 함수라고 했다. 문법은 그게 전부다.

어려운 건 문법이 아니라 어디서 자를 것인가다. 너무 잘게 나누면 파일만 늘어나고, 안 나누면 한 파일이 500줄이 된다.

나누지 않으면 어떻게 되나

상품 목록 페이지를 한 컴포넌트에 다 넣어본다.

function ProductPage() {
  return (
    <div>
      <header>
        <img src="/logo.png" alt="로고" />
        <nav>…메뉴 열 개…</nav>
      </header>

      <section>
        <div className="card">
          <img src="/p1.png" alt="상품1" />
          <h3>노트북</h3>
          <p>1,200,000원</p>
          <button>담기</button>
        </div>
        <div className="card">
          <img src="/p2.png" alt="상품2" />
          <h3>마우스</h3>
          <p>30,000원</p>
          <button>담기</button>
        </div>
      </section>

      <footer></footer>
    </div>
  );
}

문제가 눈에 보인다.

한 덩어리 컴포넌트의 증상
  1. 01

    같은 마크업이 반복된다

    카드가 열 개면 같은 코드를 열 번 쓴다. 디자인이 바뀌면 열 군데를 고친다.

  2. 02

    전체를 읽어야 한 곳을 안다

    버튼 하나 고치려고 400줄을 스크롤한다.

  3. 03

    이름이 없다

    div.card 가 무엇인지 마크업을 읽어야 안다. ProductCard 라는 이름이 있으면 읽을 필요가 없다.

나누는 기준

파일을 나누는 게 목적이 아니다. 하나의 컴포넌트는 하나의 일만 한다.

무엇을 하는가로 자른다
  • ProductPage페이지를 조립한다
    • Header로고와 메뉴를 보여준다
    • ProductList상품들을 늘어놓는다
      • ProductCard상품 하나를 보여준다
    • Footer저작권을 보여준다

이렇게 자르고 나면 ProductPage는 이렇게 짧아진다.

function ProductPage() {
  return (
    <>
      <Header />
      <ProductList />
      <Footer />
    </>
  );
}

페이지가 무엇으로 이뤄졌는지 한눈에 보인다. 세부 내용은 각자의 파일에 있다.

언제 자르지 않나

성급하게 나누는 것도 문제다.

자를 때와 두는 때

자른다

이럴 때 나눈다

  • 같은 마크업이 두 번 이상 반복된다
  • 이름이 자연스럽게 떠오른다
  • 다른 화면에서도 쓸 것 같다
  • 한 컴포넌트가 화면을 채운다

둔다

아직 나누지 않는다

  • "나중에 쓸 것 같아서"
  • 이름이 안 떠오른다
  • props 를 다섯 개 넘게 넘겨야 한다
  • 잘랐더니 오히려 읽기 어렵다

props를 다섯 개 넘게 넘겨야 한다면 잘못 자른 것이다. 조각이 부모에게 너무 많이 의존한다는 뜻이다. 5강에서 props를 배우고 나면 이 감각이 생긴다.

한 파일에 여러 컴포넌트를 둬도 되나

된다. 문법적으로 제한이 없다.

function Avatar({ src }) {
  return <img className="avatar" src={src} alt="" />;
}

function UserCard({ user }) {
  return (
    <div className="card">
      <Avatar src={user.image} />
      <span>{user.name}</span>
    </div>
  );
}

export default UserCard;

AvatarUserCard에서만 쓰인다면 같은 파일에 둔다. 파일을 늘리지 않으면서 마크업을 정리할 수 있다.

다른 곳에서도 쓰게 되면 그때 파일을 분리한다. 미리 나누지 않는다.

절대 하면 안 되는 것

컴포넌트를 다른 컴포넌트 안에 정의하는 것이다.

function App() {
  function Greeting() {
    return <p>안녕</p>;
  }

  return <Greeting />;
}

동작은 한다. 그런데 App이 다시 실행될 때마다 Greeting새 함수로 다시 만들어진다. 자바스크립트 편 11강에서 "함수는 값"이라고 했으니 매번 다른 값이다.

React는 그걸 완전히 다른 컴포넌트로 보고 화면을 통째로 새로 그린다. 안에 있던 상태도 전부 날아간다.

파일과 폴더

정답은 없지만 흔히 쓰는 방식이 있다.

익숙한 구조
  • src
    • components여러 곳에서 쓰는 조각
      • Button.jsx
      • Avatar.jsx
    • pages화면 단위
      • ProductPage.jsx
    • App.jsx
    • main.jsx

파일명은 컴포넌트 이름과 똑같이, 대문자로 시작한다. ProductCard.jsx 안에는 ProductCard가 있다. 찾기 쉽다.

규칙
컴포넌트 이름 = 파일 이름ProductCard.jsx
대문자로 시작 (파스칼 케이스)SearchBar.jsx
기본 내보내기 하나export default ProductCard
폴더는 역할로 나눈다components / pages

정리하면

  • 컴포넌트를 나누는 건 파일을 늘리려는 게 아니다. 반복을 없애고 이름을 붙이려는 것이다.
  • 이름이 자연스럽게 떠오르면 자를 때다. Wrapper밖에 안 떠오르면 아직 아니다.
  • 작을수록 좋은 게 아니다. 의미 단위로 자른다.
  • props를 다섯 개 넘게 넘겨야 한다면 잘못 자른 신호다.
  • 컴포넌트 안에 컴포넌트를 정의하지 않는다. 상태가 날아가고 원인을 찾기 어렵다.
  • 파일명과 컴포넌트 이름을 맞추고, 대문자로 시작한다.

다음 강의에서는 부모가 자식에게 값을 건네는 방법, props를 배운다. 지금까지의 컴포넌트는 항상 같은 화면만 그렸다.

컴포넌트 — 어디서 잘라야 하나 — 디코드랩(DCODELAB)