๐Redux๋?
React ํ๋ก์ ํธ์ ๊ท๋ชจ๊ฐ ์ปค์ง๋๋ง๋ค ์์์ผ๋ก ๋๊ฒจ์ฃผ์ด์ผ ํ๋ props์ ๊น์ด๋ ์ ์ ๊น์ด์ง๋ค. ๋ฐ๋ผ์, ์ด๋์์๋ ๋ด๊ฐ ์ํ๋ state๋ฅผ ์ฌ์ฉํ ์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ Redux์ด๋ค. ๋ฆฌ๋์ค๋ฅผ ์ฌ์ฉํ๋ฉด ์ํ๊ฐ์, ์ปดํฌ๋ํธ์ ์ข ์์ํค์ง ์๊ณ , ์ํ ๊ด๋ฆฌ๋ฅผ ์ปดํฌ๋ํธ์ ๋ฐ๊นฅ์์ ๊ด๋ฆฌ ํ ์ ์๊ฒ ๋๋ค. ์ฐธ๊ณ ๋ก Redux๋ react์ ์ข ์๋ ๋๊ตฌ๊ฐ ์๋๋ค!
๐Redux ์ค์น
npm ๋ช ๋ น์ด๋ฅผ ํตํด์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค์นํด์ค๋ค. ๋ฆฌ์กํธ์์ redux ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ ํจ์จ์ ์ผ๋ก ์ฌ์ฉํ ์ ์๋ react-redux ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ค์นํด์ฃผ๋ ๊ฒ์ด ์ข๋ค.
npm install redux
npm install react-redux
์ด๋ ๊ฒ๋ ๊ฐ๋ฅ
npm install redux react-redux
๐Redux ์ ์ฉํ๊ธฐ
import './App.css';
import React, {useState} from 'react';
function App() {
const [number,setNumber] = useState(1);
return (
<div id="container">
<h1>Root : {number}</h1>
<div id="gird">
<Left1 doyi={number} />
<Right1
onIncrease={() => {
setNumber(number+1)
}}
/>
</div>
</div>
);
}
function Left1(props) {
return (
<div>
<h1>Left1 : {props.doyi}</h1>
<Left2 dong = {props.doyi}/>
</div>
);
}
function Left2(props) {
return (
<div>
<h1>Left2 : {props.dong}</h1>
<Left3 sang = {props.dong}/>
</div>
);
}
function Left3(props) {
return (
<div>
<h1>Left3 : {props.sang}</h1>
</div>
);
}
function Right1(props) {
return (
<div>
<h1>Right1</h1>
<Right2
onIncrease={() => {
props.onIncrease();
}}
/>
</div>
);
}
function Right2(props) {
return (
<div>
<h1>Right2</h1>
<Right3 onIncrease={()=>{
props.onIncrease();
}} />
</div>
);
}
function Right3(props) {
return (
<div>
<h1>Right3</h1>
<input type='button' value="+" onClick={()=>{props.onIncrease();}}></input>
</div>
);
}
export default App;
์ด๋ฐ์์ผ๋ก ๋ฌดํ props์ ์ด์ฉํ ๋์ด ๋์ด ์ซ์ ์ ๋ฌํ๊ธฐ
์ฐ๋ฆฌ๋ ์ด๋ฐ ํ์์ ๋ณด๋ฉฐ Left3๊น์ง ์์ดํ์ด, ๋ธ๋ฃจํฌ์ค์ ๊ฐ์ ๊ฑธ๋ก props๋ฅผ ์ฐ๊ฒฐํ ์ ์๋๋ผ๋ ์๊ฐ์ด ๋ ๋ค.
๐Redux ํ๋ฆ
dispatch() ๋ฉ์๋๋ฅผ ํตํด Action(์ก์ ) ๊ฐ์ฒด๊ฐ ๋ฐ๋๋๋ค.
dispatch(์ก์ )๋ฅผ ํตํด Reducer๋ฅผ ํธ์ถํ๋ค.
Reducer๋ ์๋ก์ด Store ๋ฅผ ์์ฑํ๋ค.
๐Redux 3๋ ์์น
- ์ ํ๋ฆฌ์ผ์ด์ ์ํ๋ ๋ชจ๋ ํ ๊ณณ์์ ์ง์ค ๊ด๋ฆฌ๋๋ค. (๋๊ธฐํ ํ์ โ)
- ์ํ๋ ๋ถ๋ณ(์ฝ๊ธฐ ์ ์ฉ) ๋ฐ์ดํฐ ์ด๋ฉฐ, ์ค์ง ์ก์ ๋ง์ด ์ํ ๊ต์ฒด๋ฅผ ์์ฒญ ํ ์ ์๋ค. (์์ธก ๊ฐ๋ฅ)
- ๋ฆฌ๋์(ํจ์)๋ฅผ ํตํด ์ํ์ ์ต์ข ๊ฐ๋ง ์ค์ ํ๋ค. (๋จ์ํ)
์ก์ (action)
์ก์ ์ ์ ํ๋ฆฌ์ผ์ด์ ์์ "์ํ ๋ณ๊ฒฝ์ ์ค๋ช ํ๋ ์ ๋ณด"๋ฅผ ์คํ ์ด๋ก ๋ณด๋ด๋ JavaScript ๊ฐ์ฒด๋ก Redux์ ์๋ ค(dispatch) ๋ณํ๋ฅผ ์ด๋์ด๋ธ๋ค. ์ํ ๊ฐ์ ๋ณ๊ฒฝ(๊ต์ฒด) ํ ๊ฒฝ์ฐ, ๊ต์ฒด ํ ์ํ ๊ฐ(payload)์ redux(ํจ์)์ ๋ณด๋ผ ์ ์๋ค.
๋ฆฌ๋์(Reducer)
๋ชจ๋ Redux ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ณตํต์ ์ด ์๋ค. ๋ฐ๋ก ๋ฆฌ๋์(reducer)๋ฅผ ๊ตฌํํด์ผ ํ๋ค๋ ์ ์ด๋ค. ๋ฆฌ๋์๋ '์ ํ๋ฆฌ์ผ์ด์ ์ํ๋ฅผ ๊ต์ฒดํ๋ ํจ์'๋ฅผ ๋งํ๋ค. '์ด์ ์ํ(prevState)'๋ฅผ '์๋ก์ด ์ํ(state)'๋ก ๊ต์ฒด(return) ํ๋ค.
๋ฆฌ๋์๋ ์ํ์ ์ก์ ์ ์ ๋ฌ ๋ฐ์ '์ด์ ์ํ'๋ฅผ '๋ค์ ์ํ'๋ก ๊ต์ฒด ํ ํ ๋ฐํํ๋ค. ์ฃผ์ด์ง ์ํ๋ฅผ ์์ ํ๋ ๊ฒ์ด ์๋๋ผ, ์๋ก์ด ์ํ๋ก ๊ต์ฒดํ๋ ๊ฒ์ด ์ค์ํ๋ค. ์ฆ, ๋ฆฌ๋์๋ '์์ํ ํจ์'์ฌ์ผ ํ๋ค.
์คํ ์ด(Store)
Redux ์คํ ์ด(store)๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ณ , .getState(), .dispach(), .subscribe() ๊ฐ์ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค.
์คํ ์ด๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ๋ฅผ ๋ํ๋ด๋ ๋ง์ key:value ์์ผ๋ก ๊ตฌ์ฑ๋ ์ ๋ณด๋ฅผ ๊ฐ์ง ํ๋์ ํฐ JavaScript ๊ฐ์ฒด์ด๋ค. React ์ปดํฌ๋ํธ์ ๋ถ์ฐ๋์ด ์๋ ์ํ ๊ฐ์ฒด์ ๋ฌ๋ฆฌ ์คํ ์ด๋ ํ๋๋ง ์กด์ฌํ๋ค.
์คํ ์ด๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ๋ฅผ ์ ๊ณตํ๋ฉฐ ์ํ๊ฐ ์ ๋ฐ์ดํธ ๋๋ฉด ๋ทฐ(UI)๊ฐ ๋ค์ ๊ทธ๋ ค์ง๋ค.
์คํ ์ดํธ(state)
Redux ์คํ ์ด์์ ๊ด๋ฆฌํ๋ ์ํ(๋ฐ์ดํฐ) ์ด๋ค.
์ํ ํธ๋ฆฌ(state tree)๋ "๋ถ๋ณ ์ํ(Immutable State)" ์ฆ ์์ํ ์ํ๋ฅผ ๊ฐ์ ธ์ผํ๋ค. ์ ํ๋ฆฌ์ผ์ด์ ์ UI, View ๋ ์ด์ด ์ํ๋ ์์ํ ํจ์(๋ฆฌ๋์)๋ก ์ฒ๋ฆฌ ๋ ๋ ์์ธก ๊ฐ๋ฅ(์ฌ์ด๋ ์ดํํธ โ) ํ๊ธฐ ๋๋ฌธ์ด๋ค.
์๋ธ์คํฌ๋ฆฝ(subscription)
์ ํ๋ฆฌ์ผ์ด์ ์ํ ๋ณ๊ฒฝ์ ๊ตฌ๋ (subscribe, ๊ฐ์ง) ํ์ฌ ์ํ๊ฐ ์ ๋ฐ์ดํธ ๋๋ฉด ๋ฑ๋ก๋ ๋ฆฌ์ค๋(listener)๋ฅผ ์คํ์ํจ๋ค.
์์ํ ํจ์๋?
์์ ํจ์๋ ๋ฐํ(reture) ๊ฐ์ด ์ ๋ฌ ์ธ์(argument) ๊ฐ์๋ง ์์กดํ๋ ํจ์๋ฅผ ๋งํ๋ค. ์์ ํจ์๋ ์๋ก์ด ๊ฐ์ ๊ณ์ฐํ๋ค. ๋์ผํ ์ ํ์ ์ธ์ ์งํฉ์ ์ฌ์ฉํด ์์ ํจ์๋ฅผ ํธ์ถํ๋ฉด ์์ธก ๊ฐ๋ฅํ ๊ฒฐ๊ณผ ๊ฐ์ ํ์ ํ ์ ์๋ค. ๋ํ ์์ ํจ์๋ ์ ๋ฌ ๋ ์ธ์ ๊ฐ์ ๋ณ๊ฒฝํ์ง ์๋๋ค. ์๋ฅผ ๋ค์ด squareAll ํจ์๋ ์ ๋ฌ๋ ๋ฐฐ์ด ์ธ์๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ณ , map ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ์๋ก์ด ๋ฐฐ์ด์ ๋ฐํํ๋ค.
๐Redux๋ฅผ ์ด์ฉํ ์ต์ข ์ฝ๋
import './App.css';
import React, {useState} from 'react';
import { createStore } from 'redux';
import { Provider, useSelector, useDispatch} from 'react-redux';
function reducer(currentState, action) {
// reducer๋ ํ์ฌ state๊ฐ๊ณผ ์ด๋ป๊ฒ ๋ฐ๊ฟ ๊ฒ์ธ์ง์ ๋ํ action๊ฐ 2๊ฐ๋ฅผ ์ธ์๋ก ๋ฐ๋๋ค.
if(currentState === undefined){
return{
number:1
};
}
// ๊ณผ๊ฑฐ์ state๋ฅผ ๋ณต์ ํ๋ค.
const newState = {...currentState}
// ๊ทธ๋ ๊ฒ ๋ณต์ ํ ๊ฐ์ฒด๋ฅผ ์์ ํ๋ฉด ๋ถ๋ณ์ฑ์ ์ ์งํ ์ ์๋ค.
if(action.type === 'PLUS') {
newState.number++;
}
// returnํ ๊ฐ์ด ์๋ก์ด state๊ฐ์ด ๋๋ค.
return newState;
}
const storei = createStore(reducer);
// createStore ๋ ์ ์ญ ์ํ(state) ๋ฅผ ๊ฐ์ง๊ณ ์๋ store ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ํจ์์ด๋ค.
// ๊ทธ๋ฆฌ๊ณ store ๋ getState, subscribe, dispatch API ๋ฅผ ์ ๊ณตํ๋ค.
function App() {
return (
<div id="container">
<h1>Root </h1>
<div id="gird">
<Provider store={storei}>
{/* ์ด์ ์ด ์์ ์๋ component๋ store๋ฅผ ์ฌ์ฉํ ์ ์๋ค. */}
<Left1 />
<Right1/>
</Provider>
</div>
</div>
);
}
function Left1(props) {
return (
<div>
<h1>Left1 :</h1>
<Left2/>
</div>
);
}
function Left2(props) {
return (
<div>
<h1>Left2 : </h1>
<Left3 />
</div>
);
}
function Left3(props) {
// useSelector()๋ ํจ์๋ฅผ ์ธ์๊ฐ์ผ๋ก ๋ฐ๋๋ค.
const number = useSelector( (state) => state.number);
return (
<div>
{/* prop์ฌ์ฉํ์ง ์๊ณ ๋ฌด์ ์ผ๋ก ์ฐ๊ฒฐํ ํํ์ด๋ค. */}
<h1>Left3 : {number}</h1>
</div>
);
}
function Right1(props) {
return (
<div>
<h1>Right1</h1>
<Right2 />
</div>
);
}
function Right2(props) {
return (
<div>
<h1>Right2</h1>
<Right3 />
</div>
);
}
function Right3(props) {
const dispatch = useDispatch();
return (
<div>
<h1>Right3</h1>
{/* plus๋ผ๊ณ ํ๋ action์ ์ ๋ฌํ๋ค. */}
<input type='button' value="+" onClick={() => {dispatch({ type: 'PLUS'})}}></input>
</div>
);
}
export default App;
์ฐธ๊ณ ์๋ฃ
'Frontend > React' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
react - recoil์ด๋? (0) | 2023.12.31 |
---|---|
react - redux ์ด์ฉํด์ ๊ณ์ฐ๊ธฐ ๋ง๋ค๊ธฐ (2) | 2023.12.29 |
react - useRef ์ฌ์ฉํ๊ธฐ (0) | 2023.12.19 |
react Lifecycle , useEffect ์์ addEventListener (0) | 2023.11.17 |
react - router ๊ฐ๋ ์ ์ก์ ๋ณด์! (2) | 2023.10.13 |