Redux๋ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก, ์ปดํฌ๋ํธ ๊ฐ ์ํ๋ฅผ ์ ์ญ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์๋๋ก ๋์์ค๋ค.
โข store: ์ ์ฒด ์ํ๋ฅผ ๊ด๋ฆฌํ๋ ์ ์ฅ์
โข action: ์ํ๋ฅผ ๋ณ๊ฒฝํ๊ธฐ ์ํ ๊ฐ์ฒด
โข reducer: ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ ๋ก์ง์ ๊ฐ์ง ํจ์
โข dispatch: ์ก์ ์ ๋ฐ์์ํค๋ ํจ์
โข useSelector(), useDispatch(): React ์ปดํฌ๋ํธ์์ Redux ์ํ๋ฅผ ๊ฐ์ ธ์ค๊ฑฐ๋ ๋ณ๊ฒฝํ๋ Hook
โ ๊ธฐ๋ณธ ์ค์
import { createStore } from "redux"; import { Provider } from "react-redux"; import rootReducer from "./reducers"; const store = createStore(rootReducer); function App() { return ( <Provider store={store}> <TodoList /> </Provider> ); }โ ์ก์ & ๋ฆฌ๋์ ์ค์
const ADD_TODO = "ADD_TODO"; const REMOVE_TODO = "REMOVE_TODO"; export const addTodo = (text) => ({ type: ADD_TODO, text }); export const removeTodo = (id) => ({ type: REMOVE_TODO, id }); const initialState = { todos: [] }; function todoReducer(state = initialState, action) { switch (action.type) { case ADD_TODO: return { ...state, todos: [...state.todos, { id: Date.now(), text: action.text }] }; case REMOVE_TODO: return { ...state, todos: state.todos.filter((todo) => todo.id !== action.id) }; default: return state; } }โ Redux๋ฅผ ํ์ฉํ Todo ์ปดํฌ๋ํธ
import { useSelector, useDispatch } from "react-redux"; import { addTodo, removeTodo } from "../redux/todoSlice"; import { useState } from "react"; function TodoList() { const todos = useSelector((state) => state.todos); const dispatch = useDispatch(); const [text, setText] = useState(""); return ( <div> <input value={text} onChange={(e) => setText(e.target.value)} /> <button onClick={() => dispatch(addTodo(text))}>์ถ๊ฐ</button> <ul> {todos.map((todo) => ( <li key={todo.id}> {todo.text} <button onClick={() => dispatch(removeTodo(todo.id))}>์ญ์ </button> </li> ))} </ul> </div> ); }โ ๋ฐฐ์ด ์
โข Redux์ ๊ธฐ์ด ๊ฐ๋ ๊ณผ ๋์ ๋ฐฉ์ ์ดํด
โข useSelector๋ก ์ํ ์กฐํ, useDispatch๋ก ์ก์ ์ ์ ๋ฌํ๋ ๋ฐฉ์ ํ์ต
โข ์ ์ญ ์ํ๋ฅผ ํ์ฉํด ๋ถ๋ชจ-์์ ๊ฐ props ์ ๋ฌ ์์ด ์ํ ๊ณต์
โ RTK๋?
Redux๋ฅผ ์ฝ๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ๋์์ฃผ๋ Redux ๊ณต์ ํดํท.
๊ธฐ์กด Redux์ ๋ณต์กํ ๋ณด์ผ๋ฌํ๋ ์ดํธ ์ฝ๋๋ฅผ ์ค์ด๊ณ ๋ ์ง๊ด์ ์ธ API ์ ๊ณต.โ RTK ์ฃผ์ ๊ฐ๋
1. configureStore() โ createStore๋ณด๋ค ๋ ๊ฐํธํ ์ค์
2. createSlice() โ ์ก์ & ๋ฆฌ๋์ ์ฝ๋ ์๋ ์์ฑ
3. useSelector() & useDispatch() โ ์ํ ์กฐํ ๋ฐ ์ ๋ฐ์ดํธ
โ RTK ๋ฐฉ์์ผ๋ก todoSlice.jsx ์์ฑ
import { createSlice } from "@reduxjs/toolkit"; const todoSlice = createSlice({ name: "todos", initialState: [], reducers: { addTodo: (state, action) => { state.push({ id: Date.now(), text: action.payload }); }, removeTodo: (state, action) => { return state.filter((todo) => todo.id !== action.payload); }, }, }); export const { addTodo, removeTodo } = todoSlice.actions; export default todoSlice.reducer;โ store ์ค์
import { configureStore } from "@reduxjs/toolkit"; import todoReducer from "./todoSlice"; const store = configureStore({ reducer: { todos: todoReducer }, }); export default store;โ RTK ๊ธฐ๋ฐ TodoList ์ปดํฌ๋ํธ
import { useSelector, useDispatch } from "react-redux"; import { addTodo, removeTodo } from "../redux/todoSlice"; import { useState } from "react"; function TodoList() { const todos = useSelector((state) => state.todos); const dispatch = useDispatch(); const [text, setText] = useState(""); return ( <div> <input value={text} onChange={(e) => setText(e.target.value)} /> <button onClick={() => dispatch(addTodo(text))}>์ถ๊ฐ</button> <ul> {todos.map((todo) => ( <li key={todo.id}> {todo.text} <button onClick={() => dispatch(removeTodo(todo.id))}>์ญ์ </button> </li> ))} </ul> </div> ); }โ ๋ฐฐ์ด ์
โข createSlice()๋ฅผ ํ์ฉํ๋ฉด ์ก์ & ๋ฆฌ๋์ ์ฝ๋๊ฐ ํจ์ฌ ๊ฐ๊ฒฐํด์ง
โข configureStore()๋ฅผ ์ฌ์ฉํ๋ฉด ์๋์ผ๋ก Redux DevTools๊ฐ ์ค์ ๋จ
โข ๊ธฐ์กด Redux๋ณด๋ค RTK๊ฐ ํจ์ฌ ์ง๊ด์ ์ด๊ณ ์ฌ์ฉํ๊ธฐ ์ฌ์
โ RTK์ immer ํ์ฉ
RTK์ createSlice() ๋ด๋ถ์์ ๋ถ๋ณ์ฑ์ ์๋์ผ๋ก ๊ด๋ฆฌํด์ค.
๊ธฐ์กด Redux์ฒ๋ผ spread ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ์ง ์์๋ ๋จ.
โ ๊ธฐ์กด Redux ๋ฐฉ์case ADD_TODO: return { ...state, todos: [...state.todos, { id: action.id, text: action.text }] };โ RTK ๋ฐฉ์ (๋ถ๋ณ์ฑ ์๋ ๊ด๋ฆฌ)
addTodo: (state, action) => { state.push({ id: Date.now(), text: action.payload }); },
โ ๋น๋๊ธฐ ์ฒ๋ฆฌ - createAsyncThunk
RTK์์๋ createAsyncThunk๋ฅผ ์ฌ์ฉํ๋ฉด ๋น๋๊ธฐ ์ก์ ์ ์ฝ๊ฒ ๊ด๋ฆฌํ ์ ์์
์๋ฅผ ๋ค์ด, ์๋ฒ์์ ํ ์ผ ๋ชฉ๋ก์ ๊ฐ์ ธ์ค๋ API ํธ์ถ์ ์ ์ฉํ ์ ์์.import { createSlice, createAsyncThunk } from "@reduxjs/toolkit"; export const fetchTodos = createAsyncThunk("todos/fetchTodos", async () => { const response = await fetch("https://jsonplaceholder.typicode.com/todos"); return response.json(); }); const todoSlice = createSlice({ name: "todos", initialState: { items: [], status: "idle" }, reducers: { addTodo: (state, action) => { state.items.push({ id: Date.now(), text: action.payload }); }, removeTodo: (state, action) => { state.items = state.items.filter((todo) => todo.id !== action.payload); }, }, extraReducers: (builder) => { builder.addCase(fetchTodos.fulfilled, (state, action) => { state.items = action.payload; state.status = "success"; }); }, }); export const { addTodo, removeTodo } = todoSlice.actions; export default todoSlice.reducer;โ ๋ฐฐ์ด ์
โข createAsyncThunk๋ฅผ ์ฌ์ฉํ๋ฉด Redux์์ API ์์ฒญ์ ์ฝ๊ฒ ๊ด๋ฆฌํ ์ ์์.
โข ๊ธฐ์กด Redux๋ณด๋ค ์ฝ๋๊ฐ ํจ์ฌ ๊ฐ๊ฒฐํด์ง.
โข RTK๊ฐ ์ํ ๊ด๋ฆฌ์ ํ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ์๋ฆฌ ์ก๋ ์ด์ ๋ฅผ ์ดํดํ๊ฒ ๋จ .