React Context API 올바른 사용 패턴
React Context를 효율적으로 설계하고 적용하는 핵심 패턴과 성능 고려사항을 정리한 기술적 설명과 코드 예제 중심의 패턴 모음. 패턴
목차
소개
Context API는 전역 상태를 간편히 공유하는 도구다. 다만 단순 사용만으로는 성능 문제가 발생할 수 있다. 이 글에서는 context의 기본 개념을 짚고, 실무에서 자주 쓰는 사용 패턴과 react context 성능 최적화 방법을 설명한다. 초보자도 이해하기 쉬운 예제와 함께 context api 사용법을 정리한다.
언제 Context를 사용해야 하는가
Context는 컴포넌트 트리 깊숙이 데이터를 전달할 때 유용하다. 다만 모든 상태를 무작정 context로 옮기면 성능 저하가 생긴다. 다음 상황에서 적절하다.
- 다수 컴포넌트에서 접근하는 인증 정보
- 다크 모드 같은 UI 설정
- 로케일이나 테마 등 전역 설정
반면 빈번히 변경되는 대규모 상태는 지역 상태나 상태 관리 라이브러리를 고려한다.
기본 사용법 요약
Context를 만들고 Provider로 감싼 뒤 useContext로 값을 읽는다. 예시는 최소 구성으로 구성되어 있다.
const ThemeContext = React.createContext('light');
function App() {
return (
<ThemeContext.Provider value={{ theme: 'dark' }}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar() {
const ctx = React.useContext(ThemeContext);
return <div>현재 테마: {ctx.theme}</div>;
}
성능 문제와 원인
Provider의 value가 변경되면 그 Provider 아래 모든 소비자 컴포넌트가 재렌더링된다. 따라서 불필요한 재렌더링을 줄이는 것이 핵심이다. 특히 객체나 함수 값을 직접 inline으로 넘기면 매 렌더마다 참조가 바뀐다.
핵심 패턴 1: 분리된 Context
관련이 느슨한 데이터를 하나의 Context에 모으지 않고, 역할별로 분리한다. 이렇게 하면 일부 상태 변경 시 영향 범위를 좁힐 수 있다.
- AuthContext: 사용자 정보
- ThemeContext: UI 관련 설정
- SettingsContext: 앱 설정
핵심 패턴 2: Provider 내부에서 메모이제이션
value에 객체나 함수를 넘길 때는 useMemo와 useCallback으로 참조를 고정한다. 아래 예시는 메모이제이션 적용 방식이다.
function ThemeProvider({ children }) {
const [theme, setTheme] = React.useState('light');
const value = React.useMemo(() => ({ theme, setTheme }), [theme]);
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}
핵심 패턴 3: 선택자(selector) 패턴
모든 소비자가 Context 전체를 구독하면 불필요한 업데이트가 발생한다. 필요한 필드만 구독하도록 커스텀 훅을 만든다. selector 패턴을 사용하면 재렌더링을 더 잘 제어할 수 있다.
간단한 selector 훅 예
function useTheme() {
const { theme } = React.useContext(ThemeContext);
return theme;
}
function useThemeSetter() {
const { setTheme } = React.useContext(ThemeContext);
return setTheme;
}
핵심 패턴 4: Provider 위치 최적화
Provider는 필요한 최소 범위만 감싸도록 위치를 조정한다. 최상단에 모든 Provider를 두면 전체 트리가 영향을 받는다. 필요한 부분만 감싸면 렌더링 범위를 줄인다.
테스트와 타입 안정성
Context는 테스트하기 쉬워야 한다. 테스트에서는 Provider에 테스트용 값을 주입하여 컴포넌트를 격리한다. TypeScript 사용 시 Context 타입을 명확히 선언하면 의도치 않은 버그를 줄인다.
성능 측정과 디버깅
문제가 의심되면 프로파일링 도구를 사용한다. React DevTools의 Profiler가 유용하다. 또한 console.log로 렌더링 횟수를 추적하면 어느 컴포넌트가 빈번히 렌더되는지 파악하기 쉽다.
권장 실무 체크리스트
- 빈번히 변경되는 상태는 context로 옮기지 않았는지 확인
- Context는 역할별로 분리
- Provider의 value는 useMemo/useCallback으로 안정화
- 필요한 필드만 구독하는 커스텀 훅 작성
- Provider 범위를 최소화하여 렌더링 영역 축소
- 프로파일링으로 실제 성능 영향 확인
요약
Context는 매우 편리한 도구이나 잘못 쓰면 성능 문제가 생긴다. 위에서 다룬 분리, 메모이제이션, selector 패턴, Provider 위치 최적화 등을 적용하면 react context 성능 문제를 크게 완화할 수 있다. 또한 context api 사용법을 단순히 아는 것을 넘어서 설계 관점에서 접근하면 유지보수가 쉬운 코드가 된다. 마지막으로 복잡한 전역 상태는 리액트 글로벌 상태 관리 context만으로 해결하려 하기보다 상황에 맞는 도구를 함께 고려한다.