๐Ÿ“Œ TIL (Today I Learned) - Redux & RTK ์ •๋ฆฌ

์žฅํ˜„๋นˆยท2025๋…„ 2์›” 11์ผ

Redux๋ž€?

Redux๋Š” ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ, ์ปดํฌ๋„ŒํŠธ ๊ฐ„ ์ƒํƒœ๋ฅผ ์ „์—ญ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค€๋‹ค.
โ€ข store: ์ „์ฒด ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์ €์žฅ์†Œ
โ€ข action: ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ์ฒด
โ€ข reducer: ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋กœ์ง์„ ๊ฐ€์ง„ ํ•จ์ˆ˜
โ€ข dispatch: ์•ก์…˜์„ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ํ•จ์ˆ˜
โ€ข useSelector(), useDispatch(): React ์ปดํฌ๋„ŒํŠธ์—์„œ Redux ์ƒํƒœ๋ฅผ ๊ฐ€์ ธ์˜ค๊ฑฐ๋‚˜ ๋ณ€๊ฒฝํ•˜๋Š” Hook

TodoList ๋งŒ๋“ค๊ธฐ

โœ… ๊ธฐ๋ณธ ์„ค์ •

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 ์ „๋‹ฌ ์—†์ด ์ƒํƒœ ๊ณต์œ 

Redux Toolkit (RTK)

โœ… RTK๋ž€?
Redux๋ฅผ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” Redux ๊ณต์‹ ํˆดํ‚ท.
๊ธฐ์กด Redux์˜ ๋ณต์žกํ•œ ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ๋ฅผ ์ค„์ด๊ณ  ๋” ์ง๊ด€์ ์ธ API ์ œ๊ณต.

โœ… RTK ์ฃผ์š” ๊ฐœ๋…
1. configureStore() โ†’ createStore๋ณด๋‹ค ๋” ๊ฐ„ํŽธํ•œ ์„ค์ •
2. createSlice() โ†’ ์•ก์…˜ & ๋ฆฌ๋“€์„œ ์ฝ”๋“œ ์ž๋™ ์ƒ์„ฑ
3. useSelector() & useDispatch() โ†’ ์ƒํƒœ ์กฐํšŒ ๋ฐ ์—…๋ฐ์ดํŠธ

โœ… RTK๋กœ TodoList ๋งŒ๋“ค๊ธฐ

โœ” 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 ๊ธฐ๋ฐ˜ TodoList ๊ฐœ์„ 

โœ… 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๊ฐ€ ์ƒํƒœ ๊ด€๋ฆฌ์˜ ํ•„์ˆ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์ž๋ฆฌ ์žก๋Š” ์ด์œ ๋ฅผ ์ดํ•ดํ•˜๊ฒŒ ๋จ .

profile
์•ˆ๋…•ํ•˜์„ธ์š”

0๊ฐœ์˜ ๋Œ“๊ธ€