
Redux ๋ฅผ ์์ ์ ๋ณต์ตํ๋ ๊ฒ์๊ธ์ด ์๋ค. ์ฌ์ง์ด ์ด๊ฒ๋ ์๋นํ ์ค๋๋...
์ ํํ๊ฒ! ๊ฐ๋
์ ์์๊ฐ์ผ ํดํท์ด๊ณ ๋ญ๊ณ ๋ ์ฝ๋ค๊ณ ๋ค ํ๋ zustand, recoil ๋ฑ๋ฑ ํ ์ ์์ ๊ฒ ๊ฐ์์ ๋ค์ ์ฒ์๋ถํฐ ์ ๋ฆฌ ์์.์ด์ ๊ทธ๋ฅ ~ ์ด๋ ๊ฒ ์ฐ๋ ๊ฑฐ๋ผ~ ๋ค๋ค ์ด๋ ๊ฒ ์ฐ๋๊น~ ๊ฐ ์๋๊ณ ์ ๋๋ก ์ข ์๊ณ ์จ๋ณด์ ..

- Store ๏ผ์ํ๋ฅผ ๊ด๋ฆฌํ๋ ๊ณณ, ์ฆ ์ ์ฅ์
- Reducer ๏ผ์ํ๋ฅผ ๋ณํ์ํค๋ ๊ณณ, ์ฆ ๋ณ์๋ฅผ ์ ์ฅํ๋ ๊ณณ
- Actions ๏ผ์ํ๋ฅผ ๋ณํ์ํค๋ ํจ์, ์ก์ ์ ์ทจํ๋ ๊ณณ
npm install @reduxjs/toolkit react-redux
๋ ์ด๊ฑฐ ์ค์น ์ ํ๊ณ ์๋ ์ ๋ ์ ๋ ๊น..? ํ ์ ์์ ใ
_ใ
์ด๊ฑฐ ์ค์น ์ํ๋ฉด ์ค๋ฅ ๋ฌ๋ค๊ณ ๋ง๋ ์ ํด์ฃผ๋(ํธ์๊ฐ ๊ณ์ ๋๋ฉด ๋๋ฆฌ์ธ ์ค ์๋ค..) ๊ผญ ๊ผญ ์ค์นํ๊ณ ์งํํ์..

Counter ์ค์ต
์ด์ ๋จ๋ค ๋ค ํ๋ค๋ counter ์ค์ต์ ํด๋ณด์
์คํ ์ด ์์ฑ
src/app/store.js
์ผ๋จ store๋ฅผ ์์ฑํ๊ณ , ๊ทธ ์์ { configureStore } API๋ฅผ ๊ฐ์ ธ์จ๋ค.
import { configureStore } from "@reduxjs/toolkit";
const store = configureStore({
reducer: {},
});
export default store;
์ด ๊ณผ์ ์์ Redux ์ ์ฅ์๊ฐ ์์ฑ๋๊ณ ,
์ ์ฅ์๋ฅผ ๊ฒ์ฌํ ์ ์๋๋ก Redux DevTools ํ์ฅ๋ ์๋์ผ๋ก ๊ตฌ์ฑ๋๋ค. (์ด๊ฒ ๋ฆฌ๋์ค์ ๋ฆฌ๋์ค ํดํท์ ์ฐจ์ด!)
configureStore
configureStore๏ผ๊ธฐ์กด createStore์ ๋ฌ๋ฆฌ, ์ฌ๋ฌ ๊ฐ์ ์ธ์ ๋์ ์ ์ด๋ฆ์ด ์ง์ ๋ ํ๋์ object๋ฅผ ๋ฐ๊ณ , reducer์ ๋๊ฒจ ์ค๋ค.
์คํ ์ด ์ ๊ณต
index.js ํ์ผ์์ Store, Provider ๋ฅผ import ํด์ค๊ณ <App />์ <Provider>๋ก ๊ฐ์ธ store๋ฅผ props๋ก ์ ๋ฌํด์ค๋ค. (importํด์จ store๋ฅผ Provider ์ปดํฌ๋ํธ์ ์์ฑ๊ฐ์ผ๋ก ์ค์ )
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import store from './app/store'
import { Provider } from 'react-redux'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
์ฌ๊ธฐ์ ์ค๊ดํธ ์ค๋ฅ๊ฐ ์์๋๋ฐ, ์ฌ๊ธฐ๋ ๋์ค์ ํ ํฌ์คํ ์ ์ ์ด ๋์ผ๋ ค๊ตฌ ํจ.
์ฌ๋ผ์ด์ค ์์ฑ
Slice๋ฅผ ๋ง๋ค์ด์ค์ action, reducer์ ๋์์ ์จ์ค๋ค.
counterSlice.js ํ์ผ์ ๋ง๋ค์ด์, { createSlice } API๋ฅผ ๊ฐ์ ธ์จ๋ค.
์ฌ๋ผ์ด์ค๋ฅผ ์์ฑํ๋ ค๋ฉด ์ฌ๋ผ์ด์ค๋ฅผ ์๋ณํ๊ธฐ ์ํ ๋ฌธ์์ด ์ด๋ฆ, ์ด๊ธฐ ์ํ ๊ฐ, ์ํ ์
๋ฐ์ดํธ ๋ฐฉ๋ฒ์ ์ ์ํ๋ ํ๋ ์ด์์ ๋ฆฌ๋์ ํจ์๊ฐ ํ์ํ๋ค.
์ฌ๋ผ์ด์ค๊ฐ ์์ฑ๋๋ฉด ์์ฑ๋ Redux ์ก์
์์ฑ์์ ์ ์ฒด ์ฌ๋ผ์ด์ค์ ๋ํ ๋ฆฌ๋์ ๊ธฐ๋ฅ์ ๋ด๋ณด๋ผ ์ ์๋ค.
src/features/counter/counterSlice.js
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
value: 0,
};
export const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action) => {
state.value += action.payload;
},
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
initialState๋ state ์ด๊ธฐ๊ฐ
reducers๋ setState๋ผ๊ณ ์๊ฐํ๋ฉด ํธํ๋ค.
increment, decrement, incrementByAmount๊ฐ์ ํจ์๋ฅผ action์ด๋ผ๊ณ ํ๋ค.
state์ action ์ด ๋๊ฐ์ ์ธ์๋ฅผ ๋ฐ์์จ๋ค.
state๋ ํ์ฌ state๊ฐ์ด๊ณ ,
action์ reducer ํจ์๋ฅผ ๊ฑฐ์ณ ์๋ก์ด state๋ฅผ ๋ฆฌํด ํ๋ค.
์ฌ๊ธฐ์ reducer๋ฅผ ๋ฆฌํดํจ์ผ๋ก์จ
์ฌ๋ผ์ด์ค์ ์ ๊ณต๋ ์ด๊ธฐ ์ํ ๊ฐ์ ๋ํ ์ก์ธ์ค๋ฅผ ์ ๊ณตํ๊ณ , ์ง์ฐ ์ํ ์ด๊ธฐํ๊ฐ ์ ๊ณต๋๋ฉด ํธ์ถ๋๊ณ ์๋ก์ด ๊ฐ์ด ๋ฐํ๋๋ค.
createSlice
- name : ํด๋น ๋ชจ๋์ ์ด๋ฆ์ ์์ฑ
- initialState : ํด๋น ๋ชจ๋์ ์ด๊ธฐ๊ฐ ์ธํ
- reducers : ๋ฆฌ๋์๋ฅผ ์์ฑ. ์ด๋ ํด๋น ๋ฆฌ๋์์ ํค๊ฐ์ผ๋ก ์ก์ ํจ์๊ฐ ์๋์ผ๋ก ์์ฑ
- extraReducers : ์ก์ ํจ์๊ฐ ์๋์ผ๋ก ์์ฑ๋์ง ์๋ ๋ณ๋์ ์ก์ ํจ์๊ฐ ์กด์ฌํ๋ ๋ฆฌ๋์๋ฅผ ์ ์
Redux Toolkit์ createSlice ๋ฐ createReducer API๋ ์ฌ๋ฐ๋ฅธ ๋ณ๊ฒฝ ๋ถ๊ฐ๋ฅํ ์ ๋ฐ์ดํธ๊ฐ "๋ณ๊ฒฝ๋๋" ์ ๋ฐ์ดํธ ๋ก์ง์ ์์ฑํ ์ ์๊ฒํ๋ค. (์ด๊ฒ๋ ๋ฆฌ๋์ค์์ ์ฐจ์ด)
์คํ ์ด์ ์ฌ๋ผ์ด์ค ๋ฆฌ๋์ ์ถ๊ฐ
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "../features/counter/counterSlice";
const store = configureStore({
reducer: {
counter: counterReducer,
},
});
export default store;
์์์ ์์ฑํ counterSlice์์ ๋ฆฌ๋์ ํจ์๋ฅผ ๊ฐ์ ธ์์ ์คํ ์ด์ ์ถ๊ฐํด์ผ ํ๋ค.
๋ฆฌ๋์ ๋งค๊ฐ๋ณ์ ๋ด๋ถ์ ํ๋๋ฅผ ์ ์ํจ์ผ๋ก์จ store์ ์ด ์ฌ๋ผ์ด์ค ๋ฆฌ๋์ ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ํด๋น ์ํ์ ๋ํ ๋ชจ๋ ์
๋ฐ์ดํธ๋ฅผ ์ฒ๋ฆฌํ๋๋ก ์ง์ํ๋ค.
๋ฆฌ๋์ค ์ํ ๋ฐ ์์
์ด์ React-Redux Hook๋ฅผ ์ฌ์ฉํ์ฌ React ๊ตฌ์ฑ ์์๊ฐ Redux ์ ์ฅ์์ ์ํธ ์์ฉํ๋๋ก ํ ์ ์๋ค.
useSelector๋ฅผ ์ฌ์ฉํ์ฌ ์ ์ฅ์์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ
useDispatch๋ฅผ ์ฌ์ฉํ์ฌ ์์
์ ์ ๋ฌํ ์ ์๋ค.
๋ด๋ถ์ <Counter> ๊ตฌ์ฑ ์์๊ฐ ์๋ Counter.js ํ์ผ์ ๋ง๋ ๋ค์ ํด๋น ๊ตฌ์ฑ ์์๋ฅผ App.js๋ก ๊ฐ์ ธ์ค๊ณ <App> ๋ด๋ถ์์ ๋ ๋๋ง.
src/features/counter/Counter.js
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { decrement, increment } from "./counterSlice";
export function Counter() {
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<div style={{ margin: "40px" }}>
<button
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
Increment
</button>
<span>{count}</span>
<button
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
Decrement
</button>
</div>
</div>
);
}
useDispatch๋ state๋ณ๊ฒฝ ํจ์ ์ฌ์ฉ ํ ์ ๋ฌํด์ฃผ๊ธฐ ์ํด import
useSelector์ store.js์์ ์ํ๊ด๋ฆฌํ๊ธฐ ์ํด import ํ ๊ฒ์ด๋ค.
๊ทธ๋ฆฌ๊ณ counterSlice์์ ๋ง๋ state๋ณ๊ฒฝ ํจ์๋ค์ ์ฌ์ฉํ๊ธฐ ์ํด์ import ํด์ฃผ์๋ค.
dispatch ๋ณ์๋ฅผ ๋ง๋ค์ด์ useDispatch๋ฅผ ๋ด์์ฃผ๊ณ
dispatch(state๋ณ๊ฒฝํจ์()) ํด์ ์ฌ์ฉํด์ฃผ๋ฉด ๋๋ค.
counterSlice์์ ๋ง๋
increment(state, action) -> action์๋ฆฌ์
dispatch(increment(action์๋ฆฌ))๋ฅผ ๋ฐ์์ useDispatch๋ฅผ ํตํด ์ ๋ฌํด์ค๋ค.
๊ทธ๋ฆฌ๊ณ ๊ผญ action์ธ์๋ฅผ ๋ฐ์์ฌ๋ action.payload๋ฅผ ๋ถ์ฌ ์จ์ฃผ์ด์ผ ํ๋ค.
์ด์ "์ฆ๊ฐ" ๋ฐ "๊ฐ์" ๋ฒํผ์ ํด๋ฆญํ ๋๋ง๋ค
- ํด๋น Redux ์์ ์ด ์คํ ์ด๋ก ๋ฐ์ก๋๋ค.
- ์นด์ดํฐ ์ฌ๋ผ์ด์ค ๋ฆฌ๋์๋ ์์ ์ ๋ณด๊ณ ์ํ๋ฅผ ์ ๋ฐ์ดํธํ๋ค.
- <Counter> ๊ตฌ์ฑ ์์๋ ์ ์ฅ์์ ์ ์ํ ๊ฐ์ ๋ณด๊ณ ์ ๋ฐ์ดํฐ๋ก ์์ฒด๋ฅผ ๋ค์ ๋ ๋๋งํ๋ค.

console.log("count");
console.log(count);

์ฌ๋ฌ ๊ฐ์ ๋ฆฌ๋์ ๋ง๋ค๊ธฐ
store์ reducer๋ฅผ ์ฌ๋ฌ ๊ฐ ๋ฃ๊ณ ๊ด๋ฆฌํ๋ ๊ฒ์ ์ด๋ป๊ฒ ํ๋์ง ๊ถ๊ธํด์
๋ ๋ฐ๋ก ์งํํ๊ฒ ๋์๋ค ใ
_ใ
์ซ์ ์์ ๋ฐ๊พธ๊ธฐ ๐จ
features ํด๋์ ChangeColor.js ํ์ผ์ ๋ง๋ ๋ค.
src/features/color/ChangeColor.js
import React, { useState } from "react";
function ChangeColor() {
const [color, setColor] = useState("");
return (
<div>
<input
type="text"
onChange={(e) => {
setColor(e.target.value);
}}
/>
<button>CHANGE COLOR</button>
</div>
);
}
export default ChangeColor;
๋ฆฌ๋์ ์ถ๊ฐ
theme.js๋ผ๋ ํ์ผ์ ๋ง๋ ๋ค.
src/features/color/theme.js
import { createSlice } from '@reduxjs/toolkit'
// initialState๋ ๋ฆฌ๋์๋ฅผ ์ฐ๋ค๋ณด๋ฉด ์์ฃผ ๊ฑด๋ค๊ฒ ๋๋๋ฐ, ๋งค๋ฒ ์จ์ฃผ๋ค ๋ณด๋ฉด ๊ท์ฐฎ์์ ๋ฐ๋ก ๋นผ์ฃผ๋ ์์
// ์ฌ๊ธฐ๋ ์ด์ฐจํผ ํ๋๋ฐ์ ์์ง๋ง, ๊ฐ์ฒด๊ฐ ๋ง์ ๋๋ ์ ์ฉํ๋ค.
// string์ ๋๊ธธ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ""
const initialStateValue = "";
export const themeSlice = createSlice({
name: "theme",
initialState: { value: initialStateValue },
reducers: {
changeColor: ( state, action ) => {
state.value = action.payload
},
},
});
export const { changeColor } = themeSlice.actions;
export default themeSlice.reducer;
์คํ ์ด์ ์ถ๊ฐ
src/app/store.js
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from "../features/counter/counterSlice";
import themeReducer from "../features/color/theme";
export default configureStore({
reducer:{
counter: counterReducer,,
theme: themeReducer
}
})
์ ๋ณ๊ฒฝ ๊ธฐ๋ฅ ์ถ๊ฐ
์ซ์์ ์์ด ๋ฐ๋๊ฒ ํ๊ณ ์ถ์ผ๋ div์ ์คํ์ผ์ ํ๋ ์ค์ ์ธํ์ ๊ฐ์ด css ์์๋ก ๋ค์ด๊ฐ๊ฒ ํด์ผ ํ๋ค.
์ผ๋จ ChangeColor ์์ ๋ฒํผ ๊ธฐ๋ฅ + dispatch (๋ ์์ ํ๋ฐ!) ์ถ๊ฐ.
src/features/color/ChangeColor.js
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { changeColor } from "./theme";
function ChangeColor() {
const [color, setColor] = useState("");
const dispatch = useDispatch();
console.log(color);
return (
<div>
<input
type="text"
onChange={(e) => {
setColor(e.target.value);
}}
/>
<button
onClick={() => {
dispatch(changeColor(color));
}}
>
CHANGE COLOR
</button>
</div>
);
}
export default ChangeColor;
์ด๊ฑด ์ด์ App.js์ ์ถ๊ฐํด์ฃผ๊ณ ,
App.js
import React from "react";
import ChangeColor from "./features/color/ChangeColor";
import { Counter } from "./features/counter/Counter";
function App() {
return (
<div>
<Counter />
<ChangeColor />
</div>
);
}
export default App;
Counter ์ ์๋ ์ ๋ค ์์์ ๋ฐ๊ฟ ๊ฑฐ๋๊น,
์๊น Counter์ ์ถ๊ฐํด์ค๋ค.
src/features/counter/Counter.js
...
const themeColor = useSelector((state) => state.theme.value);
...
return (
<div>
<div style={{ margin: "40px", color: themeColor }}>
๋ฒํผ์ ๋๋ฌ๋ณด์.
...
</div>


input ์ฐฝ์ css์์ ์ฌ์ฉํ๋ ์์๋ช ์ ์๋ฌด๊ฑฐ๋ ์ ์ผ๋ฉด ๊ทธ๊ฑธ๋ก ๋ฐ๋๋ค.
'TIL > ์ํ๊ด๋ฆฌ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[TIL] Recoil (1) | 2024.01.05 |
---|---|
[TIL] React Context (0) | 2023.09.22 |
[TIL] Redux-Toolkit 2 - Main Project (0) | 2023.05.19 |