React에서 전역 상태를 관리하는 방법인 useContext와 Redux를 정리한 글입니다.
useContext는 컴포넌트 트리의 모든 레벨을 통해 props를 전달하지 않고도 여러 컴포넌트 간에 상태를 공유할 수 있게 해주는 React의 내장 후크이다.
useContext는 컨텍스트 객체를 생성하고 이를 공유 상태에 액세스해야 하는 자식 구성 요소에 제공하는 방식으로 작동한다.
import React, { createContext, useContext, useState } from 'react';
//CountContext 객체 생성
const CountContext = createContext();
function Counter() {
const [count, setCount] = useState(0);
return (
//Provider 하위 요소들이 useContext를 통해 접근 가능
<CountContext.Provider value={count}>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
<ChildComponent />
</CountContext.Provider>
);
}
function ChildComponent() {
const count = useContext(CountContext);
return <h2>Count: {count}</h2>;
}
useContext는 component drilling 없이 구성 요소 간에 상태를 공유하기 위해 쓰인다. 그러나 여러 구성 요소에서 상태를 관리하는 것이 더 어려울 수 있으며 깊이 중첩된 구성 요소 트리로 인해 성능 문제가 발생할 수 있다.
useState는 Component의 로컬 상태 관리를 하기 위한 훅이고, useContext는 Component의 하위 요소들에게 전역적으로 상태를 접근하기 위한 훅이다.
예로 들어, 부모 요소인 <Parent />와 자식 요소인 <A />, 그리고 <A />의 자식 요소인 <B />가 있다고 했을 때, <Parent />에서 useState로 생성한 상태는 props drilling을 통하지 않으면 <B />에서는 접근이 불가능하도록 구조되어 있다. 그러나, useContext를 사용하면 접근이 가능하다.
Redux는 중앙화된 방식으로 애플리케이션의 상태를 관리하는 방법을 제공하는 상태 관리 라이브러리다.
Redux의 장점 중 하나는 복잡한 구조를 전역 상태로 쉽게 관리할 수 있다는 것이다.
코드를 더 체계적으로 관리하고 유지할 수 있도록 각각 store, reducer, action, dispatch로 나누어 상태를 관리한다.
Redux 설치
$ npm install redux react-redux
또는
$ yarn add redux react-redux
// counter.js
const initialState = 0
//action.type은 actions.js 에서 불러온 action객체의 프로퍼티
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case "INCREMENT":
return state + 1;
case "DECREMENT":
return state - 1;
default:
return state;
}
};
export default counterReducer;
// store.js
import { createStore } from "redux";
import counterReducer from "./counter";
//store를 생성하여 counter reducer를 인수로 할당
const store = createStore(counterReducer);
export default store;
// actions.js
export const INCREMENT = "INCREMENT";
export const DECREMENT = "DECREMENT";
export const increment = () => ({
type: INCREMENT,
});
export const decrement = () => ({
type: DECREMENT,
});
// Counter.jsx
import { useSelector, useDispatch } from "react-redux";
import { increment, decrement } from "./actions";
function Counter() {
const count = useSelector((state) => state.counter);
const dispatch = useDispatch();
return (
<div>
<h2>Count: {count}</h2>
<button onClick={() => dispatch(increment())}>+</button>
<button onClick={() => dispatch(decrement())}>-</button>
</div>
);
}
export default Counter;
// App.jsx
import { Provider } from "react-redux";
import store from "./store";
function App() {
return (
<Provider store={store}>
<div className="App">
<h1>Counter App</h1>
<Counter />
</div>
</Provider>
);
}
export default App;
useContext는 전체 앱의 한 부분이 전역 객체를 필요로 할 때 사용하고, Redux는 전체 앱이나 전체적인 공통 상태가 복합적으로 다루어져야 할 때 사용한다. 그다지 복잡하지 않고, 규모가 적은 부분의 전역 상태 관리라면 useContext를 사용해 번들 사이즈의 감소등을 노려 오버헤드를 방지하는 것이 좋다.
Redux팀에서 만든 Redux 유틸리티 모음 킷 같은 라이브러리다.
반복적으로 사용되는 필수 코드들을 이쁘게 모아놓고 편하게 쓸 수 있다.
단순한 스토어 설정: Redux Toolkit을 사용하여 저장소를 설정하고 기본적으로 몇 가지 유용한 미들웨어를 활성화하는 configureStore 기능이 포함되어 있다.
간결하고 예측 가능한 코드: Redux Toolkit은 action과 reducer 같은 일반적인 Redux 패턴을 단순화하는 일련의 API 기능을 제공한다. 이러한 기능은 코드를 줄이고 Redux 코드를 더 예측 가능하고 추론하기 쉽게 만드는 데 도움이 된다.
불변성 및 성능 최적화: Redux Toolkit은 내부적으로 Immer 라이브러리를 사용하여 변경 가능한 방식으로 상태를 수정할 수 있는 리듀서 함수 작성할 수 있다. 이렇게 하면 리듀서 작성이 더 쉬워지고 경우에 따라 성능이 향상될 수 있다.
개발 도구에 대한 기본 지원: Redux Toolkit에는 강력한 디버깅 및 시간 이동 기능을 제공하는 브라우저 확장인 Redux DevTools Extension이 있다.