React 16.3 Context API로 Prop Drilling 해결하기

문제 상황

5단계 이상 중첩된 컴포넌트에서 사용자 정보를 전달해야 하는 상황이었다. 모든 중간 컴포넌트가 실제로 사용하지도 않는 props를 받아서 하위로 넘기는 전형적인 prop drilling 패턴이 발생했다.

// 기존 방식
<App user={user}>
  <Layout user={user}>
    <Container user={user}>
      <Sidebar user={user}>
        <Profile user={user} />
      </Sidebar>
    </Container>
  </Layout>
</App>

Redux를 고려했지만 이 프로젝트는 상태 관리가 복잡하지 않아 과한 선택지로 보였다.

Context API 적용

React 16.3에서 정식으로 개선된 Context API를 사용하기로 결정했다. 기존 레거시 context와 달리 명확한 Provider/Consumer 패턴을 제공한다.

// UserContext.js
import React from 'react';

const UserContext = React.createContext(null);

export const UserProvider = UserContext.Provider;
export const UserConsumer = UserContext.Consumer;
// App.js
import { UserProvider } from './UserContext';

class App extends React.Component {
  state = {
    user: { name: 'John', role: 'admin' }
  };

  render() {
    return (
      <UserProvider value={this.state.user}>
        <Layout>
          <Container>
            <Sidebar>
              <Profile />
            </Sidebar>
          </Container>
        </Layout>
      </UserProvider>
    );
  }
}
// Profile.js
import { UserConsumer } from './UserContext';

const Profile = () => (
  <UserConsumer>
    {user => (
      <div>
        <h2>{user.name}</h2>
        <span>{user.role}</span>
      </div>
    )}
  </UserConsumer>
);

결과

중간 컴포넌트들이 불필요한 props를 받지 않아도 되면서 코드가 간결해졌다. 다만 Consumer의 render props 패턴이 다소 장황한 느낌은 있다. 여러 context를 사용할 때 중첩이 심해지는 단점도 있지만, 현재 프로젝트 규모에서는 충분히 만족스러운 해결책이었다.

성능 측면에서도 Provider의 value가 변경될 때만 하위 Consumer가 리렌더링되므로 효율적이다.

React 16.3 Context API로 Prop Drilling 해결하기