React는 상태관리 대안으로 Redux를 사용하는 것을 권장한다. react-redux 모듈을 통해 React에서 특정 hook을 제공함으로 Redux를 보다 용이하게 사용할 수 있도록 해준다. 전 포스팅에서 설명한대로 Redux를 통한 상태관리로 얻을 수 있는 이점은 다음과 같다.
useState
를 사용하여 state
를 관리하기 보단 외부 모듈을 통해 전역적인 state
를 선언하고 관리할 수 있다.props > props > props
로 하위 컴포넌트에 전달하는 불필요한 구조를 줄여준다.👽 이번 포스팅에선 redux-toolkit, Thunk, combineReducers등 Redux의 확장 모듈에 대해서는 다루지 않겠다. 먼저 원초적인 Redux 구조에 대해 알아볼 필요가 있다고 생각하기 때문이다. (졸리기도 하고)
자 초초간단하게 시작해보자
Redux를 적용하기 위해 redux
와 react-redux
모듈을 설치해준다.
npm install redux react-redux
모든 전역적으로 사용되는 state
들이 보관되어 있는 공간인 Store를 만들고 Redux를 사용할Component를 감싸주면 된다. 지금 예제에서는 전역변수 공간의 느낌으로 index.js
에 Store를 설정해주는 것으로 해보겠다. 보통 store
라는 서브 패키지 내부에 선언하여 export
해서 사용한다.
store.js
메인은 초기값 (initialState
), 변경함수 (reducer
), 리듀서를 포함한 Store (store
)를 구성하고 export
하는 것이다.
import { createStore } from 'redux';
// State 초기 값
const initialState = {
name : 'carrots',
age : 25
};
// reducer 선언
// 얘는 좀 있다가 설정해주겠다. 보통은 많은 reducer를 선언하고 combineReducers로
// 1개의 reducer로 결합하여 store에 설정해준다.
const reducer = (state = initialState, action) => {
return state;
}
// store 생성
// 기본적으로 redux는 1개의 reducer를 주입해야한다.
const store = createStore(reducer);
export default store;
index.js
실제로 Store를 설정할때는 다음과 같이하면 된다. 이건 이해의 영역이 아닌 그냥 이렇게 쓰라고 규칙으로 만들어 놓은거니까 잔머리는 굴리지 마라.
// 쓸데없는건 지웠다.
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux'; // Provider
import App from './App';
import store from './store/store'; // 위에서 만든 store
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode> <!-- StrictMode로 해놓으면 렌더링이 2번되는데 걍 냅두겠음 -->
<!-- 다음과 같은 코드를 작성하면 App 하위 컴포넌트는 모드 store를 참조할 수 있음. 물론 내부에서 코드를 써야하지만 -->
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
App.js (사용 예시)
import { useSelector } from 'react-redux'; // useSelector
function App() {
// useSelector를 통해 redux에 저장된 state를 가져와 할당한다.
const userInfo = useSelector((state) => state);
return (
<div className="App">
<p>안녕? 나는 {userInfo.name}! 내 나이는 {userInfo.age}</p>
</div>
);
}
export default App;
이처럼 Store를 선언하고 해당 영역을 사용할 컴포넌트를 감싸주면 하위 컴포넌트에서 useSelector
hook을 사용해 어디서든 접근이 가능하다.
Reducer란 Action 객체를 받아, Store에 설정된 state
변수들을 디스패치하는 순수함수다. 예시로 현재 Store에서 변경함수를 작성해보면 다음과 같다.
store.js
import { createStore } from 'redux';
const initialState = {
name : 'carrots',
age : 25
};
// reducer 선언
// 내부에 if, switch 문등을 활용하여 케이스별 state 변경작업을 해주면 된다.
const reducer = (state = initialState, action) => {
// action은 보통
// {
// type : string,
// payload : {}
// }
// 형태로 구성된다.
if (action.type == 'EDIT_NAME') {
return { ...state, name : action.payload.name };
} else if (action.type == 'EDIT_AGE') {
return { ...state, age : action.payload.age };
} else {
return state;
}
}
const store = createStore(reducer);
export default store;
이렇게 선언해 놓으면 간단한 reducer가 설정된다. 사용할때는 다음과 같이 사용하면 된다.
App.js (사용 예시)
import { useSelector, useDispatch } from 'react-redux'; // useSelector, useDispatch
function App() {
const userInfo = useSelector((state) => state);
// useDispatch hook을 사용하여 store 내 리듀서가 주입된다고 생각하면 쉽다.
const dispatch = useDispatch();
return (
<div className="App">
<p>안녕? 나는 {userInfo.name}! 내 나이는 {userInfo.age}</p>
<!-- 주입받은 리듀서로 redux내 state 변경 -->
<button onClick={() => {
dispatch({
type : 'EDIT_NAME',
payload : { name : 'durrian' }
})
}}>개명</button>
</div>
);
}
export default App;
보면 어느정도 이해가 될 것이다. Store 내부 리듀서를 사용하여 state
를 변경하고 사용하는 것이다. 이게 전부다. 정말 간단하게 설명했지만 기본적인 구조는 다음과 같다는 것을 이해하면 성공이다.
오늘은 Redux의 적용 기초를 알아봣다. 사실상 지금의 Redux에서는 createStore 기능이 디플리케이트됐다. 현재는 redux-toolkit을 사용하여 구현하라고 권장한다. 다음 포스팅에서는 redux-toolkit과 미들웨어, combineReducers에 대해 정리해보겠다.
오늘 저녁도 살시찰이다. 🥕