๋ฐ๋ธ ์คํ ์ด๋ฅผ Redux๋ก ๋ฆฌํฉํ ๋งํ๊ณ ์๋ค. ๋ฐ๋๋ผJS ํ๋ก์ ํธ๋ฅผ ํ๋ฉด์ ์ํ๋ณ๊ฒฝ์ ์ํด ์ฝ๋๋ฅผ ๋ง์ด ์์ฑํด์ผํ๋ค.
์ฒ์ ํ๋ก์ ํธ๋ฅผ ์ค๊ณํ ๋๋ ๋ฐ๋๋ผ์ด๊ธฐ ๋๋ฌธ์ Redux๋ ์์ฐ๊ธฐ๋ก ๊ฒฐ์ ํ์ง๋ง, ๋ฐ๋๋ผ๋ก ์ํ๋ณ๊ฒฝ์ ํ๋ ค๋ฉด ์ด๋ ๊ฒ ์๊ธด ์ฝ๋๋ฅผ ์ฐ๊ฒ ๋๋ค...
if (i === 0) {
buttonLoginType[i + 1].classList.remove("on");
buttonLoginType[i].classList.remove("help");
buttonLoginType[i].classList.add("on");
buttonLoginType[i + 1].classList.add("help");
} else if (i === 1) {
buttonLoginType[i - 1].classList.remove("on");
buttonLoginType[i].classList.remove("help");
buttonLoginType[i].classList.add("on");
buttonLoginType[i - 1].classList.add("help");
}
๊ทธ๋์ ํ๋ก์ ํธ๋ฅผ ์ด๋์ ๋ ์์ฑํ ์์ ์ ๋ฆฌ๋์ค๋ฅผ ๋์ ํ๊ฒ ๋๋ค. ์ฝ๋๊ฐ ์์ฒญ ๊ฐ๊ฒฐํด์ง๊ณ ์๋ค!! ๐๐๐๐๐
Redux๋ ์๋ฐ์คํฌ๋ฆฝํธ์ ์์ธก๊ฐ๋ฅํ ์ํ๊ด๋ฆฌ ๋๊ตฌ๋ค.
์ํ์ ๋ณด๋ฅผ ์ค์์ง์ค์ ์ผ๋ก ๊ด๋ฆฌํ๊ณ ๋๋ฆฌ์ธ(?)์ ํตํด์๋ง ์ํ์ ๋ณด๋ฅผ ๋ณ๊ฒฝํ ์ ์๊ธฐ ๋๋ฌธ์ ์๋ํ์ง ์์ state
๊ฐ ๋ณ๊ฒฝ ๋ฌธ์ ๋ฅผ ์ฌ์ ์ฐจ๋จํ๋ค.
์ด๋ฐ ์์ด๋์ด๋ฟ๋ง ์๋๋ผ ์ด์ ์ ์ํ๊น์ง ๋ ์ฝ๋ฉ๋๋ ๊ธฐ๋ฅ๊ณผ hot ๋ชจ๋ ๋ฆฌ๋ก๋ฉ(์ฝ๋ ์์ ํ ์ ์ฅํ ๋ ์ ๋ถ ์๋ก๊ณ ์นจ๋๋ ๊ฒ์ด ์๋๋ผ ๋ณ๊ฒฝ๋ถ๋ถ๋ง ์์ ) ๊ธฐ๋ฅ ๋์ ์ธ๊ธฐ๊ฐ ๋ง์์ก๋ค๊ณ ํ๋ค.
store
๋ ์ํ์ด๊ณ dispatch
, subscribe
, getState
๋ ์ฐฝ๊ตฌ ์ง์์ด๋ค. ์์์ ๋งํ ๋๋ฆฌ์ธ์ ํด๋นํ๋ ํจ์๋ค์ด๋ค. ๊ทธ๋ฆฌ๊ณ ๊ธ๊ณ ์ง๊ธฐ๋ก reducer
๊ฐ ์๋ค.
state
๋ ์ ๋๋ก ์ง์ ์
๋ฐ์ดํธํ์ง ์๋๋ค. ๊ทธ๋์ ์ด๋ฌํ ์ญํ ๋ถ๋ด์ ๊ธฐ์ตํ๊ณ ์ ํ์ฉํ๋๊ฒ ์ค์ํ๋ค.
๊ธ๊ณ ์ง๊ธฐ์ธ reducer
์๊ฒ ๋ฐ์ดํฐ๋ฅผ ์์ ํ ์ ์๋ ๊ถํ์ด ์๋ค. reducer๋ฅผ ๋ง๋ค์ด ๋ณด์!
(์ฐธ๊ณ ๋ก ์ฐฝ๊ตฌ ์ง์๋ค์ reducer์๊ฒ ๋ฐ์ดํฐ ์ ๋ฌ๋ง ํด์ค๋ค)
import { configureStore } from "@reduxjs/toolkit";
import rootReducer from "./js/reducers/index.js";
const store = configureStore({
reducer: rootReducer,
devTools:
window.__REDUX__DEVTOOLS_EXTENSION__ &&
window.__REDUX__DEVTOOLS_EXTENSION__(),
});
configureStore
๋ ๊ธฐ์กด๋ฌธ๋ฒ์ธ createStore์ ์ฌ์ฉํ ๋ ์ทจ์์ ์ด ์๊ธฐ๋ฉด์ deprecated ๋์์ผ๋ redux/toolkit์ configureStore ์ฌ์ฉ์ ์ถ์ฒํ๋ค๋ ๋ฌธ๊ตฌ๋ฅผ ํ์ธํ ์ ์๋ค. ์๋ด์ ๋ฐ๋ผ์ configureStore๋ก ๋์ฒดํด์คฌ๋ค. ๋ฏธ๋ค์จ์ด๋ฅผ ์ถ๊ฐํ๊ณ , ๊ธฐ๋ณธ์ ์ผ๋ก redux-thunk๋ฅผ ํฌํจํ๊ณ ์๊ณ Redux DevTools Extension์ ์ฌ์ฉํ ์ ์๋ค.
combineReducers
๋ ์ฌ๋ฌ reducer๋ฅผ ํ๋ฐ ๋ชจ์๋ค. ์ํ๋ ๊ฐ์ฒดํํ๋ก ์ ์ฅ๋๋ฉฐ, ๊ฐ ๋ฆฌ๋์๋ (state, action)์ ํํ๋ฅผ ๊ฐ์ง๊ณ ๋ฆฌ๋์ค์ ์ฌ๋ผ์ด์ค ๋ฆฌ๋์ ๊ธฐ๋ฅ์ ์ฌ์ฉํด ๊ธฐ์กด์ ์ํ ๊ฐ์ฒด์ ํฉ์ณ์ง๋ค. ์ด๋ฌํ ํจํด์ ๊ตฌํํ ๊ฒ์ด combineReducers์ด๋ค. ์ ์ฝ๋์ฒ๋ผ ES6์ ๊ฐ์ฒด ๋ฆฌํฐ๋ด๋ก ์ํ์ ํํ๋ฅผ ์ ์ํ๋ค. ๋งค๊ฐ๋ณ์ ๊ฐ์ฒด ์์ ๊ฐ๋ค์ ์ํ์ key๋ก ์ ์ฅ๋๋ค.
// index.js
import { combineReducers } from "redux";
import login from "./loginReducer.js";
const rootReducer = combineReducers({ login });
export default rootReducer;
๋ฆฌ๋์์ ํจ์ ์ด๋ฆ์ combineReducers์ ๋งค๊ฐ๋ณ์ ๊ฐ์ฒด์ ์์ ๋ค์ด๊ฐ๋ค. ์ฆ ์ด ํจ์์ ์ด๋ฆ ์์ฒด๊ฐ state์ key๊ฐ ๋๋ค. ์ํ์ ๊ฐ์ฅ ์์ ์ด๋ฆ์ด ๋๋ค.
// loginReducer.js
import { LOGIN_USER } from "../actions/types.js"; // type์ ๋ณ๋์ ํ์ผ์ ๋ชจ์๋๋ค
const initialState = {
loginType: "BUYER",
typeButtonStyle: {
buyer: true,
seller: false,
},
};
export default function login(state = initialState, action) {
switch (action.type) {
...
case LOGIN_USER:
return {
...state, // ์๋ก์ด ์ ๋ณด๋ก ๋ฎ์ด์ฐ๊ธฐ!
loginType: action.loginType,
token: action.token,
typeButtonStyle: {
buyer: action.buyer,
seller: action.seller,
},
};
default:
return state;
}
}
๋ก๊ทธ์ธ ๋ฆฌ๋์๋ state์ action์ ๋งค๊ฐ๋ณ์๋ก ๋ฐ๋๋ค. ์ฌ๊ธฐ์ state๋ ๊ตฌ state
๋ฅผ ์๋ฏธํ๊ณ action
์ ๋ฐ๊พธ๊ณ ์ ํ๋ ๋ด์ฉ์ ๋ด๊ณ ์๋ค. ์ด ๋์ ์กฐํฉํด์ ์ state
๋ฅผ ๋ง๋ ๋ค. ํ๋งค์ ๋ฒํผ์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ํ๊ณ ํด๋ฆญํ ๋๋ง๋ค ์คํ์ผ์ด ๋ฐ๋๋ ๊ฒ์ ๊ณ ๋ คํด์ ์ํ๋ฅผ ๊ตฌ์กฐํํ๋ค.
์ฒ์์ ๊ตฌ์กฐ๋ฅผ ์ ์ค๊ณํด์ผ ๋ค๋ฅธ ์ฝ๋์์ ์ ๊ฐ์ ธ๋ค ์ธ ์ ์๋ค.
// ์ ๊ฐ๊ตฌ๋ฌธ ์ฌ์ฉ๋ฐฉ์
const data = {
'id': 1,
'name': 'Moon Hee',
'age': 29,
}
console.log({ ...data, 'age': 30 }); // { id: 1, name: 'Moon Hee', age: 30 }
import store from "../../../store.js";
class BuyerButton {
constructor() {
this.button = document.createElement("button");
}
storeType() {
store.dispatch({
type: "LOGIN_USER",
loginType: "BUYER",
buyer: true,
seller: false,
});
}
render() {
...
this.button.addEventListener("click", this.storeType);
store.subscribe(() => {
this.button.style.backgroundColor = store.getState().login
.typeButtonStyle.buyer
? "inherit"
: "#e9e9e9";
this.button.classList.remove(
store.getState().login.typeButtonStyle.buyer ? "help" : "on"
);
this.button.classList.add(
store.getState().login.typeButtonStyle.buyer ? "on" : "help"
);
});
return this.button;
}
}
export default BuyerButton;
dispatch
๋ reducer๋ฅผ ํธ์ถํด์ state๋ฅผ ๋ณ๊ฒฝํ๋ค. ์ ๋ฌํ ๋ด์ฉ์ ์ด๋ค state๋ฅผ ๋ฐ๊ฟ์ผํ ์ง ์๋ ค์ฃผ๋ type
๊ณผ ๋ฐ๊พธ๊ณ ์ถ์ ๋ด์ฉ์ ๊ฐ์ฒด์ ๋ด์์ ๋ณด๋ธ๋ค. ๋ฆฌ์กํธ๋ useDispatch๋ฅผ ์ฌ์ฉํ์ง๋ง ๋ฐ๋๋ผ์์๋ store.dispatch()
ํ์์ ์ฌ์ฉํ๋ค.
subscribe๋ ์คํ ์ด์ ์ํ๊ฐ ๋ฐ๋ ๋๋ง๋ค ์ธ์์ ํจ์๋ฅผ ์คํํ๋ค. ๊ตฌ๋งค์ ๋ฒํผ์ ๋๋ฅด๊ฑฐ๋ ํ๋งค์ ๋ฒํผ์ ๋๋ฅผ ๋ dispatch๊ฐ ์คํ๋๊ณ ์ด๊ฒ์ด ์คํ ์ด์ ์ํ๋ฅผ ๋ณ๊ฒฝ์ํค๋ฉด ๊ทธ ๋ณ๊ฒฝ์ฌํญ์ ์ ์ฉ์์ผ์ผ ํ ๋ ์ฌ์ฉํ๋ฉด ๋๋ค.
์๋ํ๋ ์ฝ๋๋ฅผ ์ฐพ๊ธฐ ์ํด์ ๋ถ๋ชจ ์ปดํฌ๋ํธ์ ์์ ์ปดํฌ๋ํธ๋ฅผ ๋๋๋ค๋ฉด์ ์ฝ๋ฉํ๊ณ ๋ ๋๋ง ์์ ์ ๋ฐ์ ธ์ setTimeout๋ฅผ ์ฌ์ฉํ๊ธฐ๋ ํ๋ค. Redux๋ฅผ ์ฌ์ฉํ๋ฉด ์ผํ๋ ์ปดํฌ๋ํธ์ ์ง์ ์ฝ๋ฉํ ์ ์์ด์ ๊ฐ๋ ์ฑ์ด ์ข์์ง๋ค. ๋ก๊ทธ์ธ ํ์ ์ปดํฌ๋ํธ์์ 33์ค์ด ์ค์ด๋ค์๊ณ ๊ทธ๋ฅ ์งง์์ง๊ฒ ์๋๋ผ ๋น์ฆ๋์ค ๋ก์ง์ ์ฃผ์ธ์๊ฒ ์ฝ๋๋ฅผ ์ธ ์ ์์ด์ ๋งค์ฐ๋งค์ฐ ์ข์์ก๋ค.