๐ 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]);
}
์์
- ์ฌ๋ฌ ์ปดํฌ๋ํธ์์ ๊ณต์ → Redux store ์ ๋ฐ์ดํธ
- ํ ์ปดํฌ๋ํธ๋ง ํ์ → ๋ก์ปฌ state (useState)
- 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 ํธ์ถ ํจํด ๋ด์ฅ