[DAY-70] React ์ฌํ(2)
๐ ํท๊ฐ๋ ธ๋ & ๋ชฐ๋๋ ๋ถ๋ถ๋ค๋ง ์ ๋ฆฌํ๋ ๋๋ง์ TIL
๐ฏ ๋ชจ๋ ๊ฐ์ ๋ด์ฉ์ ์ ์ง ์์์!
์ค๋์ ์๊ฐ์?
๋..์์ง recoil์ ์ํ ๋ฏนํจ์์ ํค์ด๋์ค์ง ๋ชปํ๋ค.
redux์ ์นํด์ง๋ ค๋ฉด ์์ง ๋ฉ์์ต๋๋ค.
์๋ ๋ฏธ๋ค์จ์ด ์ ๊ทธ๋ ๊ฒ ๋ง์์?
Redux
Redux
๋ Prop Drilling
๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ฑ์ฅํ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
๊ทธ๋ ๋ค๋ฉด contextAPI
์ ์ด๋ค ์ ์ด ๋ค๋ฅผ๊น?
1. ContextAPI
- React ๋ด์ฅ API
- Provider๊ฐ ๊ฐ์ง ์ํ๋ฅผ Consumer๊ฐ ๊ฐ์ ธ์ฌ ์ ์๋ค.
- ์๊ท๋ชจ ํ๋ก์ ํธ์ ์ ํฉ
2. Redux
- Thrid Party ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- ๋ฏธ๋ค์จ์ด ๊ธฐ๋ฅ ์ ๊ณต
- ์ฑ๋ฅ ์ต์ ํ ์ ๊ณต
- Reducer๋ฅผ ํตํด Store์ ์ํ๋ฅผ ์ ์ฅ, ํด๋น ์ํ๊ฐ ๋ณ๊ฒฝ๋๋ฉด View๊ฐ ๋ ๋๋ง
- ์๊ท๋ชจ ํ๋ก์ ํธ์๋ ์ ํฉํ์ง ์์ ์ ์๋ค.
์ฆ Redux
๋ ContextAPI
์ useReducer Hook
์ ํจ๊ป ์ฌ์ฉํ ๊ฒ๊ณผ ์ ์ฌํ๋ค.
Redux
๊ฐ ๋ง์ ๊ธฐ๋ฅ์ ๊ฐ์ง๊ณ ์๋ค๊ณ ํด์ ๋ฌด์กฐ๊ฑด Redux๋ฅผ ์ฌ์ฉํด์ผ ํ๋ ๊ฒ์ ์๋๋ค.
์ํฉ์ ๋ง๊ฒ ์ ํํด์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.
Redux 3๊ฐ์ง ์์น
1. Single source of truth
ํ๋์ ํ๋ก์ ํธ (์ดํ๋ฆฌ์ผ์ด์ ) ์์๋ ํ๋์ ์คํ ์ด๋ง ์ฌ์ฉํ์๋ ์์น์ด๋ค.
์ด๋ ๊ฒ ํ๋ฉด ์ดํ๋ฆฌ์ผ์ด์ ์ ๋๋ฒ๊น ์ด ์ฌ์์ง๊ณ ์๋ฒ์ ์ง๋ ฌํ๊ฐ ๋ ์ ์๋ค.
๋ํ ์ฝ๊ฒ ํด๋ผ์ด์ธํธ์์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์๋ค์ฌ์ฌ ์ ์๊ฒ ๋๋ค.
2. State is read-only
์ํ๋ฅผ ๋ณํ์ํค๋ ๋ฐฉ๋ฒ์ ์ค์ง Action์ ์ผ์ผํค๋ ๋ฐฉ๋ฒ ๋ฟ์ด๋ค.
3. Changes are made with pure functions
๋ณํ๋ฅผ ์ผ์ผํค๋ Reducer ํจ์๋ ์์ํ ํจ์์ฌ์ผ ํ๋ค.
- ๋ฆฌ๋์ ํจ์๋ ์ด์ ์ํ์ ์ก์ ๊ฐ์ฒด๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ๋๋ค.
- ํ๋ผ๋ฏธํฐ ์ธ์ ๊ฐ์๋ ์์กดํ์ง ์๋๋ค.
- ์ด์ ์ํ๋ ์ ๋๋ก ๊ฑด๋๋ฆฌ์ง ์๊ณ , ๋ณํ๋ฅผ ์ค ์๋ก์ด ์ํ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ๋ฐํํ๋ค.
- ๋๊ฐ์ ํ๋ผ๋ฏธํฐ๋ก ํธ์ถ๋ Reducer ํจ์๋ ์ธ์ ๋ ๋๊ฐ์ ๊ฒฐ๊ณผ ๊ฐ์ ๋ฐํํ๋ค.
Redux ๊ธฐ์ด
Redux
์์ ์ฌ์ฉํ๋ ๊ฐ์ฅ ๊ธฐ์ด์ ์ธ ์ฉ์ด๋ Store
, action
, dispatch
, reducer
์ด ์๋ค.
1. Store
์ํ๊ฐ ๋ค์ด์๋ ๋ถ๋ถ์ Store
๋ผ๊ณ ํ๋ค.
ํ๋์ ํ๋ก์ ํธ๋ ํ๋์ Store
๋ง ๊ฐ์ง ์ ์๋ค.
2. Action
์ํ์ ๋ณํ๊ฐ ํ์ํ๋ค๋ฉด, Action
์ ์ผ์ผ์ผ์ผ ํ๋ค.
Action
์ ๊ฐ์ฒด๋ก ํํ๋๋ฉฐ **type
ํ๋๋ฅผ ๋ฐ๋์ ๊ฐ์ง๊ณ ์์ด์ผ ํ๋ค.**
import { v4 } from "uuid";
import { Action } from "./types";
export const addTask = (content: string): Action => {
return {
type: "ADD_TASK",
payload: {
id: v4(),
content,
complete: false,
},
};
};
export const updateTask = (
id: string,
content: string,
complete: boolean
): Action => {
return {
type: "UPDATE_TASK",
payload: {
id,
content,
complete,
},
};
};
export const removeTask = (id: string): Action => {
return {
type: "REMOVE_TASK",
payload: {
id,
content: "",
complete: false,
},
};
};
3. Dispatch
Store
์ ๋ด์ฅ ํจ์ ์ค ํ๋์ธ ๋์คํจ์น๋ ์ก์
๊ฐ์ฒด๋ฅผ ๋๊ฒจ์ค ์ํ๋ฅผ ์
๋ฐ์ดํธํ๋ ์ ์ผํ ๋ฐฉ๋ฒ์ด๋ค.
์ฆ ์ํ๋ฅผ ์
๋ฐ์ดํธํ๊ธฐ ์ํด์๋ Store
์ Dispatch
ํจ์๋ฅผ ์ด์ฉํด์ผ ํ๋ค.
์ด๋ฒคํธ ํธ๋ฆฌ๊ฑฐ์ ๊ฐ๋ค.
const dispatch = useDispatch();
dispatch(addTask(task)); // ์ด๋ฐ ํ์์ผ๋ก ์ฌ์ฉํ๋ค.
4. Reducer
Reducer
๋ ํ์ฌ ์ํ์ ์ก์
๊ฐ์ฒด๋ฅผ ๋ฐ์ ํ์ํ๋ค๋ฉด ์๋ก์ด ์ํ๋ฅผ ๋ฆฌํดํ๋ ํจ์์ด๋ค.
์ด๋ฒคํธ ๋ฆฌ์ค๋๋ผ๊ณ ์๊ฐํ ์ ์๋ค.
import { Action, Task } from "./types";
export const tasks = (state: Task[] = [], action: Action) => {
switch (action.type) {
case "ADD_TASK": {
const newTask = action.payload;
return [...state, newTask];
}
case "UPDATE_TASK": {
const updatedTask = action.payload;
return state.map((oldTask) =>
oldTask.id === updatedTask.id ? updatedTask : oldTask
);
}
case "REMOVE_TASK": {
const removedTask = action.payload;
return state.filter((task) => task.id !== removedTask.id);
}
default: {
return state;
}
}
};
Redux-toolkit
์ค์ต์์ ๋ง์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ค๋ค์ง๋ง, ๊ฐ์ฅ ์ค์ํด ๋ณด์ด๋ ๊ณต์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ redux-toolkit์ ๋ํด ์ง์ด๋ณด๋๋ก ํ์.
Redux๋ฅผ ์๋ฌด ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด ์ฌ์ฉํ๊ฒ ๋๋ฉด, Type ์ ์ โ action ํจ์ ์ ์ โ reducer ์ ์ ํ์์ผ ํ๋์ Action์ด ์์ฑ๋๋ค.
์ด๋ฐ ๋๋ฌด ๋ณต์กํ ์ผ๋ จ์ ๊ณผ์ ์ ํจ์ถ์์ผ์ค ์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ Redux-toolkit
์ด๋ค.
1. configureStore
configureStore
์ ์ฌ์ฉํ๋ฉด store๋ฅผ ๋ง๋ค์ด์ค๋ค. ๋ง์น createStore
๊ณผ ๊ฐ๋ค.
ํ์ง๋ง ๋ค๋ฅธ ์ ์ reducer ๋ฟ๋ง ์๋ middleware, devTools ์ฌ์ฉ ์ฌ๋ถ, ์คํ ์ด์ ์ด๊ธฐ ๊ฐ ๋ฑ์ ๊ฐ์ด ์ง์ ํ ์ ์๋ค.
export const store = configureStore({
reducer: rootReducer,
middleware: [logger],
devTools: true,
});
2. createSlice
createSlice
๋ฅผ ์ฌ์ฉํ๋ฉด createAction
, createReducer
๊ฐ ๋ด๋ถ์ ์ผ๋ก ํจ๊ป ์ฌ์ฉ๋๋ค.
์๋์ ๊ฐ์ด ์์ฑํ๋ฉด slice ์ด๋ฆ์ ์ ๋์ฌ๋ก Action Type์ด ์๋์ผ๋ก ์์ฑ๋๋ค. (tasks/add)
export const tasks = createSlice({
name: "tasks",
initialState: [] as Task[],
reducers: {
add: {
reducer: (state: Task[], action: PayloadAction<Task>) => {
state.push(action.payload);
},
// prepare๋ฅผ ์์ฑํจ์ผ๋ก์จ ๋ฆฌ๋์๊ฐ ์คํ๋๊ธฐ ์ด์ ์ ์ก์
์ ๋ด์ฉ์ ํธ์งํ ์ ์๋ค.
prepare: (content: string) => ({
payload: {
id: v4(),
content,
complete: false,
},
}),
},
update: {
reducer: (state: Task[], action: PayloadAction<Task>) => {
const index = state.findIndex((task) => task.id === action.payload.id);
state[index] = action.payload;
},
prepare: (id: string, content: string, complete: boolean) => ({
payload: {
id,
content,
complete,
},
}),
},
remove: {
reducer: (state: Task[], action: PayloadAction<Pick<Task, "id">>) => {
const index = state.findIndex((task) => task.id === action.payload.id);
if (index !== -1) state.splice(index, 1);
},
prepare: (id: string) => ({
payload: { id },
}),
},
},
});
Redux๋ฅผ ์ฌ์ฉํ ์ค์ต์ ํ๊ณ ๋๋ ์ ์, ์ง์ ์ฅ๋ฒฝ์ด ๋งค์ฐ ๋๋ค.
๋ํ ๋ค๋ฅธ ์ฌ์ฉ ์์๋ฅผ ์ฐพ์๋ณด๋๋ฐ, ์ฌ๋๋ง๋ค ๊ฐ์ ๋ค ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ค..
์์ฃผ ์ฌ์ฉํ๋ฉด์ ๊ฐ์ ์ตํ๋ ์๋ฐ์ ์๋ค๊ณ ์๊ฐํ๋ค. ํ
์ถ์ฒ
https://kyounghwan01.github.io/blog/React/redux/redux-basic/
๋๊ธ๋จ๊ธฐ๊ธฐ