프로젝트 생성
프레임워크는 리액트, variant는 타입스크립트로 설정한다.
npm init vite
사용할 패키지들을 설치한다.
상태 관리 라이브러리인 redux와 drag and drop을 위한 react-beautiful-dnd, ts에서 스타일을 적용하기 위한 vanilla-extract 패키지를 사용한다.
npm install @reduxjs/toolkit redux clsx @vanilla-extract/css @vanilla-extract/css-utils @vanilla-extract/vite-plugin react-icons uuid react-beautiful-dnd react-redux
Redux 사용
부모 컴포넌트와 자식 컴포넌트가 소통하려면 State와 Props를 사용해야 한다. 하지만 앱이 커지면 소스코드가 지저분해지고 관리가 힘들기 때문에 상태 관리 라이브러리를 사용한다. 이 프로젝트에서는 Redux를 사용한다.
Flow
한 방향으로만 흐른다.(unidirectional)
- Action을 Dispatch 한다. Action은 객체, Dispatch는 함수이며, Dispatch의 인수로 Action을 넣어줘서 리듀서로 전달한다.
- reducer 함수에서는 타입에 따라 일을 처리하는데, 거기서 리턴하는 값이 redux store에 있는 state를 업데이트한다.
- redux store에 있는 값이 업데이트되면 리액트 컴포넌트가 리렌더링 된다.
Slice 생성
툴킷에서는 리듀서를 생성하려면 Slice가 있어야 한다.
Slice를 생성한 후에 서브 reducer를 생성한다. 아래의 loggerSlice처럼 boardsSlice, modalSlice도 생성해 준다.
store/slices/loggerSlice.ts
import { createSlice } from "@reduxjs/toolkit";
import { ILogItem } from "../../types";
/*
type/index.ts
---------------------------
export interface ILogItem {
logId: string;
logAuthor: string;
logMessage: string;
logTimestamp: string;
}
*/
type TloggerState = {
logArray: ILogItem[]
}
const initialState: TloggerState = {
logArray: []
}
const loggerSlice = createSlice({
name: 'logger',
initialState,
reducers: {
}
})
export const loggerReducer = loggerSlice.reducer;
reducer 생성
아까 생성한 서브 reducer들을 모아서 하나의 reducer를 생성한다.
store/reducer/reducer.ts
import { loggerReducer } from "../slices/loggerSlice"
import { boardsReducer } from "../slices/boardsSlice"
import { modalReducer } from "../slices/modalSlice"
const reducer = {
logger: loggerReducer,
boards: boardsReducer,
modal: modalReducer
}
export default reducer;
store 생성
아까 생성한 reducer로 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;
hook 생성
useSelector는 store의 상태 값을 반환해 주는 역할을 하는 redux의 hook이다. useSelector을 사용한 함수에서 redux store의 상태 값이 업데이트되면 바뀐 store의 상태 값을 다시 가져와 컴포넌트를 리렌더링 한다.
hooks/redux.ts
import { TypedUseSelectorHook, useDispatch } from "react-redux";
import { useSelector } from "react-redux";
import { AppDispatch, RootState } from "../store";
export const useTypedSelector: TypedUseSelectorHook<RootState> = useSelector;
export const useTypedDispatch = () => useDispatch<AppDispatch>();
provider
redux toolkit을 사용하려면 루트 컴포넌트를 Provider로 감싸야한다.
main.tsx
// ...
import { Provider } from 'react-redux'
import store from './store/index.ts'
ReactDOM.createRoot(document.getElementById('root')!).render(
<Provider store={store}>
<App />
</Provider>,
)
전역 스타일 생성
vanilla-extract 패키지를 사용하려면 vite.config.ts 파일에서 아래와 같이 플러그인을 추가해야 한다.
// ...
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin'
export default defineConfig({
plugins: [react(), vanillaExtractPlugin()],
})
App.css.ts 파일을 다음과 같이 작성하고, 스타일을 변수에 담아 해당 변수를 export 해준다.
import { createGlobalTheme, style } from "@vanilla-extract/css";
export const vars = createGlobalTheme(":root", {
color: {
main: "#ffa728",
...
deleteButton: "rgb(237, 51, 88)"
},
...
minWidth: {
list: '250px'
}
})
export const appContainer = style({
display: 'flex',
...
width: '100vw'
});
export const board = style({
display: 'flex',
flexDirection: 'row',
height: '100%'
})
export const buttons = style({
marginTop: 'auto',
paddingLeft: vars.spacing.big2
})
그리고 다음과 같이 import 해서 className에 작성해 주면 스타일이 적용된다.
import { appContainer, board, buttons } from "./App.css.ts"
function App() {
return (
<div className={appContainer}>
<div className={board}>
...
</div>
<div className={buttons}>
...
</div>
</div>
)
}
참고
export const 변수명으로 export 하면 import 할 때 중괄호를 사용해서 같은 이름으로 가져와야 하고,
default로 export하면 어떤 이름으로든 가져올 수 있다.
배운 점
- 부모 컴포넌트와 자식 컴포넌트가 소통하려면 state와 props를 사용하는데, 앱이 커지면 가독성이 떨어지고 유지 보수가 힘들어진다. 따라서 상태 관리 라이브러리를 사용한다.
- Redux의 흐름은 다음과 같다.
- Dispatch의 인수로 Action을 넣어줘서 reducer로 전달한다.
- reducer 함수에서는 전달받은 타입에 따라 일을 하고, 여기서 리턴하는 값이 store의 state를 업데이트한다.
- store에 있는 값이 업데이트되면 컴포넌트를 리렌더링 한다.
- toolkit에서는 Slice로 reducer을 생성하며, 서브 reducer들을 combine 하여 하나의 reducer을 생성한다. 이 reducer로 store을 생성한다.
- useSelector는 store의 상태 값을 반환해 주는 역할을 하는 redux의 hook으로, useSelector을 사용한 함수에서 store의 상태 값이 업데이트되면 바뀐 store의 상태 값을 다시 가져와 컴포넌트를 리렌더링 한다.
'데브코스' 카테고리의 다른 글
[12주차 - DAY4] 게시판 만들기(2) (0) | 2024.05.16 |
---|---|
[12주차 주간 발표] 인덱스 (0) | 2024.05.15 |
[12주차 - DAY1] TodoList 만들기(2) (0) | 2024.05.13 |
[11주차 - DAY5] 리액트(2) TodoList 만들기 (1) | 2024.05.10 |
[11주차 - DAY4] 리액트(1) (0) | 2024.05.09 |