📚요약
지난 시간까지 타입스크립트와 리액트의 기초를 배웠습니다. 이번 시간부터 리액트를 활용해 프로젝트를 진행해 보겠습니다.
📖리액트 - 업무 관리 프로젝트
시작하기도 전에 설정하는 과정에서 에러가 발생했다. 해당 에러는 다음 글에서 확인할 수 있습니다.
[JSX.IntrinsicElements 에러] VSCode App.tsx 왜 다 빨간줄이야?
에러 배경npm init vite를 통해 react와 typescript로 프로젝트를 시작하려고 했다.에러 그 잡채App.tsx의 기본 코드를 보기 위해서 창을 띄웠는데 HTML의 기본 태그에 전부 빨간 줄이 있었다. 모든 HTML 태그
nulzi-dev.tistory.com
📄구조 및 패키지
구조
- components 폴더 : 컴포넌트 모음.
- hooks 폴더 : custom hooks 코드.
- store 폴더 : 상태 관리 라이브러리 코드 작성. ex) redux, mobx, zustand, recoil, react context
- reducer 폴더 : slices에 있는 reducer 집합.
- slices 폴더 : redux toolkit slice 집합.
- types 폴더 : 사용자 정의 타입 집합.
패키지
- @reduxjs/toolkit
- react-redux
- redux
- clsx
- @vanilla-extract/css
- @vanilla-extract/css-utils
- @vanilla-extract/vite-plugin : vite와 연결을 위한 라이브러리
- react-icons : https://react-icons.github.io/react-icons/ 해당 사이트에서 원하는 아이콘을 찾아 쉽게 사용할 수 있습니다.
- uuid : 유니크 아이디 생성을 위한 라이브러리
- react-beautiful-dnd
🍯tip! VSCode의 Extension 중 'ES7+ React/Redux/React-Native snippets'를 설치하면 간단한 단어로 간편하게 코드를 작성할 수 있다. 예를 들어 rafce를 사용하면 자동으로 함수형 컴포넌트를 생성해 준다.
📄Redux
Redux는 상태 관리 라이브러리로 필수가 아닌 선택 사항입니다. 하지만 프로젝트나 앱의 규모가 커지게 되면 props만으로 상태를 관리하는 것이 어려워지고, 코드가 복잡해져 가독성이 떨어집니다. 그렇기 때문에 Redux와 같은 상태 관리 라이브러리를 사용해서 여러 컴포넌트에서 마치 전역 변수와 같이 사용해 상태를 공유할 수 있습니다.
📑흐름(Flow)
- Action(객체) Dispatch(함수)
- Slice 생성(Sub Reducer 생성) : '@reduxjs/toolkit' 라이브러리의 createSlice() 사용.
- 생성된 Slice를 모아 Reducer 생성 : {slice, slice, slice, ...} slice 집합을 통해 reducer 생성.
- 생성된 Reducer를 통해 Store 생성 : '@reduxjs/toolkit' 라이브러리의 configureStore() 사용.
- Reducer(함수)가 타입에 따라 return
- Redux Store의 State 업데이트
- React Component Rerendering
redux toolkit에서는 reducer를 생성하기 위해서는 slice가 필요하기 때문에 기존 redux의 흐름에 몇 가지 추가가 있습니다.
📑custom hooks 만들기
타입스크립트를 사용하면 자동으로 타입을 추론하는 경우가 있지만 그렇지 못한 경우도 많습니다. 그럴 때는 직접 타입을 알려줘야 합니다. Redux를 사용할 때도 마찬가지인데 데이터를 가져오는 useSelector 함수나 액션을 실행시키는 useDispatch 함수가 있습니다.
// 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;
// useSelector나 useDispatch 사용
/* 타입스크립트 사용 시 state가 unknown으로 추론되면
.something으로 store에서 데이터를 불러올 수 없다 */
const something = useSelector(state => state.something);
// 해서 실제로 사용할 경우 다음과 같이 타입을 지정해줘야 불러올 수 있다.
const something = useSelector((state:RootState)=> state.something);
// dispatch 또한 마찬가지이다.
const dispatch = useDispatch();
dispatch(someAction);
위와 같이 사용한다면 매번 함수를 사용할 때마다 타입을 지정해줘야 하기 때문에 상당히 귀찮아질 수 있습니다. 그렇기에 커스텀 훅을 만들어서 사용하면 편해집니다.
import { TypedUseSelectorHook, useDispatch } from "react-redux";
import { useSelector } from "react-redux";
import { AppDispatch, RootState } from "../store";
// TS의 Generic이 이용되었다.
export const useTypedSelector: TypedUseSelectorHook<RootState> = useSelector;
export const useTypedDispatch = () => useDispatch<AppDispatch>();
📄전역 스타일 생성
이번 프로젝트에서 스타일을 적용하기 위해서 기존에 사용하던 .css 파일을 사용하던 방식과 달리 .css.ts 파일을 사용합니다. '@vanilla-extract/css' 라이브러리를 활용한 방식으로 스타일을 변수처럼 사용할 수 있다는 장점이 있습니다. createGlobalTheme 함수와 style 함수를 사용해서 스타일을 설정합니다.
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), vanillaExtractPlugin()],
});
사용하기 전에 vite를 사용하면서 해당 라이브러리를 사용하기 위해서는 설정이 필요합니다. vite.confing.ts 파일에 plugin 함수를 사용하는 것입니다.
// App.css.ts
import { createGlobalTheme, style } from "@vanilla-extract/css";
export const vars = createGlobalTheme(":root", {
color: {
},
fontSizing: {
}
// ...
});
export const appContainer = style({
// ...
});
export const board = style({
// ...
});
export const buttons = style({
marginTop: "auto",
paddingLeft: vars.spacing.big2,
});
🍯tip! 전역 스타일 생성할 때 사용되는 ':root'는 의사 클래스로 자세한 내용은 MDN에서 확인할 수 있습니다.
위 코드를 사용해 생성된 스타일은 아래 코드처럼 사용됩니다.
import { appContainer, board, buttons } from "./App.css";
function App() {
return (
<div className={appContainer}>
<div className={board}></div>
<div className={buttons}>
<button></button>
</div>
</div>
);
}
export default App;
해당 코드로 클래스를 작성하고 스타일이 적용되면 크롬의 개발자 도구에서 다음과 같이 보입니다.

📖타입스크립트
📄ReturnType
타입스크립트에는 여러 유틸리티 타입이 존재합니다. 그중 ReturnType<>은 함수 Type의 반환 타입으로 구성된 타입을 생성합니다.
function func(v: string) : number {
return +v;
}
type Type =ReturnType<typeof func>; // Type은 number 타입이 된다.
const a:Type = 1234;
const b:Type = 'Only number'; // error: Type 'string' is not assignable to type 'number'.ts(2322)

이 외 다른 유틸리티들은 공식 사이트에서 확인할 수 있습니다.
Documentation - Utility Types
Types which are globally included in TypeScript
www.typescriptlang.org
📄Generic
제네릭을 사용하면 타입을 변수처럼 사용하게 해 나중에 타입을 정해서 사용할 수 있도록 만든 것입니다. 중요한 것은 제네릭을 활용하면 재사용이 가능하다는 점입니다.
function generic<T>(x: T): T {
return x;
}
generic<number>(1); // 1
/*
function generic<number>(x: number): number {
return x;
}
*/
generic<string>('hi'); // hi
/*
function generic<string>(x: string): string {
return x;
}
*/
다양한 상황에서 사용이 가능하지만 그만큼 주의도 필요하다는 점 잊지 말아야 합니다.
이용 예시
interface Obj<T> {
name: T;
}
interface State {
state: {
data: string;
loading: boolean;
};
}
const obj: Obj<State> = {
name: {
state: {
data: "",
loading: true,
},
},
};
자세한 내용은 공식 홈페이지를 통해 더 볼 수 있습니다.
❔▪❓
Q. 강사님이 loggerState 타입에는 T를 붙이지 않은 이유는 무엇인가?
다음 시간에 계속...
출처 & 참고
John Ahn 강사님의 강의
타입스크립트 유틸리티 타입 총정리, inpa, 2024.06.24
타입스크립트 Generic 타입 정복하기, inpa, 2024.06.24
'개발 > 프로그래머스 데브코스' 카테고리의 다른 글
| 프로그래머스 데브코스 58일차 with. TS 웹 풀스택 (0) | 2024.06.26 |
|---|---|
| 프로그래머스 데브코스 57일차 with. TS 웹 풀스택 (0) | 2024.06.25 |
| 데브코스 스터디(알고리즘, 코테) 17회차 (0) | 2024.06.24 |
| 프로그래머스 데브코스 55일차 with. TS 웹 풀스택 (0) | 2024.06.21 |
| 데브코스 스터디(알고리즘, 코테) 16회차 (0) | 2024.06.20 |