티스토리 뷰
Redux는 JavaScript 어플리케이션의 상태를 관리할 수 있는 상태 관리 라이브러리이다.(React, Vue, Angular, vanilla JS 등 모두 사용할 수 있다.)
React로 상태 관리를 할 때 props drilling이나 상태끌어올리기를 사용하거나 컴포넌트 안에서 상태 변경 로직이 구현되어 코드가 복잡할 때가 있다. Redux는 상태 또한 컴포넌트처럼, React 컴포넌트로부터 분리하여 다룬다.
Redux에는 세 가지 원칙이 있다.
1. Single Source of Truth
동일한 데이터는 항상 같은 곳에서 가져와야 한다. Redux의 Store가 단 한개만 존재한다는 것과 관련있는 것이다.
2. State is read-only
React의 상태는 상태변경함수를 통해서만 변경할 수 있다. 이처럼 Redux도 상태는 읽기전용이며, 직접 변경할 수 없고, Action객체를 통해서만 변경 가능하다.
3. Changes are made with pure function
상태 변경은 순수함수로만 가능하다. 외부요인으로 인해 상태 값이 달라지는 일이 없도록 Reducer를 순수함수로 만들어야 한다.
Redux를 사용하기 위해서는 다음의 Store, Reducer, Action, Dispatch 네 가지의 개념을 알아야 한다.
Store
Redux에서 상태를 관리하는 오직 하나뿐인 상태 저장소이다. 여러 개의 상태를 써도 모두 하나의 Store에 저장하는 것이다.
createStore메서드에 다음으로 소개할 Reducer함수를 만든 후 전달인자로 전달하여 store을 생성한다. 그리고 react-redux에서 제공하는 Provider컴포넌트로 App을 감싸고, store props로 우리가 생성한 store을 전달해준다.
import { createStore } from "redux";
const store = createStore(reducer);
root.render(
<Provider store={store}>
<App />
</Provider>
);
이 단계에서 몇 가지 작업을 해주면 Chrome과 파이어폭스에서 Redux DevTools를 사용할 수 있다.
먼저 크롬 익스텐션을 설치하고
node package를 설치한 뒤
코드를 다음과 같이 작성하면
앱을 실행하면 Redux DevTools 익스텐션이 활성화되어 사용할 수 있다.
import { createStore } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
const store = createStore(reducer, composeWithDevTools());
root.render(
<Provider store={store}>
<App />
</Provider>
);
참고로 Redux Toolkit을 사용하면 이러한 추가 작업없이 configureStore 메서드로 자동으로 Redux DevTools를 사용할 수 있게 된다.
Redux Toolkit은 Redux에서 공식적으로 권장하는 방법으로, 본 포스팅에서 다루는 내용을 사용하면 코드가 길어져서 이를 간편화한 버전이다. RTK를 사용하는 방법은 다음 포스팅에서 확인할 수 있다.
Reducer
함수이다. state와 action을 전달인자로 받는다. action 객체의 type에 따라 상태를 변경시킨다. Reducer는 순수함수여야 한다. 외부 요인으로 인해 상태가 엉뚱한 값이 되면 안되기 때문이다.
const reducer = (state = 1, action) => {
switch(action.type){
case 'INCREASE':
return state + 1;
case 'DECREASE':
return state - 1;
case 'SET_NUMBER':
return action.payload;
default:
return state;
}
}
Action
상태에 대해 어떤 액션을 취할지 정의해놓은 객체이다.
// payload가 필요 없는 경우
{ type: 'INCREASE' }
// payload가 필요한 경우
{ type: 'SET_NUMBER', payload: 5 }
Reducer에서 action.type을 참고하여 상태갱신을 결정하므로, type 프로퍼티는 필수로 지정해주어야 한다. 대문자와 SnakeCase로 작성한다. 특정 값을 전달하고 싶으면 payload 프로퍼티로 전달해주고 Reducer에서 사용할 수 있다.
action은 말 그대로 객체이다보니 사실 payload 프로퍼티이름을 text나 id 등으로 지어도 동작한다. 하지만 보통 관습적으로 payload라고 한다.
Dispatch
Reducer와 Action을 모두 정의해주었다. 그런데 이들을 각각 생성만 했을 뿐, 상호작용하고 있지 않다. Dispatch를 이용해서 이들을 연결해준다. Dispatch는 Reducer에 Action을 전달해주는 함수이다. 그래서 Dispatch의 전달인자로 Action 객체가 들어간다. 아래에 설명할 connect() 또는 useDispatch 훅을 통해 생성한 dispatch를 다음과 같이 사용한다.
// Action 객체를 직접 작성하는 경우
dispatch( { type: 'INCREASE' } );
dispatch( { type: 'SET_NUMBER', payload: 5 } );
// 액션 생성자(Action Creator)를 사용하는 경우
const increase = () =>{
return {
type: 'INCREASE'
}
}
const setNumber = (num) =>{
return {
type: 'SET_NUMBER',
payload: num
}
}
dispatch( increase() );
dispatch( setNumber(5) );
React에서 dispatch를 하고 상태를 사용하기 위해서 다음의 두 가지 방법을 사용할 수 있다.
connect 상태감지 (비추천)
function App({ count, dispatch }) { // connect로 전달된 props인 상태 count와 dispatch함수
return ()
}
const mapStateToProps = (state) => ({ count: state }) // 객체를 반환
const mapDispatchToProps = (dispatch) => ({ dispatch }) // 객체를 반환
export default connect(mapStateToProps, mapDispatchToProps)(App)
react-redux의 connect() 함수로 상태를 가져와서 사용할 수도 있다. 그러나 Redux 공식문서는 useSelector 훅을 기본적으로 사용할 것을 권장한다.
useDispatch와 useSelector (추천)
Reducer와 Action을 연결해주는 Dispatch를 손쉽게 사용하기 위해 useDispatch라는 훅이 있고, 관리하고 있는 상태를 가져와서 사용할 수 있도록 useSelector이라는 훅이 있다.
const dispatch = useDispatch();
dispatch(increase());
// 또는 dispatch( { type: 'INCREASE' } )
function App(){
const tomato = useSelector(state => state);
return <h1>{`Count: ${tomato}`}</h1>
}
useSelector의 콜백함수의 전달인자(state)는 Store에 저장되어있는 모든 상태가 담긴다. 위 코드처럼 전달인자를 그대로 반환하면 모든 상태를 다 사용할 수 있다. 현재는 숫자를 증감시키는 상태 하나만 있기 때문에 지정한 변수 tomato를 사용하면 된다.
전체 코드
다음은 종합하여 Redux를 사용한 예이다. (setNumber은 없다.)
// index.js
import React from 'react';
import { createRoot } from 'react-dom/client';
import { Provider } from 'react-redux';
import { legacy_createStore as createStore } from 'redux';
import App from './App';
const rootElement = document.getElementById('root');
const root = createRoot(rootElement);
export const increase = () => {
return {
type: 'INCREASE',
};
};
export const decrease = () => {
return {
type: 'DECREASE',
};
};
const reducer = (state = 1, action) => {
switch (action.type) {
case 'INCREASE':
return state + 1;
case 'DECREASE':
return state - 1;
default:
return state;
}
};
const store = createStore(reducer);
root.render(
<Provider store={store}>
<App />
</Provider>
);
// App.js
import React from 'react';
import './style.css';
import { useSelector, useDispatch } from 'react-redux';
import { increase, decrease } from './index.js';
export default function App() {
const dispatch = useDispatch();
const state = useSelector((state) => state);
console.log(state);
const plusNum = () => {
dispatch(increase());
};
const minusNum = () => {
dispatch(decrease());
};
return (
<div className="container">
<h1>{`Count: ${state}`}</h1>
<div>
<button className="plusBtn" onClick={plusNum}>
+
</button>
<button className="minusBtn" onClick={minusNum}>
-
</button>
</div>
</div>
);
}
회고
MyLikes프로젝트에서 상태관리 라이브러리로 Recoil을 사용했었다. Recoil은 React의 상태관리 라이브러리인데, useState와 사용법이 굉장히 비슷하고 간단하다. Redux와 마찬가지로 전역상태를 관리하는데도 reducer, action, dispatch, selector를 사용하기 위한 코드를 작성하고 그들을 상호작용 시키는 등의 긴 코드가 필요 없다.
그런데 npm trends에 따르면 Redux 사용량이 현저하게 많다. 대형 프로젝트일수록 Redux의 상태 관리 안정성과 devtool을 제공하는 등의 이점이 있다고 한다. Redux를 조금 더 연습해볼 필요가 있겠다.
아래의 포스팅은 바닐라 자바스크립트에서 Redux를 사용하는 방법이다.
'개발 > JS, TS, React' 카테고리의 다른 글
Redux Toolkit 으로 Redux 더 간편하게 사용하기 (0) | 2023.04.30 |
---|---|
Redux vanillaJS에서 사용하기 (0) | 2023.04.25 |
React State와 Props / Side effect / 순수함수 / Hooks (0) | 2023.04.06 |
Node.js서버와 Express서버 (0) | 2023.04.05 |
SPA와 MPA / SSR과 CSR / AJAX (0) | 2023.03.23 |
- Total
- Today
- Yesterday
- 티스토리검색
- 을지로맛집
- 깃허브 데스크탑 로그아웃
- 태릉맛집
- 춘천닭갈비
- 롯데월드 매직패스 프리미엄
- 춘천맛집
- 신불당 술집
- 회고
- 티스토리
- 공릉 이자카야
- 홍천 삼겹살
- 맥 깃허브 데스크탑
- 공릉 꼬치
- 공릉 술집
- 태릉삼겹살
- 태릉 이자카야
- sitemap
- 공릉 밀크티
- 롯데월드 키오스크
- 롯데월드 보조배터리
- 태릉 꼬치
- 이수 맛집
- 태릉 술집
- 구글서치콘솔
- Til
- 공릉 카페
- 공릉 맛집
- 공릉맛집
- solo project
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |