
๐ฏ Component๋ฅผ ์์ฑํ๋ฉด์ @reduxjs/toolkit, react-redux ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ตํ๋๋ค.
Trello ๋ณด๋ ์ถ๊ฐ UI๋ฅผ ๋ง๋ค๋ฉด์, Redux Toolkit์ createSlice, configureStore, ๊ทธ๋ฆฌ๊ณ React-Redux์ useDispatch, useSelector ์ฌ์ฉ๋ฒ์ ์ตํ๋๋ค.
types.ts )๋ณด๋, ๋ฆฌ์คํธ, ํ์คํฌ์ ๊ตฌ์กฐ๋ฅผ ์ ์ํฉ๋๋ค.
// types.ts
export type Task = {
taskId: string;
taskName: string;
};
export type List = {
listId: string;
listName: string;
tasks: Task[];
};
export type Board = {
boardId: string;
boardName: string;
lists: List[];
};
export type BoardState = {
boards: Board[];
};
boardSlice.ts )createSlice๋ ์ํ์ ์ํ๋ฅผ ๋ฐ๊พธ๋ ํจ์(reducer)๋ฅผ ํ ๋ฒ์ ๋ง๋ค์ด์ฃผ๋ ํจ์์
๋๋ค.
PayloadAction<T>์ผ๋ก ์ก์
์ ๋ค์ด์ค๋ ๋ฐ์ดํฐ ํ์
์ ์ ํ ์ ์์ต๋๋ค.
// boardSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { IBoard } from '../../types';
type TBoardState = {
modalActive: boolean;
boardArray: IBoard[];
};
type TAddBoardAction = {
board: IBoard;
};
type TDeleteListAction = {
boardId: string;
listId: string;
};
const initialState: TBoardState = {
modalActive: false,
boardArray: [
{
boardId: 'board-0',
boardName: '์ฒซ ๋ฒ์งธ ๊ฒ์๋ฌผ',
lists: [
{
listId: 'list-0',
listName: 'list 1',
tasks: [
{
taskId: 'task-0',
taskName: 'Task 1',
taskDescription: 'Description',
taskOwner: 'John',
},
{
taskId: 'task-1',
taskName: 'Task 2',
taskDescription: 'Description',
taskOwner: 'John',
},
],
},
{
listId: 'list-1',
listName: 'list 2',
tasks: [
{
taskId: 'task-3',
taskName: 'Task 3',
taskDescription: 'Description',
taskOwner: 'John',
},
],
},
],
},
],
};
const boardSlice = createSlice({
name: 'boards',
initialState,
reducers: {
addBoard: (state, { payload }: PayloadAction<TAddBoardAction>) => {
state.boardArray.push(payload.board);
},
},
});
export const { addBoard } = boardSlice.actions;
export const boardsReducer = boardSlice.reducer;
store/index.ts )reducer ๊ฐ์ฒด๋ก ์ฌ๋ฌ ์ฌ๋ผ์ด์ค(reducer)๋ฅผ ํ๋๋ก ํตํฉํฉ๋๋ค.// store/reducer/reducer.ts
import { boardsReducer } from '../slices/boardsSlice';
import { loggerReducer } from '../slices/loggerSlice';
import { modalReducer } from '../slices/modalSlice';
const reducer = {
logger: loggerReducer,
boards: boardsReducer,
modal: modalReducer,
};
export default reducer;
configureStore๋ ์ฌ๋ฌ slice๋ฅผ ํ๋๋ก ํฉ์น๊ณ Redux store๋ฅผ ์์ฑํด์ฃผ๋ ํจ์์
๋๋ค.// store/index.ts
import { configureStore } from '@reduxjs/toolkit';
import reducer from './reducer/reducer';
const store = configureStore({
reducer,
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export default store;
hooks.ts )React ์ปดํฌ๋ํธ์์ ํ์
์์ ํ๊ฒ dispatch์ selector๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ๋์์ค๋๋ค.
useDispatch๋ ์ปดํฌ๋ํธ์์ ์ก์
์ ์คํ์ํค๋ ํจ์์
๋๋ค.
useSelector๋ Redux store์ ์ ์ฅ๋ ์ํ๊ฐ์ ๊บผ๋ด์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ทธ ๊ฐ์ด ๋ฐ๋๋ฉด, ์๋์ผ๋ก ์ปดํฌ๋ํธ๋ฅผ ๋ฆฌ๋ ๋๋งํด์ค๋๋ค.
import { AppDispatch, RootState } from '../store';
import { TypedUseSelectorHook, useDispatch } from 'react-redux';
import { useSelector } from 'react-redux';
export const useTypedSelector: TypedUseSelectorHook<RootState> = useSelector;
export const useTypedDispatch = () => useDispatch<AppDispatch>();
๐ค
useSelector๊ฐ ํ์ํ ์ด์ ๋ ๋ญ๊น?React๋ Redux store์ ๊ฐ์ ์๋์ผ๋ก ๊ฐ์งํ์ง ์๊ธฐ ๋๋ฌธ์ ๋ณ๊ฒฝ ์ฌํญ์ ์ด๋ค ์ปดํฌ๋ํธ๊ฐ ๊ด์ฐฐํ ์ง ์ง์ ์๋ ค์ค์ผ ํฉ๋๋ค.
๊ฐ๋ React ( useState)Redux ( useSelector,useDispatch)์ํ ์ ์ฅ ์ปดํฌ๋ํธ ์ ( useState)์ค์ ์ ์ฅ์์ธ Redux store ์ํ ๋ณ๊ฒฝ setState(newVal)dispatch(action)๋ฆฌ๋ ๋๋ง ์๋ useSelector๋ก ๊ด์ฐฐํ๊ณ ์๋ ์ปดํฌ๋ํธ๋ง ์๋
BoardList.tsx )๋ฒํผ ํด๋ฆญ์ผ๋ก ๋ณด๋๋ฅผ ์ถ๊ฐํ๊ณ , Redux ์ํ๋ฅผ ํ๋ฉด์ ํ์ํฉ๋๋ค.
// BoardList.tsx
import React, { useRef, useState } from 'react';
import { useTypedSelector } from '../../hooks/redux';
import SideForm from './SideForm/SideForm';
import { FiPlusCircle } from 'react-icons/fi';
import {
addButton,
addSection,
boardItem,
boardItemActive,
container,
title,
} from './BoardList.css';
import clsx from 'clsx';
type TBoardListProps = {
activeBoardId: string;
setActiveBoardId: React.Dispatch<React.SetStateAction<string>>;
};
const BoardList: React.FC<TBoardListProps> = ({
activeBoardId,
setActiveBoardId,
}) => {
const { boardArray } = useTypedSelector((state) => state.boards);
const [isFormOpen, setIsFormOpen] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);
const handleClick = () => {
setIsFormOpen(!isFormOpen);
setTimeout(() => {
inputRef.current?.focus();
}, 0);
};
return (
<div className={container}>
<div className={title}>๊ฒ์ํ:</div>
{boardArray.map((board, index) => (
<div
key={board.boardId}
onClick={() => setActiveBoardId(boardArray[index].boardId)}
className={clsx(
{
[boardItemActive]:
boardArray.findIndex((b) => b.boardId === activeBoardId) ===
index,
},
{
[boardItem]:
boardArray.findIndex((b) => b.boardId === activeBoardId) !==
index,
}
)}
>
<div>{board.boardName}</div>
</div>
))}
<div className={addSection}>
{isFormOpen ? (
<SideForm inputRef={inputRef} setIsFormOpen={setIsFormOpen} />
) : (
<FiPlusCircle className={addButton} onClick={handleClick} />
)}
</div>
</div>
);
};
export default BoardList;
useRef() : ํจ์ํ ์ปดํฌ๋ํธ์์ ref๋ฅผ ๋ง๋ค๊ธฐ ์ํด ์ฐ๋ ํ
์
๋๋ค.
ref๋?React ์์๋ ๊ฐ์ ๋ํ โ์ง์ ์ ์ธ ์ฐธ์กฐโ ๋ฅผ ์๋ฏธํ๋ฉฐ DOM ์์๋ ์ปดํฌ๋ํธ์ ์ง์ ์ ๊ทผํ๊ณ ์ถ์ ๋ ์ฌ์ฉ๋ฉ๋๋ค.
const myRef = useRef(null);
myRef.current๊ฐ ์ค์ ๊ฐ๋ฆฌํค๋ ๋์์ด ๋ฉ๋๋ค.[์ฌ์ฉ ์์]
- input ํฌ์ปค์ค ์ฃผ๊ธฐ
- ์คํฌ๋กค ์์น ํ์ธ ๋๋ ์กฐ์
- ๋น๋์ค ์ปจํธ๋กค๋ฌ ๋ค๋ฃจ๊ธฐ
โ ๏ธ ๋ณดํต์ ์ํฉ์์ ๋ฆฌ์กํธ๋ Virtual DOM ๊ธฐ๋ฐ์ผ๋ก ์๋ํ๊ธฐ ๋๋ฌธ์ ์ง์ DOM์ ๊ฑด๋๋ฆฌ๋ฉด ๋ฆฌ์กํธ์์๋ ๋ฐ๋ ๊ฒ์ ๋ชจ๋ฅด๊ธฐ ๋๋ฌธ์ ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์์ต๋๋ค.
์ถ์ฒ: ๋ฆฌ๋์ค ๊ณต์ ๋ฌธ์
[React ์ปดํฌ๋ํธ]
|
| ํด๋ฆญ (์: ๋ณด๋ ์ถ๊ฐ ๋ฒํผ)
โ
dispatch(addBoard) ํธ์ถ
|
โ
[Reducer ์คํ]
createSlice ๋ด๋ถ์ addBoard ํจ์ ์คํ
|
โ
[Redux store ์ํ ๋ณ๊ฒฝ]
์ ๋ณด๋๊ฐ board ๋ฐฐ์ด์ ์ถ๊ฐ๋จ
|
โ
[useSelector ๊ตฌ๋
์ค]
board ์ํ๊ฐ ๋ณ๊ฒฝ๋์์ผ๋ฏ๋ก ์ปดํฌ๋ํธ ๋ฆฌ๋ ๋๋ง
| ๋ผ์ด๋ธ๋ฌ๋ฆฌ | ์ฌ์ฉ๋ ํจ์ | ์ญํ ์ค๋ช |
|---|---|---|
| @reduxjs/toolkit | createSlice | ์ํ์ reducer๋ฅผ ํ ๋ฒ์ ์์ฑํจ |
configureStore | ์ฌ๋ฌ reducer๋ฅผ ํฉ์ณ store ์์ฑ | |
PayloadAction | ์ก์ ์ payload ํ์ ๋ช ์ | |
| react-redux | useDispatch | ์ปดํฌ๋ํธ์์ ์ก์ ์ ๋์คํจ์น (๋ณด๋ด๊ธฐ) |
useSelector | ์ปดํฌ๋ํธ์์ ์ํ๋ฅผ ๊ตฌ๋ ํ์ฌ ๊ฐ์ ธ์ค๊ธฐ | |
TypedUseSelectorHook | useSelector์ ํ์
์ ๋ถ์ฌ ์์ ํ๊ฒ ์ฌ์ฉํ๊ธฐ |
๊ทธ๋ฅ ๋ง์ ๋ฐ๋ผ์น๊ธฐ๋ณด๋ค ์ ๋ฆฌํ๋ฉด์ ๋ ์ดํดํ๋ ๊ฒ ๋ ๋์์ด ๋์๋ค. ์์ง์ ์ด์ํ์ง๋ง ๋ ๋ฐ๋ณต ํ์ต์ด ํ์ํ ๊ฒ ๊ฐ๋ค.