React 16.3 Context API로 전역 상태 관리 리팩토링
배경
회사 어드민 프로젝트는 Redux로 상태 관리를 하고 있었다. 하지만 단순히 유저 정보, 테마 설정 정도만 전역으로 관리하는 상황에서 액션 타입, 액션 생성자, 리듀서를 모두 작성하는 게 과하다는 생각이 들었다.
React 16.3에서 새로운 Context API가 정식으로 나왔고, 프로덕션에서 사용해도 괜찮다는 공식 입장이 나왔다. 이번 기회에 리팩토링을 진행했다.
기존 Redux 코드
// actions/user.js
export const SET_USER = 'SET_USER';
export const setUser = (user) => ({ type: SET_USER, payload: user });
// reducers/user.js
const initialState = { user: null };
export default function userReducer(state = initialState, action) {
switch (action.type) {
case SET_USER:
return { ...state, user: action.payload };
default:
return state;
}
}
이런 구조가 user, theme, notification 등 여러 개 존재했다.
Context API로 전환
// contexts/UserContext.js
import React, { Component, createContext } from 'react';
const UserContext = createContext();
export class UserProvider extends Component {
state = {
user: null
};
setUser = (user) => {
this.setState({ user });
};
render() {
return (
<UserContext.Provider value={{
user: this.state.user,
setUser: this.setUser
}}>
{this.props.children}
</UserContext.Provider>
);
}
}
export const UserConsumer = UserContext.Consumer;
사용하는 쪽은 다음과 같이 변경했다.
import { UserConsumer } from '../contexts/UserContext';
const Header = () => (
<UserConsumer>
{({ user, setUser }) => (
<div>
{user ? user.name : 'Guest'}
</div>
)}
</UserConsumer>
);
결과
- Redux 관련 코드 약 300줄 삭제
- 번들 사이즈 약 8KB 감소 (redux, react-redux 제거)
- 새로운 전역 상태 추가 시 Provider만 작성하면 됨
물론 복잡한 상태 로직이나 미들웨어가 필요한 경우엔 여전히 Redux가 유리하다. 하지만 우리 프로젝트처럼 단순한 케이스에선 Context API가 충분했다.
주의사항
Context value가 변경되면 해당 Context를 구독하는 모든 컴포넌트가 리렌더링된다. 성능 이슈가 생긴다면 Context를 용도별로 분리하거나, shouldComponentUpdate로 최적화가 필요하다.