Redux
- https://redux-toolkit.js.org/
- Redux ๊ฐ๋ฐ์๋๊ตฌ
- cf. Nomard Coder Redux ๋ฌด๋ฃ๊ฐ์
- cf. ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ฏธํ: Redux ๋๋ MobX ๋ฅผ ํตํ ์ํ ๊ด๋ฆฌ
1. ๊ฐ๋
1.1 ์ก์ (Action)
- ์ํ์ ์ด๋ ํ ๋ณํ๊ฐ ํ์ํ๊ฒ ๋ ๋,
์ก์ ์ด๋ ๊ฒ์ ๋ฐ์์ํต๋๋ค. - ์ด๋, ํ๋์ ๊ฐ์ฒด๋ก ํํ๋๋๋ฐ์, ์ก์ ๊ฐ์ฒด๋ ๋ค์๊ณผ ๊ฐ์ ํ์์ผ๋ก ์ด๋ค์ ธ์์ต๋๋ค.
1{2type: 'TOGGLE_VALUE'3}
์ก์
๊ฐ์ฒด๋ type ํ๋๋ฅผ ํ์์ ์ผ๋ก ๊ฐ์ง๊ณ ์์ด์ผ ํ๊ณ , ๊ทธ ์ธ์ ๊ฐ๋ค์ ๊ฐ๋ฐ์ ๋ง์๋๋ก ๋ฃ์ด์ค ์ ์์ต๋๋ค. ์์:
1{2type: "ADD_TODO",3data: {4id: 0,5text: "๋ฆฌ๋์ค ๋ฐฐ์ฐ๊ธฐ"6}7}8{9type: "CHANGE_INPUT",10text: "์ใด"11}
1.2 ์ก์ ์์ฑํจ์ (Action Creator)
- ์ก์ ์ ๋ง๋๋ ํจ์
- ๋จ์ํ ํ๋ผ๋ฏธํฐ๋ฅผ ๋ฐ์์์ ์ก์ ๊ฐ์ฒด ํํ๋ก ๋ง๋ค์ด์ค
1function addTodo(data) {2return {3type: 'ADD_TODO',4data,5}6}78// ํ์ดํ ํจ์๋ก๋ ๋ง๋ค ์ ์์ต๋๋ค.9const changeInput = text => ({10type: 'CHANGE_INPUT',11text,12})
1.3 ๋ฆฌ๋์ (Reducer)
- ๋ณํ๋ฅผ ์ผ์ผํค๋ ํจ์์ ๋๋ค.
- ๋ฆฌ๋์๋ ๋๊ฐ์ง์ ํ๋ผ๋ฏธํฐ๋ฅผ ๋ฐ์์ต๋๋ค.
- ๋ฆฌ๋์๋, ํ์ฌ์ ์ํ์, ์ ๋ฌ ๋ฐ์ ์ก์ ์ ์ฐธ๊ณ ํ์ฌ ์๋ก์ด ์ํ๋ฅผ ๋ง๋ค์ด์ ๋ฐํํฉ๋๋ค.
1function reducer(state, action) {2// ์ํ ์ ๋ฐ์ดํธ ๋ก์ง3return alteredState4}
์์ธํ๊ฑด, ์ถํ ์ง์ ๊ตฌํํ๋ฉด์ ์์๋ณด๊ฒ ์ต๋๋ค.
๐ก Reduce
- to change something into a simpler or more general form
- ๋จ์ํ๊ฒ ์ค์ด๋ค๋ผ๋ ์๋ฏธ๋ณด๋ค ๋ณ๊ฒฝ์ด๋ผ๋ ์๋ฏธ์ ๊ฐ๊น์
- ์ํ์์ ์ด๋ค ๋ณต์กํ ์ํ๋ฌธ์ ๋ฅผ ๊ฐ๋จํ๊ฒ ๋ง๋ค์ด ํธ๋ ๋ฐฉ๋ฒ์ reduction์ด๋ผ๊ณ ํจ
- โ๊ณ ์ณ๋๊ฐ๋คโ (๊ฐ๋จํ๊ฒ๋ง๋ค๊ธฐ์ํด์, ํน์ ํน์ ๊ท์น์ ์ ์ฉํ๊ธฐ์ํด์)
- ์ฃผ์ด์ง ์ํ๋ฅผ ๊ณ ์ณ๋๊ฐ๋ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์์ ์์ฃผ๋ณด์ด๋ reduce()ํจ์
- ๋ฆฌ๋์ค์์์ reduce()๋ ํ์ฌ์ํ(previousState)๋ฅผ ์๋ก์ด์ํ(newState)๋ก ๋ณ๊ฒฝํ ๋ ์ฐ๋ ํจ์
- ๋ฆฌ๋์๊ฐ reduce()ํจ์์์ ์ฌ์ฉํ๋ ์ฝ๋ฐฑํจ์์ด๊ธฐ๋๋ฌธ์ ๋ฆฌ๋์๋ผ๊ณ ๋ถ๋ฆผ
๋ฆฌ์กํธ(React) ์ํ(State) ์์ฑ์(Producer)
- ์ก์ (Action)์ด ๋ ๋ผ์ค๋ฉด ๋ฆฌ๋์(Reducer)๊ฐ ์คํ ์ด(Store)์ ์ํ(State)๋ฅผ ๋ณ๊ฒฝ์ํค๋ ๋ฐฉ์์ผ๋ก ๋์
1.4 ์คํ ์ด (Store)
- state๋ค์ ๋ณด๊ดํ๋ ํ์ผ
- ๋ฆฌ๋์ค์์๋ ํ ์ ํ๋ฆฌ์ผ์ด์ ๋น ํ๋์ ์คํ ์ด๋ฅผ ๋ง๋ญ๋๋ค.
- ์คํ ์ด ์์๋, ํ์ฌ์ ์ฑ ์ํ์, ๋ฆฌ๋์๊ฐ ๋ค์ด๊ฐ์๊ณ , ์ถ๊ฐ์ ์ผ๋ก ๋ช๊ฐ์ง ๋ด์ฅ ํจ์๋ค์ด ์์ต๋๋ค.
1.5 ๋์คํจ์น (dispatch)
- ์คํ ์ด์ ๋ด์ฅํจ์ ์ค ํ๋
- ๋์คํจ์น๋, ์ก์ ์ ๋ฐ์ ์ํค๋ ๊ฒ์ด๋ผ๊ณ ์ดํดํ์๋ฉด ๋ฉ๋๋ค.
- dispatch ๋ผ๋ ํจ์์๋ ์ก์
์ ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌํฉ๋๋ค.. dispatch(action) ์ด๋ฐ์์ผ๋ก ๋ง์ด์ฃ .
- ๊ทธ๋ ๊ฒ ํธ์ถ์ ํ๋ฉด, ์คํ ์ด๋ ๋ฆฌ๋์ ํจ์๋ฅผ ์คํ์์ผ์
- ํด๋น ์ก์ ์ ์ฒ๋ฆฌํ๋ ๋ก์ง์ด ์๋ค๋ฉด ์ก์ ์ ์ฐธ๊ณ ํ์ฌ ์๋ก์ด ์ํ๋ฅผ ๋ง๋ค์ด์ค๋๋ค.
1.6 ๊ตฌ๋ (subscribe)
- ๊ตฌ๋ ๋ํ ์คํ ์ด์ ๋ด์ฅํจ์ ์ค ํ๋
- subscribe ํจ์๋, ํจ์ ํํ์ ๊ฐ์ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์์ต๋๋ค.
- subscribe ํจ์์ ํน์ ํจ์๋ฅผ ์ ๋ฌํด์ฃผ๋ฉด, ์ก์ ์ด ๋์คํจ์น ๋์์ ๋ ๋ง๋ค ์ ๋ฌํด์ค ํจ์๊ฐ ํธ์ถ๋ฉ๋๋ค.
2. ๋ฆฌ๋์ค without ๋ฆฌ์กํธ
- ๋ฆฌ๋์ค๋ ๋ฆฌ์กํธ์ ์ข ์๋์ง ์์ต๋๋ค. ๋ฆฌ์กํธ์์ ์ฌ์ฉํ๋ ค๊ณ ๋ง๋ ๊ฑฐ๊ธด ํ์ง๋ง,
- ์ค์ ๋ก ๋ค๋ฅธ UI ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํ๋ ์์ํฌ์ ํจ๊ป ์ฌ์ฉ ๋ ์๋ ์์ต๋๋ค (์: angular-redux, ember-reduxโฆ)
- ๋ฌผ๋ก , ๋ฐ๋๋ผ ์๋ฐ์คํฌ๋ฆฝํธ์๋ ํจ๊ป ์ฌ์ฉํ ์๋ ์์ต๋๋ค.
๋ค์ ๋งํฌ๋ฅผ ํด๋ฆญํด์ฃผ์ธ์:
2.1 DOM ๋ ํผ๋ฐ์ค ๊ฐ์ ธ์ค๊ธฐ
1// index.js2import { createStore } from 'redux'34const lightDiv = document.getElementsByClassName('light')[0]5const switchButton = document.getElementById('switch-btn')67const counterHeadings = document.getElementsByTagName('h1')[0]8const plusButton = document.getElementById('plus-btn')9const minusButton = document.getElementById('minus-btn')
- ๋ฐ๋ก UI ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ง ์๊ธฐ ๋๋ฌธ์ DOM ์ ์ง์ ๊ฑด๋ค์
- DOM API ๋ค์ ์ฌ์ฉํ์ฌ HTML ์์ ๋ํ๋๊ณ ์๋ ๊ฐ ์์๋ค์ ๋ํ ๋ ํผ๋ฐ์ค ๋ง๋ฌ
2.2 ์ก์ ํ์ ์ ์
1// index.js2// ์๋ต34// **** ์ก์ ํ์ ์ ์5const TOGGLE_SWITCH = 'TOGGLE_SWITCH'6const INCREMENT = 'INCREMENT'7const DECREMENT = 'DECREMENT'
- ํ๋ก์ ํธ์์ ์ํ์ ๋ณํ๋ฅผ ์ผ์ผํค๋๊ฒ์ ํ๋์ ์ก์ ์ผ๋ก ๋ณด๊ณ , ๊ทธ ์ก์ ์ ๋ํ ์ด๋ฆ์ ์ ํด์ฃผ๋ ๊ณผ์
- ์ด๋ฆ์ ๋ฌธ์์ด ํํ๋ก, ์ฃผ๋ก ๋๋ฌธ์๋ก ์์ฑํ๋ฉฐ ์ก์ ์ด๋ฆ์ ๊ณ ์ ํด์ผ ํ๊ณ , ์ค๋ณต๋๋ฉด ์๋จ
2.3 ์ก์ ์์ฑ ํจ์ ์ ์
1// index.js2// ์๋ต34// **** ์ก์ ์์ฑํจ์ ์ ์5// ์ก์ ์์ฑ ํจ์ : ์ก์ ๊ฐ์ฒด๋ฅผ ๋ง๋๋ ํจ์6// ์ก์ ๊ฐ์ฒด๋ type ๊ฐ์ ํ์7// ๋๋จธ์ง ์ก์ ์์ ์ฐธ๊ณ ํ๊ณ ์ถ์ ๊ฐ๋ค์ ๋ง์๋๋ก8const toggleSwitch = () => ({ type: TOGGLE_SWITCH })9const increment = diff => ({ type: INCREMENT, diff })10const decrement = () => ({ type: DECREMENT })
- ์ก์ ๊ฐ์ฒด๋ฅผ ๋ง๋๋ ํจ์๋ฅผ, ์ก์ ์์ฑ ํจ์๋ผ๊ณ ๋ถ๋ฆ
- ์ก์ ๊ฐ์ฒด๋ type ๊ฐ์ ํ์๋ก ๋ค๊ณ ์์ด์ผ ํ๋ฉฐ, ๋๋จธ์ง ์ก์ ์์ ์ฐธ๊ณ ํ๊ณ ์ถ์ ๊ฐ๋ค์ ๊ฐ๋ฐ์ ๋ง์๋๋ก
2.4 ์ด๊ธฐ๊ฐ ์ค์
1// index.js2// ์๋ต34// **** ์ด๊น๊ฐ ์ค์ 5const initialState = {6light: false,7counter: 0,8}
2.5 ๋ฆฌ๋์ ํจ์ ์ ์
1// index.js2// ์๋ต34// **** ๋ฆฌ๋์ ํจ์ ์ ์5// ๋ฆฌ๋์ : ๋ณํ๋ฅผ ์ผ์ผํค๋ ํจ์6function reducer(state = initialState, action) {7switch (action.type) {8case TOGGLE_SWITCH:9return {10...state, // ๊ธฐ์กด์ ๊ฐ์ ๊ทธ๋๋ก ๋๋ฉด์ ์๋ก์ด ๊ฐ์ ๋ฎ์ด์ฐ๋ ๋ฐฉ์(์คํ๋ ๋ ์ฐ์ฐ์)11light: !state.light, // light ๊ฐ ๋ฐ์ ์ํค๊ธฐ12}13case INCREMENT:14return {15...state,16counter: state.counter + action.diff,17}18case DECREMENT:19return {20...state,21counter: state.counter - 1,22}23default:24// ์ง์ํ์ง ์๋ ์ก์ ์ ๊ฒฝ์ฐ ์ํ ์ ์ง25return state26}27}
- ๋ฆฌ๋์๋ ๋ณํ๋ฅผ ์ผ์ผํค๋ ํจ์
- ํ๋ผ๋ฏธํฐ๋ก๋ state ์ action ์ ๋ฐ์์ด
- ๋ฆฌ๋์ ํจ์๊ฐ ๊ฐ์ฅ ์ฒ์ ํธ์ถ ๋ ๋๋ state ๊ฐ undefined
- state๊ฐ undefined ๋ก ์ฃผ์ด์ก์๋ initialState ๋ฅผ ์ฌ์ฉํ๋๋ก ์ค์ ํ๊ธฐ์ํด ํ๋ผ๋ฏธํฐ์ชฝ์์ ๊ธฐ๋ณธ๊ฐ์ด ์ค์
2.6 ์คํ ์ด ๋ง๋ค๊ธฐ
1// index.js2// ์๋ต34// **** ์คํ ์ด ๋ง๋ค๊ธฐ5// createStore ํจ์๋ฅผ ์ฌ์ฉ, ํ๋ผ๋ฏธํฐ๋ ๋ฆฌ๋์ ํจ์๋ฅผ ์ ๋ฌ6const store = createStore(reducer)
- ์คํ ์ด๋ฅผ ๋ง๋ค๋ createStore ํจ์๋ฅผ ์ฌ์ฉ
- ํ๋ผ๋ฏธํฐ๋ ๋ฆฌ๋์ ํจ์๋ฅผ ์ ๋ฌ
2.7 render() ๊ตฌํ
1// index.js2// ์๋ต34// **** render ํจ์ ๋ง๋ค๊ธฐ5const render = () => {6const state = store.getState() // ํ์ฌ ์ํ๋ฅผ ๊ฐ์ ธ์ต๋๋ค.7const { light, counter } = state // ํธ์์ ๋น๊ตฌ์กฐํ ํ ๋น8if (light) {9lightDiv.style.background = 'green'10switchButton.innerText = '๋๊ธฐ'11} else {12lightDiv.style.background = 'gray'13switchButton.innerText = '์ผ๊ธฐ'14}15counterHeadings.innerText = counter16}1718render()
- ์คํ ์ด์ ํ์ฌ ์ํ๋ฅผ ๊ฐ์ ธ์ฌ ๋ ์คํ ์ด์ ๋ด์ฅํจ์
getState๋ฅผ ์ฌ์ฉ
2.8 ์คํ ์ด ๊ตฌ๋ (subscribe) ํ๊ธฐ
1// **** ์์์2const listener = () => console.log('์ ๋ฐ์ดํธ ๋์ด์!')3const unsubscribe = store.subscribe(listener)4// ๋์ค์ unsubscribe();
- ์คํ ์ด์ ์ํ๊ฐ ๋ฐ๋ ๋ ๋ง๋ค, ์ฐ๋ฆฌ๋ render ํจ์๋ฅผ ํธ์ถํด์ค์ผ ํจ
- ๊ทธ๋ฌ๋ ค๋จผ, ์คํ ์ด๋ฅผ ๊ตฌ๋ ํด์ฃผ์ด์ผ ํฉ๋๋ค.
- ๊ตฌ๋
์ ํ ๋์๋ ์คํ ์ด์ ๋ด์ฅํจ์
subscribe๋ฅผ ์ฌ์ฉ
1// index.js2// ์๋ต34// **** ๊ตฌ๋ ํ๊ธฐ5store.subscribe(render)
- subscribe ํจ์์ ํ๋ผ๋ฏธํฐ๋ก๋, ํจ์ํํ์ ๊ฐ์ ์ ๋ฌ
- ์ ๋ฌ๋ ํจ์๋, ์ก์ ์ด ๋์คํจ์น ๋ ๋ ๋ง๋ค ํธ์ถ์ด ๋จ
- subscribe ๋ฅผ ํธ์ถํ๋ฉด ๋ฐํ๊ฐ์ผ๋ก ๊ตฌ๋ ์ ํด์ ํ๋ unsubscribe()๋ฅผ ๋ฐ๋๋ฐ ๋์ค์ ํ์ํด์ง ๋ ํธ์ถํ๋ฉด ๋จ
- cf. ๋ฆฌ์กํธ ์์ด ํ๊ธฐ ๋๋ฌธ์ ์ด๋ ๊ฒ subscribe ํจ์์ ๋ํ ์ฌ์ฉ๋ฒ์ ์ตํ๋ณด๊ณ ์์ง๋ง, ๋์ค์ ๋ฆฌ์กํธ์์ ๋ฆฌ๋์ค๋ฅผ ์ฝ๊ฒ ์ฌ์ฉํ๊ธฐ ์ํด react-redux ๋ผ๋๊ฑธ ์ฌ์ฉํ๊ฒ ๋๋๋ฐ ํด๋น ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ๋์ ํด์ฃผ๋ฏ๋ก ๋ฆฌ์กํธ ํ๋ก์ ํธ์์ subscribe ๋ฅผ ์ง์ ํด์ผ ๋๋ ์ผ์ ํน๋ณํ ์ํฉ์ ์ ์ธํ๊ณ ๋ ๊ฑฐ์ ์์
2.9 ์ด๋ฒคํธ ๋ฌ์์ฃผ๊ธฐ, ์ก์ ๋ฐ์์ํค๊ธฐ
1// index.js2// ์๋ต34// **** ์ด๋ฒคํธ ๋ฌ์์ฃผ๊ธฐ, ์ก์ ๋ฐ์ ์ํค๊ธฐ5// ์ก์ ์ ๋ฐ์์ํค๋๊ฒ์ ๋์คํจ์น(dispatch) ๋ผ๊ณ ํจ6// ๋์คํจ์น๋ฅผ ํ ๋, ์คํ ์ด์ ๋ด์ฅํจ์ dispatch ๋ฅผ ์ฌ์ฉ7switchButton.onclick = () => {8store.dispatch(toggleSwitch())9}1011plusButton.onclick = () => {12store.dispatch(increment(5))13}1415minusButton.onclick = () => {16store.dispatch(decrement())17}
- ์ก์
์ ๋ฐ์์ํค๋๊ฒ์
๋์คํจ์น (dispatch)๋ผ๊ณ ๋ถ๋ฆ - ๋์คํจ์น๋ฅผ ํ ๋, ์คํ ์ด์ ๋ด์ฅํจ์ dispatch ๋ฅผ ์ฌ์ฉ
- ํ๋ผ๋ฏธํฐ๋ ์ก์ ๊ฐ์ฒด๋ฅผ ์ ๋ฌ
https://codesandbox.io/s/vvzqnvw17y
ํ๋ฒ ๋ฒํผ๋ค์ ๋๋ฌ๋ณด์ธ์. ๊ฐ์ด ์ ๋ฐ๋๋์? ๋ชจ๋ ๋ค ์ ์ ์๋ํ๋ค๋ฉด, ๊ฐ๋ ๋ถ๋ถ์ ํค์๋๋ฅผ ๋ค์ ํ๋ฒ ์ญ ํ์ด๋ณด์
3. ๋ฆฌ๋์ค with ๋ฆฌ์กํธ
3.1 ๋ฆฌ๋์ค์ 3๊ฐ์ง ๊ท์น
ํ๋์ ์ฑ์์ ์คํ ์ด 1๊ฐ๋ง ๋ง๋ค์ด์ ์ฌ์ฉ- ์ฌ๋ฌ๊ฐ์ ์คํ ์ด๋ฅผ ์ฌ์ฉํ๋๊ฒ์ ์ฌ์ค ๊ฐ๋ฅํ๊ธฐ๋ ํ๋, ๊ถ์ฅํ์ง ์์
์ํ๋ ์ฝ๊ธฐ์ ์ฉ- ๋ฆฌ์กํธ์์ state ๋ฅผ ์ ๋ฐ์ดํธ ํด์ผ ํ ๋, setState ๋ฅผ ์ฌ์ฉํ๊ณ ,
- ๋ฐฐ์ด์ ์
๋ฐ์ดํธํ ๋๋ ๋ฐฐ์ด ์์ฒด์ push ๋ฅผ ์ง์ ํ์ง ์๊ณ ,
- concat ๊ฐ์ ํจ์๋ฅผ ์ฌ์ฉํด ๊ธฐ์กด์ ๋ฐฐ์ด์ ์์ ํ์ง ์๊ณ
- ์๋ก์ด ๋ฐฐ์ด์ ๋ง๋ค์ด์ ๊ต์ฒดํ๋ ๋ฐฉ์์ผ๋ก ์ ๋ฐ์ดํธ
- ์์ฒญ ๊น์ ๊ตฌ์กฐ๋ก ๋์ด์๋ ๊ฐ์ฒด๋ฅผ ์
๋ฐ์ดํธ๋ฅผ ํ ๋๋ ๋ง์ฐฌ๊ฐ์ง๋ก,
- ๊ธฐ์กด์ ๊ฐ์ฒด๋ ๊ฑด๋ค์ด์ง ์๊ณ
Object.assign์ ์ฌ์ฉํ๊ฑฐ๋ spread ์ฐ์ฐ์ (...) ๋ฅผ ์ฌ์ฉ
- ๊ธฐ์กด์ ๊ฐ์ฒด๋ ๊ฑด๋ค์ด์ง ์๊ณ
- ๋ฆฌ๋์ค์์๋ ๋ง์ฐฌ๊ฐ์ง๋ก ๊ธฐ์กด์ ์ํ๋ ๊ฑด๋ค์ด์ง ์๊ณ ์๋ก์ด ์ํ๋ฅผ ์์ฑํ์ฌ ์ ๋ฐ์ดํธ
- ๋ฆฌ๋์ค์์ ๋ถ๋ณ์ฑ์ ์ ์งํด์ผ ํ๋ ์ด์ ๋
- ๋ด๋ถ์ ์ผ๋ก ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ ๋๋ ๊ฒ์ ๊ฐ์งํ๊ธฐ ์ํ์ฌ shallow equality ๊ฒ์ฌ๋ฅผ ํ๊ธฐ ๋๋ฌธ
- ์ด๋ก ์ธํด ๊ฐ์ฒด์ ๋ณํ๋ฅผ ๊ฐ์งํ ๋ ๊ฐ์ฒด์ ๊น์ํ ์์ชฝ๊น์ง ๋น๊ต๋ฅผ ํ๋ ๊ฒ์ด ์๋๋ผ,
- ๊ฒํฅ๊ธฐ ์์ผ๋ก ๋น๊ต๋ฅผ ํ์ฌ ์ข์ ์ฑ๋ฅ์ ์ ์งํ ์ ์์
- ์ฌ๊ธฐ์๋ Immer.js ๋ฅผ ์ฌ์ฉํ์ฌ ๋ถ๋ณ์ฑ์ ์ ์งํ๋ฉฐ ์ํ๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์
๋ณํ๋ฅผ ์ผ์ผํค๋ ํจ์(๋ฆฌ๋์)๋ ์์ํ ํจ์์ฌ์ผ ํฉ๋๋ค.- ๋ฆฌ๋์ ํจ์๋ ์ด์ ์ํ์, ์ก์ ๊ฐ์ฒด๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์
- ์ด์ ์ ์ํ๋ ์ ๋๋ก ๊ฑด๋ค์ด์ง ์๊ณ , ๋ณํ๋ฅผ ์ผ์ผํจ ์๋ก์ด ์ํ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด์ ๋ฐํํจ
- ๋๊ฐ์ ํ๋ผ๋ฏธํฐ๋ก ํธ์ถ๋ ๋ฆฌ๋์ ํจ์๋ ์ธ์ ๋ ๋๊ฐ์ ๊ฒฐ๊ณผ๊ฐ์ ๋ฐํํด์ผ๋ง ํจ
๋์ผํ input์ด๋ผ๋ฉด ์ธ์ ๋ ๋์ผํ output์ด ์์ด์ผ ํฉ๋๋ค. ๊ทธ๋ฐ๋ฐ new Date(), ๋๋ค์ซ์, ๋คํธ์ํฌ ์์ฒญ๊ฐ์ ์คํ๋ง๋ค ๋ค๋ฅธ ๊ฒฐ๊ณผ๊ฐ์ด ๋์ค๋ ์์ ๋ค์ ๋ฆฌ๋์ ํจ์ ๋ฐ๊นฅ์์ ์ฒ๋ฆฌํด์ผ ํฉ๋๋ค. ์ด๋ฅผ ์ํด ๋ฆฌ๋์ค ๋ฏธ๋ค์จ์ด๋ฅผ ์ฌ์ฉํฉ๋๋ค.
3.2 ๋ฆฌ์กํธ์์ ๋ฆฌ๋์ค๋ฅผ ์ฐ๋ ค๋ฉด
๋ค์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด ์ค์น๋์ด์ผ ํฉ๋๋ค.
- redux: ๋ฆฌ๋์ค ๋ชจ๋
- react-redux: ๋ฆฌ์กํธ ์ปดํฌ๋ํธ์์ ๋ฆฌ๋์ค๋ฅผ ์ฌ์ฉํ๊ธฐ์ํ ์ ์ฉํ ๋๊ตฌ๋ค์ด ๋ค์ด๊ฐ์์ต๋๋ค.
- redux-actions: ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ผญ ์ค์น ํ ํ์๋ ์์ต๋๋ค. ๋จ, ์์๋๋ฉด ๊ต์ฅํ ์ ์ฉํฉ๋๋ค.
์ค์ต
3.3 Immer.js๋ฅผ ์ฌ์ฉํ ๋ถ๋ณ์ฑ ๊ด๋ฆฌ
4. ๊ณต์๋ฌธ์ ์์ ๋ฐ๋ผํด๋ณด๊ธฐ
1npm install @reduxjs/toolkit react-redux
4.1 Redux Store ๋ง๋ค๊ธฐ
store = state๋ค์ ๋ณด๊ดํ๋ ํ์ผ
1// app/store.js2import { configureStore } from '@reduxjs/toolkit'34export default configureStore({5reducer: {},6})
4.2 Redux State Slice ์์ฑ
1// src/index.js2import React from 'react'3import ReactDOM from 'react-dom/client'4import './index.css'5import App from './App'6import { Provider } from 'react-redux' // ์ถ๊ฐ7import store from './app/store'89// Start the mocking conditionally.10if (process.env.NODE_ENV === 'development') {11const { worker } = require('./mocks/browser')12worker.start()13}1415const root = ReactDOM.createRoot(document.getElementById('root'))16root.render(17<React.StrictMode>18{/* ์ถ๊ฐ */}19<Provider store={store}>20<App />21</Provider>22</React.StrictMode>,23)
- Provider๋ผ๋ ์ปดํฌ๋ํธ์ ์๊น ์์ฑํ ํ์ผ์ import
<Provider store={importํด์จ๊ฑฐ}>์ด๊ฑธ๋ก<App/>์ ๊ฐ์ธ๋ฉด ๋ฉ๋๋ค.- ๊ทธ๋ผ ์ด์
<App>๊ณผ ๊ทธ ๋ชจ๋ ์์ ์ปดํฌ๋ํธ๋ค์ store.js์ ์๋ state๋ฅผ ๋ง๋๋ก ๊บผ๋ด์ธ ์ ์์
4.3 Store์ Slice Reducer ์ถ๊ฐ
Redux store์ state ๋ณด๊ดํ๋ ๋ฒ
createSlice()๋ก state ๋ง๋ค๊ณconfigureStore()์์ ๋ฑ๋ก
1// features/counter/counterSlice.js2import { createSlice } from '@reduxjs/toolkit'34// 1. createSlice()๋ก state ๋ง๋ค๊ธฐ5export const counterSlice = createSlice({6name: 'counter',7initialState: {8value: 0,9},10reducers: {11// Redux Toolkit์ immer๋ฅผ ์ฌ์ฉํ์ฌ "๋ถ๋ณ์ฑ"์ ๋ณด์ฅํฉ๋๋ค.12// ๋ถ๋ณ์ฑ์ ๋ณด์ฅํ๋ฉด ๋ ์ฝ๊ฒ ์์ธก ๊ฐ๋ฅํ ์ํ๋ฅผ ์ ์งํ ์ ์์ต๋๋ค.13increment: state => {14state.value += 115},16decrement: state => {17state.value -= 118},19incrementByAmount: (state, action) => {20state.value += action.payload21},22},23})2425// Action creators๋ reducer ํจ์ ๋ณ๋ก ์์ฑ๋ฉ๋๋ค.26export const { increment, decrement, incrementByAmount } = counterSlice.actions2728export default counterSlice.reducer
1// app/store.js2import { configureStore } from '@reduxjs/toolkit'3import counterReducer from '../features/counter/counterSlice'45// 2. configureStore() ์์ ๋ฑ๋ก6export default configureStore({7reducer: {8counter: counterReducer,9},10})
4.4 ์ปดํฌ๋ํธ์์ State, Actions ์ฌ์ฉ
1import { useDispatch, useSelector } from 'react-redux'2import { decrement, increment, incrementByAmount } from './counterSlice'34export default function Counter() {5const count = useSelector(state => state.counter.value)6const dispatch = useDispatch()78return (9<div>10<div>11<button onClick={() => dispatch(increment())}>Increment</button>12<span>{count}</span>13<button onClick={() => dispatch(decrement())}>Decrement</button>14<button onClick={() => dispatch(incrementByAmount(5))}>+5</button>15</div>16</div>17)18}
4.5 ๋น๊ต์ฉ Counter2
1import { useDispatch, useSelector } from 'react-redux'2import { decrement, increment, incrementByAmount } from '../features/counter/counterSlice'34export default function Counter2() {5const count = useSelector(state => state.counter.value)6const dispatch = useDispatch()78return (9<div>10<div>11<button onClick={() => dispatch(increment())}>Increment</button>12<span>{count}</span>13<button onClick={() => dispatch(decrement())}>Decrement</button>14<button onClick={() => dispatch(incrementByAmount(5))}>+5</button>15</div>16</div>17)18}
1import './App.css'2import TestMocking from './components/TestMocking'3import Counter from './features/counter/Counter'4import Counter2 from './components/Counter2'56export default function App() {7return (8<div className="App">9<TestMocking />10<Counter />1112<Counter2 />13</div>14)15}