[React] 리액트 컴포넌트를 만들 때 고려해야 할 것들

박효진 (@gywlsp)

이 글은 React 컴포넌트를 만들 때 고려해야할 사항들에 대해 설명한다.

컴포넌트를 어떻게 나눌까?

컴포넌트는 새로운 함수나 객체를 만들 때처럼 만들면 된다. 이때 단일 책임 원칙에 유의해야 한다. 이는 컴포넌트가 한 가지 일을 하는 것이 이상적이라는 원칙이다.

아래는 단일 책임 원칙에 유의하여 컴포넌트를 분리한 예시이다.

https://ko.reactjs.org/static/eb8bda25806a89ebdc838813bdfa3601/6b2ea/thinking-in-react-components.png

  1. FilterableProductTable(노란색): 예시 전체를 포괄한다.
  2. SearchBar(파란색): 모든 유저의 입력(user input)을 받는다.
  3. ProductTable(연두색): 유저의 입력(user input)을 기반으로 데이터 콜렉션(data collection)을 필터링 해서 보여준다.
  4. ProductCategoryRow(하늘색): 각 카테고리(category)의 헤더를 보여준다.
  5. ProductRow(빨강색): 각각의 제품(product)에 해당하는 행을 보여준다.

또한 UI 일부가 여러 번 사용되거나 (Button, Panel, Avatar), UI 일부가 자체적으로 복잡한(App, Comment) 경우에도 별도의 컴포넌트로 분리하는 게 좋다.

네이밍

컴포넌트, props, state, 변수, 함수 등 모두 각각의 역할이 명확히 드러나도록 이름을 지어야 한다.

컴포넌트

컴포넌트의 역할을 명확하게 알 수 있도록 이름을 지어야 한다. (ex: UserFollowButton)

재사용되는 컴포넌트라면 범용성을 고려하여 이름을 지어야 한다. (bad: CommentAuthorImage, good: Avatar)

구체적인 맥락에서 쓰이는 컴포넌트라면 그 맥락을 알 수 있게 이름을 지어야 한다. (ex. RankingHeader)

props

props의 이름은 사용될 context가 아닌 컴포넌트 자체의 관점에서 짓는 것이 좋다.

Comment 컴포넌트에 Comment를 단 사람의 프로필 이미지를 나타내는 Avatar 컴포넌트가 존재한다고 가정하자.

Avatar는 자신이 Comment 내에서 렌더링 된다는 것을 알 필요가 없다. 따라서 props의 이름을 author로 쓰는 것보단 user로 쓰는 것이 낫다.

function Avatar(props) {
  return (
    <img className="Avatar" src={props.user.avatarUrl} alt={props.user.name} />
  );
}

무엇을 state로?

애플리케이션을 올바르게 만들기 위해서는 애플리케이션이 필요로 하는 가장 최소한의 state를 찾고, 이를 통해 나머지 모든 것들이 필요에 따라 그때그때 계산되도록 만들어야 한다.

핵심은 중복배제원칙이다. 예를 들어 TODO 리스트를 만든다고 하면, TODO 아이템을 저장하는 배열만 유지하고 TODO 아이템의 개수를 표현하는 state를 별도로 만들지 않아도 된다. 배열의 길이를 이용해 TODO 아이템의 개수를 표현하면 되기 때문이다.

어떤 애플리케이션이 다음과 같은 데이터를 가지고 있다고 가정하자.

  • 제품의 원본 목록(API에서 데이터를 받음)
  • 유저가 입력한 검색어
  • 체크박스의 값
  • 필터링된 제품들의 목록

어떤 게 state가 되어야 할까? 이는 아래 세 가지 질문을 통해 결정할 수 있다.

  1. 부모로부터 props를 통해 전달되는가? 그렇다면 state가 아니다.
  2. 시간이 지나도 변하지 않는가? 그렇다면 state가 아니다.
  3. 컴포넌트 안의 다른 state나 props를 가지고 계산 가능한가? 그렇다면 state가 아니다.

위 기준을 가지고 예시 목록들 각각이 state가 되어야 하는지 판단해보자.

  • 제품의 원본 목록 : state가 아니다. API에서 데이터를 받아 부모로부터 props로 전달되기 때문이다.
  • 유저가 입력한 검색어, 체크박스 : state이다. 시간이 지남에 따라 변하기도 하면서 다른 것들로부터 계산될 수 없기 때문이다.
  • 필터링된 제품들의 목록 : state가 아니다. 제품의 원본 목록과 검색어, 체크박스의 값을 조합해서 계산해낼 수 있기 때문이다.

state의 위치?

어떤 컴포넌트가 어떤 state를 가져야 할지 결정하기 어렵다면 아래 과정을 따라 결정하면 보다 쉽다.

애플리케이션이 가지는 각각의 state에 대해서

  • state를 기반으로 렌더링하는 모든 컴포넌트를 찾는다.
  • 공통 소유 컴포넌트 (common owner component; 계층 구조 내에서 특정 state가 있어야 하는 모든 컴포넌트들의 상위에 있는 하나의 컴포넌트)를 찾는다.

공통 혹은 더 상위에 있는 컴포넌트가 state를 가져야 한다. state를 소유할 적절한 컴포넌트가 없다면, state를 소유하는 컴포넌트를 하나 만들어서 공통 오너 컴포넌트의 상위 계층에 추가하면 된다.

다른 컴포넌트 간에 존재하는 state를 동기화하려고 노력하는 대신, 상위 컴포넌트에 하나의 state를 두고 하향식 데이터 흐름에 기대는 것이 좋다.

하향식 데이터 흐름

트리 구조가 props의 폭포라고 상상하면 각 컴포넌트의 state는 임의의 점에서 만나지만 동시에 아래로 흐르는 부가적인 수원이라고 할 수 있다.

모든 state는 항상 특정한 컴포넌트가 소유하고 있으며, 그 state로부터 파생된 UI 또는 데이터는 오직 트리구조에서 자신의 아래에 있는 컴포넌트에만 영향을 미친다.

이를 하향식 데이터 흐름 또는 단방향 데이터 흐름이라고 부른다.

*React 공식 문서를 바탕으로 작성한 글입니다.