githubEdit

컴포넌트 간 State 공유하기

하나 이상의 컴포넌트의 state가 동시에 변경되고자 할때, 각 컴포넌트 내부의 state를 제거하고 두 컴포넌트의 공통 부모 컴포넌트로 state를 옮겨야 합니다. 그리고 그 state를 두 컴포넌트의 props로 전달해야 합니다.

이를 ‘State 끌어올리기’라고 하며 가장 흔한 코드입니다.

State 끌어올리기

Panel이라는 하나의 컴포넌트를 여러 곳에서 사용할 때, Panel의 활성화 상태에 대한 state는 각 Panel에 독립적으로 적용됩니다. 따라서 여러 Panel들은 서로의 활성화 상태를 알 수 없습니다.

하지만 만약 여러개의 Panel이 동시에 활성화하지 못하게 하고싶다면 어떨까요?

여러 Panel 중 하나의 Panel만 활성화하려면 State 끌어올리기를 해야합니다.

다음 코드와 같이 공통 부모 컴포넌트에 activeIndex라는 state를 만들고, 이를 Panel의 props에 넣어 선택된 하나의 Panel만이 활성화 되도록 합니다.

import { useState } from 'react';

export default function Accordion() {
  const [activeIndex, setActiveIndex] = useState(0);
  return (
    <>
      <h2>Almaty, Kazakhstan</h2>
      <Panel
        title="About"
        isActive={activeIndex === 0}
        onShow={() => setActiveIndex(0)}
      >
        With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
      </Panel>
      <Panel
        title="Etymology"
        isActive={activeIndex === 1}
        onShow={() => setActiveIndex(1)}
      >
        The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
      </Panel>
    </>
  );
}

function Panel({
  title,
  children,
  isActive,
  onShow
}) {
  return (
    <section className="panel">
      <h3>{title}</h3>
      {isActive ? (
        <p>{children}</p>
      ) : (
        <button onClick={onShow}>
          Show
        </button>
      )}
    </section>
  );
}

React의 단방향 데이터 흐름과 State 끌어올리기

위와 같은 State 끌어올리기가 필요한 이유는, React가 단방향 데이터 흐름을 갖기 때문입니다.

React에서는 데이터가 부모 → 자식으로만 흘러갑니다. 부모 컴포넌트가 props를 통해 자식 컴포넌트에 데이터를 전달하며, 자식 컴포넌트는 부모의 state를 직접 수정할 수 없습니다.

자식이 부모의 state를 변경하려면, 부모 컴포넌트에서 상태를 업데이트하는 함수를 정의하고, 이를 자식에게 props로 전달해야 합니다. 자식은 그 함수를 호출하여 부모의 상태를 업데이트할 수 있습니다.

위의 예시에서는 부모 컴포넌트 Accordion에서 activeIndex라는 상태를 관리하고, 자식 Panel 컴포넌트로 isActiveonShow라는 props를 전달합니다. onShow는 자식 컴포넌트에서 호출될 때 부모의 상태를 업데이트합니다. 자식 컴포넌트는 부모의 상태(activeIndex)를 기준으로 자신의 UI를 렌더링합니다.

이는 React의 단방향 데이터 흐름을 보여줍니다.

단방향 데이터 흐름이 필요한 이유

React가 단방향 데이터 흐름을 사용하는 이유는 데이터 흐름을 예측 가능하게 만들기 위해서입니다. 만약 데이터가 부모 ↔ 자식 간 양방향으로 자유롭게 이동한다면 다음과 같은 문제가 생길 수 있습니다:

  1. 상태 관리의 혼란:

    • 상태를 누가 관리하는지 알기 어려워지고, 유지보수가 어려워질 수 있습니다.

  2. 디버깅 어려움:

    • 데이터의 변화를 추적하기가 어렵습니다.

    • 양방향 바인딩으로 인해 불필요한 렌더링이 발생하거나, 의도치 않은 상태 변경이 일어날 수 있습니다.

React에서는 이를 방지하기 위해 단방향 데이터 흐름을 기본 원칙으로 하고, 데이터의 흐름을 부모 → 자식으로만 제한합니다. 이로 인해 상태 관리와 UI 업데이트의 흐름이 명확해집니다.

양방향 바인딩

방향 바인딩에서는 UI와 데이터 모델 간에 동기화가 자동으로 이루어집니다.

  • 데이터가 변경되면 UI가 자동으로 업데이트되고,

  • 반대로 UI에서의 변화도 데이터 모델에 자동으로 반영됩니다.

1. Angular

Angular는 양방향 바인딩을 기본적으로 지원하는 프레임워크입니다. Angular에서 양방향 바인딩을 구현하기 위해 [(ngModel)]이라는 디렉티브를 사용합니다.

2. Vue.js

Vue.js도 양방향 바인딩을 기본적으로 지원하는 프레임워크입니다. v-model 디렉티브를 사용해 양방향 바인딩을 구현합니다.

장점

  1. 생산성 증가:

    • UI와 데이터 모델 간에 동기화 작업을 개발자가 직접 처리하지 않아도 되므로 생산성이 높아집니다.

  2. 코드 간결화:

    • 데이터와 UI를 연결하는 코드가 간단해지고 직관적입니다.

단점

  1. 복잡성 증가:

    • 데이터 흐름이 양방향으로 이루어지기 때문에, 상태의 변화를 추적하기 어려울 수 있습니다.

    • 대규모 애플리케이션에서 데이터 흐름을 관리하기가 어려워질 수 있습니다.

  2. 디버깅 어려움:

    • 상태가 여러 곳에서 변경될 수 있어, 문제를 파악하고 수정하기 어렵습니다.

  3. 성능 문제:

    • UI와 데이터가 항상 동기화되기 때문에, 불필요한 업데이트가 발생할 가능성이 있습니다.

결론적으로 단방향 데이터 흐름은 자동으로 동기화되지 않는 대신, 데이터 흐름을 명확히 관리할 수 있습니다. 따라서 이는 데이터가 더욱 복잡해질수록 유리합니다.

제어와 비제어 컴포넌트

“제어되지 않은” 몇몇 지역 state를 갖는 컴포넌트를 사용하는 것은 흔한 일입니다. 예를 들어 isActive state를 갖는 원래의 Panel 컴포넌트는 해당 컴포넌트의 부모에서 패널의 활성화 여부에 영향을 줄 수 없기 때문에 제어되지 않습니다.

반대로 컴포넌트의 중요한 정보가 자체 지역 state 대신 props에 의해 만들어지는 경우 컴포넌트가 “제어된다”고 합니다. 이를 통해 부모 컴포넌트가 동작을 완전히 지정할 수 있습니다. isActive prop을 갖는 최종 Panel 컴포넌트는 Accordion 컴포넌트에 의해 제어됩니다.

비제어 컴포넌트는 설정할 것이 적어 부모 컴포넌트에서 사용하기 더 쉽습니다. 하지만 여러 컴포넌트를 함께 조정하려고 할 때 비제어 컴포넌트는 덜 유연합니다. 제어 컴포넌트는 최대한으로 유연하지만, 부모 컴포넌트에서 props로 충분히 설정해주어야 합니다.

쉽게 말하면, 컴포넌트를 (props를 통해) “제어”할지 (state로) “비제어” 할지 고려해야 한다고 할 수 있습니다.

React의 단일 진리의 원천(Single Source of Truth)

애플리케이션에서 특정 데이터는 한 곳에서만 관리되어야 합니다. 중복된 상태를 여러 컴포넌트에서 관리하면 혼란과 버그가 생길 수 있기 때문입니다. 따라서 공유된 상태공통 부모 컴포넌트에 두고, 필요한 자식 컴포넌트에게 내려주는 방식으로 관리합니다.

복잡한 상태를 관리할수록 "단일 진리의 원천"을 유지하는 것이 중요합니다.

컴포넌트가 많아지면 어떤 상태가 어디에 존재해야 할지 고민하게 되는데, 이를 React의 기본 원칙에 따라 신중히 설계하면 코드를 깔끔하고 유지보수 가능하게 만들 수 있습니다.

Last updated