[React] 전역 상태관리 #redux
- 프론트/리액트
- 2024. 9. 4.
💡 전역 상태관리 - Redux
📍 useState의 불편함
컴포넌트의 state를 공유할 때 Props를 통해 부모 컴포넌트에서 자식 컴포넌트로 전달하게 된다. 아니 그래야만 한다.
1. 반드시 부모-자식 관계가 되어야 공유가 가능하다.
2. prop drilling이 발생하고 불필요한 코드가 생긴다. (props를 받아서 전달만 하는 컴포넌트는 불필요하다.)
3. 자식 컴포넌트에서 부모 컴포넌트로 값을 보낼 수 없다.
📍 리덕스를 사용하면
- state를 공유할 때 부모-자식 관계가 아니어도 된다
- 중간에 의미 없는 props의 전달 과정이 없어도 된다.
- 자식 컴포넌트에서 만든 state를 부모 컴포넌트에서 사용이 가능하다.
💡 Global state & Local State
Global state는 redux를 의미, Local state는 useState를 의미한다.
📍 Global state (전역 상태)
Global state는 컴포넌트에서 생성되지 않는다. 중앙에서 state들을 생성하고 관리한다. 즉, 특정 컴포넌트에서 State를 관리하지 않고 중앙에서 관리하는 형태이기 때문에 어떤 컴포넌트에서든지 State를 다이렉트로 접근할 수 있다.
📍 Redux vs Context API
(1) 성능 최적화
`Context API` 는 Provider하위의 모든 컴포넌트를 리렌더링시킬 수 있다. 상태가 변경될 때 모든 컴포넌트가 불필요하게 업데이트하는 것을 막기 위해 최적화 과정이 추가적으로 필요하다(memo, useCallback 등). 반면, `Redux ` 는 상태 변경 시 관련 컴포넌트만 선택적으로 업데이트가 가능하다.
(2) 중앙화와 일관성
`Redux`는 상태를 하나의 저장소에 저장한다. 이로 인해 상태 관련 로직이 중앙에서 관리되어 일관성있게 된다. 따라서 디버깅과 테스팅이 용이하다.
정리
중앙 state 관리소를 사용할 수 있게 도와주는 패키지(라이브러리)이다. 쓰면 편하고 좋다.
💡 Redux 세팅
📍 Redux 설치
yarn add redux react-redux # redux, react-redux 동시 설치
📍 Redux 폴더 구조
─src
├─assets
├─components
├─context
└─redux
├─config/configStore.js
└─modules
src하위에 `redux/config`, `redux/modules` 폴더를 생성한다. 그리고 config폴더 하위에 `configStore.js` 파일을 생성한다.
config
- redux설정과 관련된 파일을 놓을 폴더
modules
- 우리가 만들 state들의 그룹을 묶어놓을 폴더
configStore.js
- store와 관련된 설정할 파일
📍 Redux 기본 설정(?)
configStore.js
import { combineReducers, createStore } from "redux";
// 1) rootReducer를 만든다.
const rootReducer = combineReducers({});
// 2) store 조합
const store = createStore(rootReducer);
// 3) 만든 store export
export default store;
3가지의 작업이 필요하다.
- rootReducer를 생성하고, 기본값을 설정한다.
- createStore에는 rootReducer를 전달한다.
- 반환 받은 store를 export 해준다.
main.jsx
import { Provider } from 'react-redux'
import store from './redux/config/configStore.js'
createRoot(document.getElementById('root')).render(
<Provider store={store}>
<App />
</Provider>
)
- `react-redux`의 Provide를 import 한다.
- 이전에 생성한 store를 import한다.
- `<Provider>`로 `<App>` 을 감싸주고 store를 넣어준다.
여기까지 진행했으면 App 하위의 모든 컴포넌트는 store를 주입한 Provider의 영향권 안에 존재하게 되고 사용할 수 있다.
📍 modules
우리 실질적으로 만들 모듈 코드를 작성하면 된다. 아래 counter 모듈을 생성하는 예시를 보자.
modules/counter.js
const initialState = {
number: 0,
}
// 리듀서(함수)
const counter = (state = initialState, action) => {
switch (action.type) {
default:
return state;
}
}
export default counter;
- `initialState`: 초기 값을 지정해놓은 변수
- 리듀서(`counter`): 2개의 매개변수를 전달 받는다. (state, action)
- 리듀서에 대한 부연설명을 해보자면 리듀서는 함수, 변화를 일으키는 함수이다. 변화의 종류는 action 객체에 따라 처리하도록 구현하면 된다.
- action에 따른 동작을 switch문으로 작성한다. (if문도 상관은 없지만 switch를 많이 사용한다고 한다.)
다시 돌아가서 우리가 생성한 리듀서를 rootReducer에 전달하면 된다.
configStore.js
import { combineReducers, createStore } from "redux";
import counter from "../modules/counter";
const rootReducer = combineReducers({
counter,
});
- 리듀서가 존재하지 않을때는 빈 객체를 주입했지만, 리듀서를 작성하고 나서 `combineReducer`에 주입시켜주면 된다. (새로운 리듀서를 생성할 때 마다 주입하면 된다.)
일단 redux를 사용하기 전 기본 세팅은 끝났다. 앞으로는 이런 구조(혹은 편한대로 알아서)로 redux를 사용하면 된다.
💡 Redux 사용하기
rootReducer에 저장된 리듀서들을 사용할 것이다. `useSelector` 라는 훅을 이용하여 꺼내오면 된다.
App.jsx
import { useSelector } from 'react-redux'
function App() {
// const counterReducer = useSelector((state) => state.counter);
const counterReducer = useSelector((state) => state);
console.log('counterReducer', counterReducer.counter);
return (
<>
</>
)
}
- `useSelector`: 콜백함수를 매개변수로 받는다. 해당 콜백함수는 state(rootReducer)를 받게 된다.
특정 리듀서 반환
위에서 작성했던 counter 리듀서를 받고 싶다면 두 가지 방법이 있다.
- `state.counter`를 사용하여 처음부터 counter 리듀서를 반환 받을 수 있다. (이 방법을 지향하자)
- 사용할 때 마다 `counterReducer.counter` 로 사용할 수 있다.
'프론트 > 리액트' 카테고리의 다른 글
[React] use어쩌구들 깔끔하게 사용하기 #커스텀 훅 (0) | 2024.09.12 |
---|---|
[React] styled-components {css} #조건에 따른 CSS 처리 (0) | 2024.09.10 |
[React] props 계속 전달하기 귀찮을 때 #useContext (0) | 2024.09.03 |
[React] useRef의 몇 가지 특징들 #useState vs useRef (0) | 2024.09.02 |
[React+JS] 헷갈리는 `중첩 객체` 구조분해할당 (0) | 2024.08.30 |