들어가며

useContext를 설명하기 이전에 기존에 사용하던 콘텍스트 API의 문제점을 알아야 한다.

콘텍스트 API는 반복적인 데이터 전달 코드를 방지하고자 사용하는데, 이러한 콘텍스트 API에도 문제점이 존재한다. 바로 ProviderConsumer를 중첩해서 사용할 수록 코드의 깊이가 깊어진다는 점이다.

ProviderConsumer를 중첩한 코드를 통해 어떠한 문제점이 있는지 알아보자. 이렇게 코드가 여러 번 중첩될 일은 드물겠지만 프로젝트의 크기가 커지면서 중첩할 일이 생기게 된다면 자식 컴포넌트 코드에서는 부모 컴포넌트에서 넘긴 데이터를 받기 위해 Consumer 컴포넌트를 여러 번 사용해야 하고, 코드가 점점 복잡해지고 이해하기 어려워진다.

/* 부모 컴포넌트 */
<A.Provider>
  <B.Provider>
    <C.Provider>
      <D.Provider>
      </D.Provider>
    </C.Provider>
  </B.Provider>
</A.Provider>

/* 자식 컴포넌트 */
<A.Consumer>
  {data1 => (
    <B.Consumer>
      {data2 => (
        <C.Consumer>
          {data3 => (
            <D.Consumer>
              {data4 => (
                /* 자식 컴포넌트 내용 */
              )}
            </D.Consumer>
          )}
        </C.Consumer>
      )}
    </B.Consumer>
  )}
</A.Consumer>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

React Hooks의 종류인 useContext를 사용하면 Consumer를 사용함에 따라 코드가 깊어지는 것을 방지할 수 있다.

useContext 사용

useContext는 기존에 생성한 콘텍스트 객체를 사용해서, 데이터를 가지고 오는 함수로 Consumer 컴포넌트의 역할을 대신한다고 보면 된다.

const 변수명 = useContext(콘텍스트 객체);
1

예를 들어 App 컴포넌트에서 Provider룰 3번 중첩해서 데이터를 넘기는 경우가 있다고 가정해보자. 그리고 user, product, sale 모든 데이터의 사용이 필요한 ProductMeta라는 컴포넌트가 있을 경우, ConsumeruseContext는 아래와 같이 서로 다른 방식으로 데이터를 받아오게 된다.

function App() {
  const [user] = useState({ name: "홍길동" });
  const [product] = useState({ name: "상품" });
  const [sale] = useState({ discount_rate: 7 })

  return (
    <UserContext.Provider value={user}>
      <ProductContext.Provider value={product}>
        <SaleContext.Provider value={sale}>
          <Product></Product>
        </SaleContext.Provider>
      </ProductContext.Provider>
    </UserContext.Provider>
  )
}

function Product() {
  return (
    <div>
      <ProductMeta>
      </ProductMeta>
    </div>
  ) 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

Consumer 사용 방식

useContext를 사용하지 않고 작성하던 기존 방법이다. 부모가 Provider 컴포넌트로 전달한 데이터를 받기 위해서는 해당 개수 만큼의 Consumer 컴포넌트를 사용해서 데이터를 받아와야 하는데, 아래 코드를 보면 알겠지만 코드가 깊어지고 한 눈에 알아보기 어렵다는 것을 알 수 있다.

function ProductMeta() {
  return (
    <div>
      <UserContext.Consumer>
        {(user) => (
          <ProductContext.Consumer>
            {(product) => (
              <SaleContext.Consumer>
                {(sale) => (
                  <>
                    <div>유저 이름: {user.name}</div>
                    <div>제품 이름: {product.name}</div>
                    <div>할인율: {sale.discount_rate}%</div>
                  </>
                )}
              </SaleContext.Consumer>
            )}
          </ProductContext.Consumer>
        )}
      </UserContext.Consumer>
    </div>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

useContext 사용 방식

다음은 useContext를 사용한 방식으로 Consumer 컴포넌트를 사용했을 때와 비교하면 코드가 간결하고 이해하기 쉽다.

단순히 ProductMeta 한 개의 컴포넌트에서도 눈에 띄는 차이가 있는 만큼, 규모가 큰 프로젝트에서 ProductMeta와 같이 여러 개의 Provider로부터 데이터를 받아오는 컴포넌트가 여러 개 필요하다고 생각해보면 Consumer 대신 useContext를 사용해서 코드를 작성하는게 좋다.

function ProductMeta() {
  const user = useContext(UserContext);
  const product = useContext(ProductContext);
  const sale = useContext(SaleContext);

  return (
    <div>
      <div>유저 이름: {user.name}</div>
      <div>제품 이름: {product.name}</div>
      <div>할인율: {sale.discount_rate}%</div>
    </div>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
Last Updated:
Contributors: dailyuno