๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๊ฐœ๋ฐœ

์ „์—ญ ๊ด€๋ฆฌ vs ๋กœ์ปฌ ๊ด€๋ฆฌ vs API

by mlheein 2025. 8. 25.

๐Ÿ“„ React ์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌ(Redux Toolkit) & API ์ •๋ฆฌ

1๏ธโƒฃ ์ „์—ญ ์ƒํƒœ vs ๋กœ์ปฌ ์ƒํƒœ

์ƒํƒœ ์œ ํ˜•์‚ฌ์šฉ ์กฐ๊ฑด์˜ˆ์‹œ
๋กœ์ปฌ ์ƒํƒœ (useState) ํ•œ ์ปดํฌ๋„ŒํŠธ ์•ˆ์—์„œ๋งŒ ์“ฐ์ด๊ณ , ๋‹ค๋ฅธ ๊ณณ๊ณผ ๊ณต์œ  X ์ž…๋ ฅ๊ฐ’, ๋ชจ๋‹ฌ ์—ด๋ฆผ/๋‹ซํž˜, ์ฒดํฌ๋ฐ•์Šค ์ƒํƒœ
์ „์—ญ ์ƒํƒœ (Redux) ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ/ํŽ˜์ด์ง€์—์„œ ๋™์‹œ์— ํ•„์š” ๋กœ๊ทธ์ธ ์‚ฌ์šฉ์ž ์ •๋ณด, ๊ถŒํ•œ, ์„ ํƒ๋œ ๊ฑด๋ฌผ, ๋‹คํฌ๋ชจ๋“œ ์„ค์ •

์›์น™: ์—ฌ๋Ÿฌ ํŽ˜์ด์ง€/์ปดํฌ๋„ŒํŠธ์—์„œ ๊ณต์œ ๋˜๋ฉด ์ „์—ญ ์ƒํƒœ, ์•„๋‹ˆ๋ฉด ๋กœ์ปฌ ์ƒํƒœ


2๏ธโƒฃ Redux Toolkit ์‚ฌ์šฉ ์˜ˆ์‹œ

userSlice.js

import { createSlice } from "@reduxjs/toolkit";

const initialState = {
  id: null,
  name: "",
  token: null,
  isLoggedIn: false,
};

const userSlice = createSlice({
  name: "user",
  initialState,
  reducers: {
    setUser: (state, action) => {
      state.id = action.payload.id;
      state.name = action.payload.name;
      state.token = action.payload.token;
      state.isLoggedIn = true;
    },
    clearUser: () => initialState,
  },
});

export const { setUser, clearUser } = userSlice.actions;
export default userSlice.reducer;
 

store/index.js

import { configureStore } from "@reduxjs/toolkit";
import userReducer from "./userSlice";

export const store = configureStore({
  reducer: {
    user: userReducer,
  },
});

3๏ธโƒฃ useSelector๋กœ ์ƒํƒœ ์ฝ๊ธฐ

import React from "react";
import { useSelector } from "react-redux";

function Profile() {
  const user = useSelector((state) => state.user);

  return (
    <div>
      <h2>ํ”„๋กœํ•„ ์ •๋ณด</h2>
      <p>ID: {user.id}</p>
      <p>์ด๋ฆ„: {user.name}</p>
      <p>๋กœ๊ทธ์ธ ์—ฌ๋ถ€: {user.isLoggedIn ? "๋กœ๊ทธ์ธ๋จ" : "๋กœ๊ทธ์•„์›ƒ๋จ"}</p>
    </div>
  );
}
 

4๏ธโƒฃ ๋กœ๊ทธ์ธ/๋กœ๊ทธ์•„์›ƒ ํ๋ฆ„

userAPI.js

import { setUser, clearUser } from "../store/userSlice";

// ๋กœ๊ทธ์ธ
export const loginUser = (credentials) => async (dispatch) => {
  try {
    const res = await fetch("/api/login", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(credentials),
    });
    if (!res.ok) throw new Error("๋กœ๊ทธ์ธ ์‹คํŒจ");
    const data = await res.json();

    localStorage.setItem("token", data.token);  // ์ƒˆ๋กœ๊ณ ์นจ ์œ ์ง€
    dispatch(setUser(data));
  } catch (err) {
    console.error("๋กœ๊ทธ์ธ ์—๋Ÿฌ:", err);
    throw err;
  }
};

// ๋กœ๊ทธ์•„์›ƒ
export const logoutUser = () => async (dispatch) => {
  try {
    await fetch("/api/logout", { method: "POST", credentials: "include" });
  } catch (err) {
    console.warn("๋กœ๊ทธ์•„์›ƒ ์—๋Ÿฌ:", err);
  } finally {
    localStorage.removeItem("token");
    dispatch(clearUser());
  }
};
 
 

5๏ธโƒฃ localStorage์™€ Redux์˜ ์ฐจ์ด

์ €์žฅ์†ŒํŠน์ง•์‚ฌ์šฉ ๋ชฉ์ 
Redux store ๋ฉ”๋ชจ๋ฆฌ ๊ธฐ๋ฐ˜, ์•ฑ ์‹คํ–‰ ๋™์•ˆ๋งŒ ์œ ์ง€ ํŽ˜์ด์ง€ ์ „ํ™˜ ์‹œ ์‹ค์‹œ๊ฐ„ ์ฐธ์กฐ
localStorage ๋ธŒ๋ผ์šฐ์ €์— ์˜๊ตฌ ์ €์žฅ ์ƒˆ๋กœ๊ณ ์นจ ํ›„์—๋„ ๋กœ๊ทธ์ธ ์œ ์ง€, ํ† ํฐ ์ €์žฅ
  • ์ƒˆ๋กœ๊ณ ์นจ ์‹œ store๋Š” ์ดˆ๊ธฐํ™”
  • localStorage์˜ ํ† ํฐ/์ •๋ณด๋กœ store๋ฅผ ์žฌ์„ค์ •ํ•˜๋ฉด ๋กœ๊ทธ์ธ ์ƒํƒœ ์œ ์ง€ ๊ฐ€๋Šฅ
const token = localStorage.getItem("token");
if (token) {
  dispatch(setUser({ id: 1, name: "Moon", token }));
}
 

6๏ธโƒฃ API ํ˜ธ์ถœ๊ณผ store ์—…๋ฐ์ดํŠธ

์ „์—ญ ์ƒํƒœ์šฉ ๋ฐ์ดํ„ฐ

import { useEffect } from "react";
import { useDispatch } from "react-redux";
import { setBuildings, setBuildingError } from "../store/buildingSlice";

function BuildingList() {
  const dispatch = useDispatch();

  useEffect(() => {
    async function fetchBuildings() {
      try {
        const res = await fetch("/api/buildings");
        if (!res.ok) throw new Error("API ํ˜ธ์ถœ ์‹คํŒจ");
        const data = await res.json();
        dispatch(setBuildings(data));
      } catch (err) {
        console.error("๋นŒ๋”ฉ ๋ฆฌ์ŠคํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ ์‹คํŒจ:", err);
        dispatch(setBuildingError(err.message)); // ์˜ค๋ฅ˜ ์ƒํƒœ๋งŒ ์ €์žฅ
      }
    }
    fetchBuildings();
  }, [dispatch]);
}

์š”์ 

  1. ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ณต์œ  → Redux store ์—…๋ฐ์ดํŠธ
  2. ํ•œ ์ปดํฌ๋„ŒํŠธ๋งŒ ํ•„์š” → ๋กœ์ปฌ state (useState)
  3. API ์‹คํŒจ ์‹œ store ์ดˆ๊ธฐํ™”ํ•˜์ง€ ์•Š๊ณ  ๊ธฐ์กด ๋ฐ์ดํ„ฐ ์œ ์ง€

7๏ธโƒฃ ํ•ต์‹ฌ ์›์น™ ์ •๋ฆฌ

  • ์ „์—ญ ์ƒํƒœ๋Š” ์—ฌ๋Ÿฌ ํ™”๋ฉด์—์„œ ๊ณต์œ ๋˜๋Š” ์ค‘์š”ํ•œ ๋ฐ์ดํ„ฐ
  • API ์—…๋ฐ์ดํŠธ → Redux ์—…๋ฐ์ดํŠธ ํŒจํ„ด์„ ์ง€์ผœ์•ผ ์ผ๊ด€์„ฑ ์œ ์ง€
  • localStorage + store ์—ฐ๋™ → ์ƒˆ๋กœ๊ณ ์นจ ํ›„ ๋กœ๊ทธ์ธ ์ƒํƒœ ์œ ์ง€
  • ๋กœ๊ทธ์ธ: try-catch ํ•„์ˆ˜ (์‹คํŒจ ์‹œ ์•ฑ ์ง„ํ–‰ ๋ถˆ๊ฐ€)
  • ๋กœ๊ทธ์•„์›ƒ: JWT ๊ธฐ๋ฐ˜์ด๋ฉด ๋‹จ์ˆœ ํ† ํฐ ์‚ญ์ œ, ์„ธ์…˜ ๊ธฐ๋ฐ˜์ด๋ฉด API + try-catch ํ•„์ˆ˜
  • API ์‹คํŒจ ์‹œ store ์ดˆ๊ธฐํ™” ๊ธˆ์ง€ → ๊ธฐ์กด ๋ฐ์ดํ„ฐ ์œ ์ง€

 

  • Redux: ์›๋ž˜ Redux ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž์ฒด. createStore, combineReducers, dispatch, subscribe ๋“ฑ ๊ธฐ๋ณธ API ์‚ฌ์šฉ.
  • Redux Toolkit(RTK): Redux ๊ณต์‹์—์„œ ๋งŒ๋“  “ํ˜„๋Œ€์ ์ธ Redux ์‚ฌ์šฉ ํŒจํ„ด” ๋ชจ์Œ.
    • createSlice → ์•ก์…˜๊ณผ ๋ฆฌ๋“€์„œ๋ฅผ ํ•œ ๋ฒˆ์— ๋งŒ๋“ค์–ด์คŒ
    • configureStore → ์Šคํ† ์–ด ์ƒ์„ฑ, ๋ฏธ๋“ค์›จ์–ด ์„ค์ • ๋“ฑ์„ ๊ฐ„๋‹จํ•˜๊ฒŒ
    • createAsyncThunk → ๋น„๋™๊ธฐ API ํ˜ธ์ถœ ํŒจํ„ด ๋‚ด์žฅ