๐จ๐ป๐ปMVC ํจํด๊ณผ FLUX ํจํด
์ด๋ ๊ฒ ์ด๋ ต๊ณ ๋ณต์กํ MVCํจํด์ ๋ฌธ์ ์ ์ด์๋ ์๋ฐฉํฅ ๋ฐ์ธ๋ฉ์ผ๋ก ์ธํด์ state์ ๊ด๋ฆฌ์ ์ด๋ ค์์ ๊ฒช์๋ค.
๋จ๋ฐฉํฅ ๋ฐ์ธ๋ฉ์ ์ํด์ FLUXํจํด์ด ๋ฑ์ฅํ๊ฒ ๋์๋ค.
View๋ MVC ํจํด๊ณผ ๋ฌ๋ฆฌ ๋ฐ์ดํฐ๋ฅผ ์ง์ ๋ณ๊ฒฝ์ํค์ง ์๊ณ Action๋ง์ ๋๊ฒจ์ค ํ, View์์ ์ด๋ค์ง Action์ ๋ฐ๋์ Dispatcher๋ฅผ ๊ฑฐ์ณ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ ์งํํ๊ฒ ๋ฉ๋๋ค.
๋ฐ์ดํฐ ๋ณ๊ฒฝ์ด ์ด๋ค์ง ์ดํ, Store์ ๊ฐ์ ์ ์ฅํ๋ ๊ณผ์ ์ ํตํด View๋ ๋ณ๊ฒฝ๋ ๋ฐ์ดํฐ๋ฅผ Store๋ฅผ ํตํด์ ์ ๋ฌ๋ฐ๊ฒ ๋ฉ๋๋ค.
์์ ๊ฐ์ ๊ณผ์ ์ ํตํด ์ด๋ค์ง ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ ๋ทฐ์ ๋ชจ๋ธ ์ฌ์ด์ state ์ ์ด๋ฅผ ๊ฐํธํํด์ฃผ๊ณ ๊ธฐ์กด์ ํ๋ฆ์ ํ์ ํ๊ธฐ ์ด๋ ค์ ๋ ๊ด๋ฆฌ์ ์์ธก์ด ๊ฐ๋ฅํ๊ฒ ํด์ฃผ์ด, ๋์ฑ ํธ๋ฆฌํ state ๋ณ๊ฒฝ ๋ฐฉ๋ฒ์ ์ ์ํ๊ฒ ๋ฉ๋๋ค.
์์ ๊ฐ์ ๋ฐฉ์์ ์ฑํํ์ฌ ์ต์ข ์ ์ผ๋ก 2015๋ ์ Dan Abramov์ ์ํด์ React + Flux์ ๊ตฌ์กฐ์ ‘Reducer’๋ฅผ ๊ฒฐํฉํ ‘Redux’๊ฐ ๋ฑ์ฅํ๊ฒ ๋ฉ๋๋ค.
์ดํ์ Flux ํจํด์ ์ด์ฉํ Redux๊ฐ ๋ฑ์ฅํ๊ฒ ๋์๋๋ฐ, Store, Dispatch, Reducer์ ๋ํ ๊ฐ๋ ์ ์ ํํ๊ฒ ๋ค์ ์ ๋ฆฌํด์ฃผ์์ต๋๋ค.
ํ๋ก ํธ์๋๋ผ๋ฉด ๊ผญ ์ฝ์ด๋ด์ผํ ๋ฐ์ด๋ธ๊ฐ์ ๊ธ
๐์ ์ญ ์ํ๊ด๋ฆฌ๊ฐ ํ์ํ ์ด์
1. ๋ฆฌ๋์ค๋ ๋ณต์กํ State๊ด๋ฆฌ๋ฅผ ๋จ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ์ฒด๊ณ๋ก ๋ง๋ค์ด์ฃผ๋ ๋๊ตฌ์ด๋ค.
2. ๊ตฌ์กฐ๊ฐ ๊ฐ๋จํ ๊ฒฝ์ฐ, React๋ง์ผ๋ก๋ ์ถฉ๋ถํ ๋จ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ์ ์ฌ์ฉํ ์ ์๋ค.
3. ์น ๊ตฌ์กฐ์ ํ๋ฆ์ ํ์ ํ๊ณ ์ค๊ณ๊ณผ์ ์์ Redux์ ์ฌ์ฉ์ฌ๋ถ๋ฅผ ๊ฒฐ์ ํ๋ ๊ฒ์ด ์ข์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ฑํ๋๋ฐ ๋์์ ์ค๋ค.
์ ์ญ ์ํ๋ฅผ ํตํด์ ๋ ๋์ ์ปดํฌ๋ํธ ์ค๊ณ๋ฅผ ํ ์ ์๋ค.
๐ค๋ฆฌ๋์ค ์ค์นํ๊ธฐ
$ yarn add redux react-redux
react-redux๋ redux๋ฅผ react์์ ์ฌ์ฉํ ์ ์๋๋ก ํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค.
๐ํ๋ก์ ํธ ๊ตฌ์ฑํ๊ธฐ
src ํด๋ ๋ด๋ถ์ redux ํด๋๋ฅผ ์์ฑํด์ฃผ๊ณ config, modules ํด๋๋ฅผ ์์ฑํฉ๋๋ค.
๊ฒฝ๋ก์ ๋ง๊ฒ js ํ์ผ์ ์์ฑํฉ๋๋ค.
๐redux / config / configStore.js
๐redux / modules / counter.js
๊ฐ๊ฐ์ ์ญํ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- redux : ๋ฆฌ๋์ค์ ๊ด๋ จ๋ ์ฝ๋๋ฅผ ๋ชจ๋ ๋ชจ์ ๋์ ํด๋ ์ ๋๋ค.
- config : ๋ฆฌ๋์ค ์ค์ ๊ณผ ๊ด๋ จ๋ ํ์ผ๋ค์ ๋์ ํด๋ ์ ๋๋ค.
- configStore : “์ค์ state ๊ด๋ฆฌ์" ์ธ Store๋ฅผ ๋ง๋๋ ์ค์ ์ฝ๋๋ค์ด ์๋ ํ์ผ ์ ๋๋ค.
- modules : ์ฐ๋ฆฌ๊ฐ ๋ง๋ค State๋ค์ ๊ทธ๋ฃน์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋ฉ๋๋ค.
์๋ฅผ ๋ค์ด ํฌ๋๋ฆฌ์คํธ๋ฅผ ๋ง๋ ๋ค๊ณ ํ๋ค๋ฉด, ํฌ๋๋ฆฌ์คํธ์ ํ์ํ state๋ค์ด ๋ชจ๋ ๋ชจ์ฌ์์ todos.js๋ฅผ ์์ฑํ๊ฒ ๋ํ ๋ฐ์, ์ด todos.js ํ์ผ์ด ๊ณง ํ๋์ ๋ชจ๋์ด ๋ฉ๋๋ค.
/* ducks ํจํด์ ๋ํ ๊ธ */
โ๋ฆฌ๋์ค ๊ตฌ์ฑ์์ ์ธํ ํ๊ธฐ
๐src/configStore.js
import { createStore } from "redux";
import { combineReducers } from "redux";
const rootReducer = combineReducers({});
const store = createStore(rootReducer);
export default store;
creatorStore๋ ์ ์ฅ์๋ฅผ ๋ง๋ค์ด ์ฃผ๋ ํจ์์ ๋๋ค.
๋ฆฌ์กํธ ์ฑ์ ํฌ๊ธฐ๊ฐ ์ปค์ง๋ค ๋ณด๋ฉด ์์ฐ์ค๋ Reducer๊ฐ ๋ง์์ง๋๋ค.
๊ทธ ๋ combineReducers๋ Reducer๋ค์ ํ๋์ ์ํ ๊ฐ์ฒด๋ก ๋ง๋ค์ด์ฃผ๋ ์ญํ ์ ํฉ๋๋ค.
๐คcreateStore์ ๋ฐ์ค์ด ์๊ฒจ์!
createStore๋ ๋์ด์ ์ ๋ฐ์ดํธํ์ง ์์ต๋๋ค. deprecated์ด๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ํจ์๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ์ถ์ฒํ๊ณ ์์ต๋๋ค. redux-toolkit ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ๋์ฒด๋์๊ธฐ ๋๋ฌธ์ธ๋ฐ์.
๋น์ฅ์๋ ํฐ ๋ฌธ์ ๋ ์๊ธฐ ๋๋ฌธ์ createStore๋ฅผ ์ด์ฉํด์ ์งํํ๊ฒ ์ต๋๋ค.
@deprecated
We recommend using the configureStore method of the @reduxjs/toolkit package, which replaces createStore.
๐index.js
import store from "./redux/config/configStore";
import { Provider } from "react-redux";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<Provider store={store}>
<App />
</Provider>
);
๋๋ ํ ๋ฆฌ ์ต์๋จ์ index.js ํ์ผ์ Provider๋ก App์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ธ์ค๋๋ค.
provider๋ store์ ์ ๊ทผํ ์ ์๋๋ก ์ฐ๋ํด์ฃผ๋ ์ญํ ์ ํฉ๋๋ค.
๐ฎ๊ตฌํํ๊ธฐ
๋ชจ๋ ๋ง๋ค๊ธฐ
๐ src/modules/counter.js
// ์ด๊ธฐ ์ํ๊ฐ
const initialState = {
number: 0,
};
// reducer
const counter = (state = initialState, action) => {
switch (action.type) {
default:
return state;
}
};
export default counter;
๋ชจ๋์ ๊ธฐ๋ณธ์ ์ธ ํํ๋ ์ด๋ ๊ฒ ์ก์๋๊ณ ๊ฐ๋๋ค.
์ด๊ธฐ์ํ๊ฐ์ useState ์ฌ์ฉ์์ ์ด๊ธฐ๊ฐ์ ๋ฃ์ด์ฃผ๋ ๊ฒ๊ณผ ๊ฐ์ ํํ์ ๋๋ค.
const [count, setCount] = useState(0);
๊ฐ์ฒด ํํ์ด๋ ๋ฐฐ์ด ํํ์ด๋ ์ํฉ์ ๋ฐ๋ผ ํ์ํ ๊ฐ์ ์ ๋ ฅํด์ฃผ๋ฉด ๋ฉ๋๋ค.
const initialState = 0;
const initialState = [0];
const initialState = {
number: 0,
};
Reducer๋ ๋ณํ๋ฅผ ์ํค๋ ํจ์์ ๋๋ค. state hook์์์ setState์ ์ญํ ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋ฉ๋๋ค.
"ํจ์"๋ผ๋ ๊ฒ์ ์ ์ํด์ผํฉ๋๋ค.
const counter = (state = initialState, action) => {
switch (action.type) {
default:
return state;
}
};
๋ฆฌ๋์ ๋ด๋ถ์ ๊ฐ๊ฐ์ ๋์์ธ Action์ ์ถ๊ฐํ๋ ๋ฐฉ๋ฒ์ ๋ฐ์์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
๋ชจ๋ ์ฐ๊ฒฐํ๊ธฐ
๐src/redux/modules/config/configStore.js
import counter from "../modules/counter";
const rootReducer = combineReducers({
counter: counter, // counter๋ง ์จ๋ ๋จ (ES6)
});
์์์ ์์ฑํ ๋ชจ๋์ ์์ฑํด๋ store์ combineReducers์ ์ถ๊ฐํด์ฃผ๋ฉด store์ ๋ชจ๋์ด ์ฐ๊ฒฐ๋ฉ๋๋ค.
Dispatch
๋ฆฌ๋์ค๋ action -> dispatch -> reducer ์์ผ๋ก ๋์ํฉ๋๋ค.
์์๋ฅผ ๊ธฐ์ตํ์๊ณ , ์ก์
์ ๋ง๋ค์ด์ Dispatch ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ฐ์์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
UseSelector
store์ ๋ชจ๋์ด ์ฐ๊ฒฐ๋์ผ๋ฉด UseSelector๋ผ๋ Hook์ ์ด์ฉํด์ store๋ฅผ ์กฐํํ ์ ์์ต๋๋ค.
๋ค์ ์ฝ๋๋ก ์กฐํํด๋ด ์๋ค.
๐App.js
import React from "react";
import { useSelector } from "react-redux";
const App = () => {
const counterStore = useSelector((state) => state);
return <div>{counterStore}</div>;
}
export default App;
+1 ๋ฒํผ ๋ง๋ค๊ธฐ
๐src/redux/modules/counter.js
const initialState = {
number: 0,
};
const counter = (state = initialState, action) => {
switch (action.type) {
case "PLUS_ONE":
return {
number: state.number + 1,
};
default:
return state;
}
};
export default counter;
PLUS_ONE ์ก์ ์ Reducer ๋ด๋ถ์ ์ถ๊ฐํด์ค๋๋ค.
import React from "react";
import { useDispatch, useSelector } from "react-redux";
const App = () => {
const dispatch = useDispatch();
const number = useSelector((state) => state.counter.number);
return (
<div>
{number}
<button onClick={() => {
dispatch(({type: "PLUS_ONE"}));
}}>+ 1</button>
</div>
);
};
export default App;
๋ฒํผ์ ๋ง๋ค์ด์ dispatch๋ฅผ onClick ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ก ์ ๋ฌํด์ค๋๋ค.
์ด ๋ dispatch ๋ด๋ถ์ ์์ฑํด ์ค Action์ ๊ฐ์ฒด ํํ๋ก ์ ๋ฌํฉ๋๋ค.
๊ฐ์ store์์ ์ฝ์ด์ฌ ์ ์๋๋ก useSelector๋ฅผ ์ด์ฉํด์ state์ ์ ์ฅ๋ number์ ๊ฐ์ ธ์ต๋๋ค.
-1 ๋ฒํผ ๋ง๋ค๊ธฐ
+1 ๋ฒํผ์ ๋ง๋ ๊ฒ์ฒ๋ผ -1์ ํด์ฃผ๋ ๋ฐ๋ ๊ธฐ๋ฅ๋ ๋ง๋ค์ด๋ด ์๋ค.
const counter = (state = initialState, action) => {
switch (action.type) {
case "MINUS_ONE":
return {
number: state.number - 1,
};
case "PLUS_ONE":
return {
number: state.number + 1,
};
default:
return state;
}
};
const App = () => {
...
return (
<div>
<button onClick={() => {
dispatch(({type: "MINUS_ONE"}));
}}>- 1</button>
{number}
<button onClick={() => {
dispatch(({type: "PLUS_ONE"}));
}}>+ 1</button>
</div>
);
};
โAction Creator ์ถ๊ฐํ๊ธฐ
๋ค์๊ณผ ๊ฐ์ ์ด์ ๋ก Action Creator๋ฅผ ์ฌ์ฉํฉ๋๋ค.
1. ๊ฐ๋ ์ฑ์ ์ฆ๊ฐ
Action Creator์ Action์ ๋ชจ์ ๋์ผ๋ฉด ํด๋น ๋ชจ๋์ด ๊ฐ์ง๊ณ ์๋ Action์ ํ๋ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
2. ํจ์จ์ ์ธ ์ ์ง๋ณด์
Action์ ์์ ํ ์ผ์ด ์๊ฒผ์ ๋ ํ๋ ํ๋ ์์ ํ ์ผ ์์ด Action Creator๋ง ์์ ํด์ฃผ๋ฉด ๋ฉ๋๋ค.
3. ํด๋จผ ์๋ฌ ๋ฐฉ์ง
์์ฑํ ํ์ ์ผ๋ก ์๋์์ฑ ๊ธฐ๋ฅ์ ๋์ ๋ฐ์ ์ ์์ต๋๋ค.
Action Creator๋ฅผ ๋ง๋ค์ด ๋ด ์๋ค.
๐counter.js
const PLUS_ONE = "PLUS_ONE";
const MINUS_ONE = "MINUS_ONE";
export const plusOne = () => {
return {
type: PLUS_ONE,
};
};
export const minusOne = () => {
return {
type: MINUS_ONE,
};
};
Action value๋ ์์๋ก ์ ์ธํด์ฃผ๊ณ (์์๋ ๋๋ฌธ์๋ก ์์ฑํ๋ ๊ฒ ์ ๊ณ ํ์ค์ ๋๋ค.)
์ ์ธํ ์์๋ฅผ ์ด์ฉํด์ Action Creator๋ฅผ ๋ง๋ค์ด์ค๋๋ค.
Action Creator๋ App.js์์ ์ฌ์ฉํ ์์ ์ด๊ธฐ ๋๋ฌธ์ export๋ฅผ ๋ถ์ฌ์ค๋๋ค.
๐app.js
import { minusOne, plusOne } from "./redux/modules/counter";
const App = () => {
...
return (
<div>
<button onClick={() => {
dispatch(minusOne());
}}>
- 1
</button>
{number}
<button onClick={() => {
dispatch(plusOne());
}}>
+ 1
</button>
</div>
);
};
app.js์์ ์์ฑํ Action Creator๋ค์ importํ ๋ค์์
dispatch ๋ถ๋ถ์ ์ ๋ ฅํด ์ฌ์ฉํด์ฃผ๋ฉด ๋ฉ๋๋ค.
[์ฐธ๊ณ ]
https://devlog-h.tistory.com/26
์คํ๋ฅดํ์ฝ๋ฉํด๋ฝ react ์๋ จ์ฃผ์ฐจ ๊ฐ์
'react' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Vite] vite๋ก ์ด์ ๋ชจ๋ฅด๊ฒ ์น์์ผ ํต์ ์ด ์๋๋ ๊ฒฝ์ฐ(ws, sockjs) (1) | 2023.01.05 |
---|---|
json-server ์ปค์คํ ํ๊ธฐ (0) | 2022.12.19 |
[React] Lifecycle๊ณผ ๋ฉ์๋ ๊ทธ๋ฆฌ๊ณ Hook (0) | 2022.12.12 |
[React] Redux-toolkit ์ ๋ฌธํ๊ธฐ (0) | 2022.12.10 |
[React] gh-pages ๋ฐฐํฌํ๊ธฐ (2) | 2022.12.02 |