React Context API 성능 이슈와 해결

문제 상황

회사 프로젝트에서 Redux를 걷어내고 Context API로 전환하는 작업을 진행했다. Hooks가 안정화되면서 간단한 전역 상태는 Context로 충분하다고 판단했기 때문이다.

그런데 UserContext에 사용자 정보를 담아두니 user 객체의 특정 필드만 바뀌어도 해당 Context를 구독하는 모든 컴포넌트가 리렌더링됐다. 프로필 페이지에서 닉네임만 수정해도 Header, Sidebar 등 관련 없는 컴포넌트까지 렌더링되는 현상이었다.

시도한 해결책

1. Context 분리

하나의 Context에 모든 상태를 담지 않고 변경 빈도에 따라 분리했다.

const UserInfoContext = createContext();
const UserSettingsContext = createContext();

function UserProvider({ children }) {
  const [userInfo, setUserInfo] = useState({ id, name, avatar });
  const [settings, setSettings] = useState({ theme, language });
  
  return (
    <UserInfoContext.Provider value={userInfo}>
      <UserSettingsContext.Provider value={settings}>
        {children}
      </UserSettingsContext.Provider>
    </UserInfoContext.Provider>
  );
}

2. useMemo로 value 메모이제이션

Provider의 value가 매번 새 객체로 생성되는 문제를 방지했다.

const value = useMemo(() => ({ user, updateUser }), [user]);

return (
  <UserContext.Provider value={value}>
    {children}
  </UserContext.Provider>
);

3. 구독 최적화

정말 필요한 데이터만 구독하도록 커스텀 훅을 만들었다.

function useUserName() {
  const user = useContext(UserContext);
  return user.name;
}

결과

Context 분리와 메모이제이션으로 불필요한 렌더링이 80% 이상 줄었다. Chrome DevTools의 React Profiler로 측정했을 때 프로필 업데이트 시 렌더링되는 컴포넌트 수가 23개에서 4개로 줄어들었다.

다만 상태가 복잡해지면 여전히 Redux나 MobX가 나은 선택일 수 있다는 걸 깨달았다. Context는 변경이 적은 전역 값(테마, 언어, 인증 정보)에 적합하다.

React Context API 성능 이슈와 해결