1. Redux๋?
์ํ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ, ์ค์ state ๊ด๋ฆฌ์๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก ๋์์ฃผ๋ ํจํค์ง
์ปดํฌ๋ํธ ๊ฐ์ state ๋ฐ์ดํฐ๋ค์ ๋ถ๋ชจ-์์ ๊ด๊ณ๋ก ์ค์ ํ์ฌ props๋ฅผ ํตํด ํ์ ์ ์ผ๋ก ์ ๋ฌํ์ง ์๊ณ , ์ค์์์ ๊ด๋ฆฌํ์ฌ ์ด๋ค ์ปดํฌ๋ํธ์์๋ ์ฝ๊ฒ ์ ๊ทผํ๊ณ ์ ์ดํ ์ ์๋๋ก ํด์ค๋ค.
1) Redux์ ํ๋ฆ
- View์์ ์ก์ ์ด ์ผ์ด๋๋ค.
- dispatch์์ action์ด ์ผ์ด๋๊ฒ ๋๋ค.
- action์ ์ํ reducerํจ์๊ฐ ์คํ๋๊ธฐ ์ ์ middleware๊ฐ ์๋ํ๋ค.
- middleware์์ ๋ช ๋ น๋ด๋ฆฐ ์ผ์ ์ํํ๊ณ ๋ ๋ค, reducerํจ์๋ฅผ ์คํํ๋ค.
- reducer์ ์คํ๊ฒฐ๊ณผ store์ ์๋ก์ด ๊ฐ์ ์ ์ฅํ๋ค.
- store์ state์ subscribeํ๊ณ ์๋ UI์ ๋ณ๊ฒฝ๋ ๊ฐ์ ์ค๋ค.
2) Provider
- Redux์ ๊ฐ์ฅ ๊ธฐ๋ณธ ์์๋ก์, Redux๋ฅผ ์ฌ์ฉํ๋ ์ด์ ๋ state๋ฅผ ๊ด๋ฆฌํ๊ณ Globalํ๊ฒ ์ฌ์ฉํ๊ธฐ ์ํจ
- ์ด๋ ๊ฒ ํ๊ธฐ ์ํด์๋ ๊ฐ์ฅ ์ต์์ ์ปดํฌ๋ํธ์ Provider ์ปดํฌ๋ํธ๋ฅผ ๋ถ๋ชจ ์ปดํฌ๋ํธ๋ก ๋ง๋ค์ด์ค
- ๋ณดํต CRA๋ฅผ ํตํด์ ๋ฆฌ์กํธ ์ฑ์ ๋ง๋ค๊ฒ ๋๋ฉด App ์ปดํฌ๋ํธ๊ฐ ๊ฐ์ฅ ์์ ์ปดํฌ๋ํธ๊ฐ ๋๋๋ฐ, App ์ปดํฌ๋ํธ ๋ด๋ถ์ ์ปดํฌ๋ํธ๋ค์ด store๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ํ๊ธฐ ์ํด <Provider />๋ก wrapping ํด์ฃผ์ด์ผ ํจ
3) Action
- state์ ์ด๋ค ๋ณํ๊ฐ ํ์ํ๊ฒ ๋ ๋, action์ด๋ผ๋ ๊ฒ์ ๋ฐ์์ํด
- ์์ JS ๊ฐ์ฒด์ ํํ์ด๋ฉฐ, action type๊ณผ payload ๋ ๋งค๊ฐ๋ณ์๋ฅผ ๊ฐ๋๋ค
- type์ ํ์, payload๋ ์ต์
- action ๊ฐ์ฒด๋ action type์ payload๋งํผ ์ฒ๋ฆฌ
4) Action Creator
- action ๊ฐ์ฒด๋ฅผ ๋ฆฌํดํ๋ ํจ์
- ๋จ์ํ ๋งค๊ฐ๋ณ์๋ฅผ ๋ฐ์์์ ์ก์ ๊ฐ์ฒด ํํ๋ก ๋ง๋ค์ด์ฃผ๋ ์ญํ
- Action Creator๋ฅผ ์ฌ์ฉํ๋ ์ด์ ๋, ๋์ค์ ์ปดํฌ๋ํธ์์ ๋์ฑ ์ฝ๊ฒ ์ก์ ์ ๋ฐ์์ํค๊ธฐ ์ํด์๋ค. ๊ทธ๋์ ๋ณดํต exportํ์ฌ ๋ค๋ฅธ ํ์ผ์์ ๋ถ๋ฌ์์ ์ฌ์ฉ
5) Reducer
- state์ ๋ณํ๋ฅผ ์ผ์ผํค๋ ํจ์
- ํ์ฌ์ state๋ฅผ action์ type์ ๋ฐ๋ผ ๋ณ๊ฒฝํ๋ ํจ์๋ก, input์ state์ action ๋ ํ๋ผ๋ฏธํฐ๋ฅผ ๊ฐ๋๋ค
const initialState = { number: 0 }
const reducerName = (state = initialState, action) => {
switch (action.type) {
case CASE_ONE:
return { number: state.number +1 };
case CASE_TWO:
return { number: state.number -1 };
default:
return state;
}
};
- Action์ ์คํํ๋ ํจ์
6) ๊ทธ ์ธ
(1) Store
- State๋ฅผ ์ ์ฅํ๊ณ ๊ด๋ฆฌํ ์ ์๋ ์ค์ state ๊ด๋ฆฌ์
(2) Dispatch
- store์ ๋ด์ฅํจ์
- action์ reducer์๊ฒ ์ ๋ฌํ์ฌ action์ ๋ฐ์์ํค๋ ๊ฒ
(3) Subscribe
- store ๋ด์ฅํจ์
- subscribe ํจ์์ ํน์ ํจ์๋ฅผ ์ ๋ฌํด์ฃผ๋ฉด, action์ด dispatch ๋์์ ๋๋ง๋ค ์ ๋ฌํด ์ค ํจ์๊ฐ ํธ์ถ
- React์์๋ useSelector Hook ๋๋ connect ํจ์ ์ฌ์ฉ
2. ํ๋ก์ ํธ์ ์ค์น ํ ๊ธฐ๋ณธ ์ธํ ๊ณผ์
1) ์ค์น
# Yarn
yarn add redux
yarn add react-redux
# ์์ 2๊ฐ๋ฅผ ํ ์ค๋ก ์ค์น
yarn add redux react-redux
# NPM
npm install @reduxjs/toolkit
2) ํด๋ ๊ตฌ์กฐ
๐ฆsrc
โฃ ๐redux : redux ๊ด๋ จ ์ฝ๋ ํ์ผ
โ โฃ ๐config : redux ์ค์ ๊ด๋ จ ํ์ผ(redux ํด๋ ๋ด๋ถ์ config ํด)
โ โ โ ๐configStore.js : configStore : ์ค์ state ๊ด๋ฆฌ์ -> ์ค์ ์ฝ๋ (.js) ํ์ผ
โ โ ๐modules : modules : state์ ๊ทธ๋ฃน (ex. Todo-list ํ๋ก์ ํธ๋ฅผ ๋ง๋ ๋ค๋ฉด todo.js ํ์ผ)
โ โ โ ๐todos.js
โฃ ๐components
โฃ ๐index.js
โ ๐App.jsx
3) configStore.js
// configStore.js -> ์ค์ ๋ฐ์ดํฐ ๊ด๋ฆฌ์(store)๋ฅผ ์ค์ ํ๋ ํ์ผ
import { createStore } from "redux";
import { combineReducers } from "redux";
import todos from "../modules/todos";
// rootReducer: reducer๋ค์ ๋ชจ์์ ํ๋๋ก ๋ง๋ค์ด ๋์ ๊ธฐ๋ณธ reducer
const rootReducer = combineReducers({
// ๊ฐ์ฒด์์ key-value๊ฐ ๊ฐ์ผ๋ฉด ์๋ตํด์ 1๋ฒ๋ง ์์ฑ ๊ฐ๋ฅ
// todos: todos,
todos,
});
const store = createStore(rootReducer);
// ์์์ ๋ง๋ store๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด ๋ฐ์ผ๋ก export
export default store;
4) index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
// ์๋ ๋ ๊ฐ import
import { Provider } from "react-redux";
import store from "./redux/config/configStore";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
// provider: ์์์ ๋ง๋ค์๋ store์ ๊ธฐ๋ฐ์ผ๋ก ์ง๋ฐฐ๊ถ์ ํ์ฌํ๋๋ก ํ๋ ๊ฒ
// <Provider>๋ก <App /> ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ธ๋ฉด,
// App ์ปดํฌ๋ํธ๊ฐ Provider์ ์ง๋ฐฐ๊ถ ์์ผ๋ก ๋ค์ด์ด
<Provider store={store}>
<App />
</Provider>
);
5) ๋ชจ๋ ํ์ผ(todos.js ์์)
Ducks pattern
- ๋ชจ๋ํ์ผ์ ducks pattern์ผ๋ก ์์ฑํ๋ค
Ducks pattern์ด๋?
1. Reducer ํจ์๋ฅผ export default ํ๋ค.
2. Action Creator ํจ์๋ค์ export ํ๋ค.
3. Action type์ app/reducer/ACTION_TYPE ํํ๋ก ์์ฑํ๋ค.
โก๏ธ ๋ชจ๋ ํ์ผ 1๊ฐ์ Action type, Action Creator, Reducer๊ฐ ๋ชจ๋ ์กด์ฌํ๋ ์์ฑ ๋ฐฉ์
// Action
// 2๊ฐ์ input - 1. type, 2. payload
// type์ ์ฒ๋ฆฌ๋ฅผ payload๋งํผ ํด์ค๋ค
// Action values
// human error๋ฅผ ์ค์ด๊ธฐ ์ํด์ action type์ ๋ณ์ํํด์ฃผ๋ ๊ฒ
export const ADD_TODO = "ADD_TODO";
export const DELETE_TODO = "DELETE_TODO";
export const ISDONE_TODO = "ISDONE_TODO";
// Action Creator
export const add_todo = (todo) => {
return {
type: ADD_TODO,
todo: {
id: todo.id,
title: todo.title,
content: todo.content,
isDone: todo.isDone
},
};
};
export const delete_todo = (id) => {
return {
type: DELETE_TODO,
id,
};
};
export const isdone_todo = (isDone) => {
return {
type: ISDONE_TODO,
isDone,
};
};
// state์ ์ด๊ธฐ ์ํ๊ฐ
const initialState = {
todos: [
{
id: uuid(),
title: "์ ๋
์ ํฌ์ค์ฅ ๊ฐ๊ธฐ",
content: "์จ์ดํธ ์ด๋ 30๋ถ ๊ผญ ํ๊ธฐ",
isDone: false,
},
{
id: uuid(),
title: "๋ฆฌ์กํธ ์์ ํ๊ธฐ",
content: "to-do list ๋ง๋ค๊ธฐ",
isDone: true,
},
],
};
// Reducer
// 2๊ฐ์ input - 1. state, 2. action
// output - ์๋ก์ด store
// export ํด์ configStore์ combineReducers์ ๋ชฉ๋ก์ ๋ฃ์ด์ฃผ๊ธฐ
export const todos = (state = initialState, action) => {
// if๋ฌธ ๋๋ switch๋ฌธ์ผ๋ก ์ก์
์ ์ง์
switch (action.type) {
case ADD_TODO:
return {...state,
todos: [...state.todos, action.todo],
};
case DELETE_TODO:
return {...state,
todos: [...state.todos.filter((todo)=>todo.id !== action.id)],
};
case ISDONE_TODO:
return {
todos: state.todos.map((todo) => {
if (todo.id === action.isDone) {
return { ...todo, isDone: !todo.isDone };
} else {
return todo;
}
})
};
default:
return state;
}
};
6) App.jsx
import { useSelector } from "react-redux";
const App = () => {
// ์ฌ๊ธฐ์์ store์ ์ ๊ทผํ์ฌ, counter์ ๊ฐ์ ์ฝ์ด์ค๊ณ ์ถ๋ค
// Redux Hook ์ฌ์ฉ - useSelector
// state: ์ค์ ์ ์ฅ์ state ์ ์ฒด
const data = useSelector((state) => {
return state;
});
console.log(data);
return <div>Redux!!</div>;
};
export default App;