- Today
- Total
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- Flutter
- Firebase
- 주식차트
- graphql
- 3주차
- javascript
- 리액트
- chart
- error
- 비전공자
- 코인
- nextjs
- websocket
- 에러
- API
- 차트구현
- 코인차트
- typeorm
- react
- rtk
- 차트만들기
- 채팅
- apollo
- typescript
- Coin
- 항해99
- Redux
- 차트
- 주식
- nestjs
Act99 기술블로그
[React] Context API 와 상태관리 툴의 차이 본문
1. 전역 상태 관리는 언제할 것인가?
React에서는 useState 훅을 이용해 지역 상태 관리를 할 수 있다. 컴포넌트 안에서, 혹은 props로 전달할 때 만 자식 컴포넌트에서 사용할 수 있다. 또한 모든 상태 관리를 props drilling 으로 하나씩 내려주어 전역의 모든 상태관리를 진행할 수 있지만 props drilling 지옥에 빠질 수 있다. 이 때 전역 상태 관리 도구 Context API 와 Redux 등과 같은 도구들을 사용할 수 있다.
2. Context API 특징
Context API 는 React 자체에 내장 기능이기 때문에 React 에서만 사용할 수 있다. 이는 Entry 파일에서 구성한 Provider 을 내려주는 형식이며, 사용하고자 하는 컴포넌트에서 작성한 Dispatch 와 State를 꺼내서 사용한다. Reducer를 여러 개 만들면 Provider 에서 여러 단계로 만들어 사용할 수 있다.
3. Context
const CountContext= createContext({
counter: 0,
plusCount: () => {},
});
function CountProvider() {
const [counter, setCounter] = useState(0);
const plusCount = () => {
setCounter(count+1)
}
// Create an object containing both the value and the setter
const contextValue = {counter, setCounter};
return (
<CountContext.Provider value={contextValue}>
<SomeChildComponent />
</CountContext.Provider>
)
}
export {CountContext, CountProvider}
React 에서 Context를 생성하기 위해서는 createContext 를 사용해야 한다. 또한, Context 도 하나의 React 컴포넌트이므로, 컴포넌트 안에서 변경 가능한 데이터를 다루기 위한 State 를 사용해야 한다.
이렇게 추가한 createContext 를 사용하여 Context 를 생성한다. 이 때, 전역 변수로 사용될 데이터의 초기값을 설정해야 한다.
Context도 하나의 React 컴포넌트이므로, 기본적으로 컴포넌트의 형태를 가지고 있다. 이 때 화면에 표시될 내용에 Context의 Provider를 감싸서 제공한다.
Context는 하나의 컴포넌트이기 때문에, 내부적으로 변경 가능한 데이터를 사용하려면 useState 를 사용하여 State 를 사용해야 한다. 이 역시 Context 의 Provider 에 value 로 제공해야 한다. 이제 만든 컴포넌트를 export 시켜준다. 이를 이용해 공통 부모 컴포넌트에 제공할 것이다.
- Provider
앞에서 만든 Context 를 사용하기 위해, 공통 부모 컴포넌트인 App 컴포넌트에 Context의 Provider를 제공한다.
import { CountProvider } from './Contexts/Count';
import { CountLabel } from './Components/CountLabel';
import { PlusButton } from './Components/PlusButton';
function App() {
return (
<CountProvider>
<CountLabel />
<PlusButton />
</CountProvider>
);
}
export default App;
이렇게 감싸서 Context 내부에 생성한 전역 데이터를 자유롭게 접근할 수 있게 된다.
이 때 Context Provider 을 소비하는 자식 컴포넌트들을 Consumer 라고 한다.
- Consumer
import { useContext } from 'react';
import { CountContext } from '../../Contexts/Count';
export const CountLabel = () => {
const { count } = useContext(CountContext);
return <div>{count}</div>;
};
위에 코드는 CountLabel 이라는 자식 컴포넌트이다. 여기서 보면, 전역 상태를 받기 위해 useContext라는 훅을 이용한다.
import { useContext } from 'react';
import { CountContext } from '../../Contexts/Count';
export const PlusButton = () => {
const { plusCount } = useContext(CountContext);
return <button onClick={plusCount}>+ 1</button>;
};
다음 코드는 PlusButton 코드이다. 이는 전역변수를 업데이트시켜줄 함수를 context로 받아왔다.
4. Context API의 특징
- react에서만 사용할 수 있다.
- Entry 파일(root)에서 구성한 Provider를 내려주는 형식이다.
- 사용하고자 하는 컴포넌트에서 작성한 Dispatch와 State를 꺼내 사용한다.
- reducer를 여러 개 만들면 Provider에서 여러 단계로 만들어 사용할 수 있다.
즉, Context API는 Redux 생태계와 유사하다.
- 사용방식
Context 생성
createContext 를 통해 상태를 저장한다.
initState 에 초기 상태값을 객체 형태로 넣는다.
import { createContext } from "react";
const GameBoardStateContext = createContext(initState});
Action 생성
액션을 지정하여 Reducer에서 서로 다른 타입일 때 다른 로직을 진행시킬 수 있다.
export type Action = { type: "UPDATE"; payload };
Reducer 생성
액션에 따라 State를 변경시키는 로직을 작성할 수 있다.
import { Action } from "@/actions/gameBoardAction";
import { GameBoardState } from "@/contexts/gameBoardContext";
const gameBoardReducer = (state: GameBoardState, action: Action): GameBoardState => {
switch (action.type) {
case "UPDATE":
return {
...action.payload,
// 변경되는 payload 로직 작성
};
default:
throw new Error("Unhandled action");
}
};
Provider 생성
위에서 작성한 GameBoardReducerContext를 Provider로 하여 State 값을 Value로 넣습니다.
React의 useReducer에서 상태값과 dispatch를 value로 사용할 수 있습니다. (해당 예제에서는 state만 Provider로 사용하였지만 중첩된 Provider로 여러 상태를 넣을 수 있습니다.)
export const ContextProvider = ({ children }: { children: React.ReactNode }) => {
const [gameBoard, gameBoardDispatch] = useReducer(gameBoardReducer, initState);
return (
<GameBoardStateContext.Provider value={gameBoard}>{children}</GameBoardStateContext.Provider>
);
};
Entry 파일에 적용
위에서 생성한 Provider를 Entry 파일인 App의 root에 감싸 주면 Provider 밑에 작성하는 하위 컴포넌트들에서 상태를 꺼내 사용할 수 있습니다.
const App = () => {
return (
<>
<ContextProvider>// 하위 컴포넌트들</ContextProvider>
</>
);
};
개별 컴포넌트에서 사용하기
하위 컴포넌트에서 상태를 사용할 때는 useContext를 통해 state 값을 꺼내 사용 가능합니다.
import { useContext } from "react";
const state = useContext(GameBoardStateContext);
5. Redux 특징
- React, Vue와 같은 프레임워크 환경에서 사용할 수 있습니다.
- 상태를 저장하는 Store를 따로 가지고 있습니다.
- thunk, saga와 같은 미들웨어를 추가적으로 사용하여 구성할 수 있습니다.
- Redux devtool extension을 사용하면 상태에 대한 디버깅이 가능합니다.
- 전역 상태 관리 외에도 로컬스토리지 상태저장, 버그리포트 첨부 기능 등의 기능들을 사용할 수 있습니다.
Action 생성
상태가 변하는 것에 대한 액션을 함수로 작성합니다.
export const focusChange = (payload) => {
return {
type: "focusChange",
payload,
};
};
Reducer 생성
초기 상태를 설정하고, Action을 type으로 구별하여 상태를 업데이트하는 로직을 Reducer로 작성합니다.
const initialState = {
startDate: null,
endDate: null,
focusedInput: START_DATE,
};
const datePickerReducer = (state = initialState, action) => {
switch (action.type) {
case "focusChange":
return { ...state, focusedInput: action.payload };
default:
return state;
}
};
Store 생성
- redux의 createStore를 통해 store의 인자에 생성한 Reducer를 넣어 생성합니다.
- 만약 Reducer가 여러 개라면 combineReducer를 통해 Reducer를 하나로 합치는 과정을 추가로 진행해야 합니다.
import { createStore } from "redux";
const store = createStore(datePickerReducer);
React-redux 의 Provider 로 root에 store 등록
Entry 파일에서 Provider 에 store 를 등록합니다.
import { Provider } from "react-redux";
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);
UseSelector 로 개별 컴포넌트에 사용
- 상태 값을 사용하고자 하는 컴포넌트에서 useSelector로 상태 객체를 꺼내서 사용할 수 있습니다.
- 아래는 state를 distructuring한 모습입니다.
import { useSelector } from "react-redux";
const {
datePickerReducer: { startDate, endDate },
} = useSelector((state) => state);
요약 및 결론
결론적으로 보면 Context API와 Redux 사용법은 매우 유사하다.
단순 전역 상태 관리만 있어도 된다면 Context API 를 사용하며, 디버깅이나 로깅 등의 상태 관리 외의 기능이나 미들웨어가 필요하다면 Redux 를 사용하는게 좋다.
권장사항
해결하려는 문제에 가장 적합한 도구를 선택해라
- 단순 prop-drilling 을 피하는 것이 목적이라면 Context 를 사용해라
- 적당히 복잡한 컴포넌트가 있거나 외부 라이브러리를 사용하고 싶지 않다면 Context + useReducer 를 사용해라
- 특정 구성 요소만 re-render 시키거나, 사이드이펙트를 줄이기 위해 더 강력한 기능이 필요하다면 Redux + React-Redux 를 사용해라
자료 출처 : https://www.robinwieruch.de/redux-vs-usereducer/
https://blog.isquaredsoftware.com/2021/01/context-redux-differences/
https://dev-yakuza.posstree.com/ko/react/context-api/#완료
https://egg-programmer.tistory.com/281