
🎯 React와 TypeScript를 활용한 Trello 클론 프로젝트를 시작하기 전, 필요한 환경을 세팅합니다.
$ npm init vite
✔ Project name: » ./
✔ Select a framework: » React
✔ Select a variant: » TypeScript
$ npm install
$ npm run dev
Vite 템플릿 기반의 프로젝트로 설정하고, ./ 현재 폴더에 프로젝트를 생성합니다.
React + TS 를 선택해주고 라이브러리를 설치해주고 서버를 실행하면 http://localhost:5173/ 주소로 실행 중인 프로젝트를 확인할 수 있습니다.
⚡️ Vite는 왜 등장했을까?
Vite는 빠르고 가벼운 프론트엔드 개발용 빌드 도구이자 개발 서버입니다. 기존에는 주로 "Webpack"이라는 번들러를 사용해 빌드를 진행했지만, '사전 번들링 + 전체 분석' 구조 때문에
- 첫 실행이 느리고,
- 새로고침이 느리고,
- 설정이 복잡하는 등의 단점이 있었기에
이러한 문제를 해결하기 위해 더 빠르고 간단한 개발 환경을 제공하는 Vite가 등장했습니다.
🤔 그럼 Vite는 어떤 구조이길래 빠를까?
Vite는 모듈을 미리 번들링하지 않고, 브라우저가 요청한 파일만 즉시 처리합니다. 개발 중에는 ES 모듈(ESM)을 활용해,
import된 파일을 실시간으로 불러와 실행하는 방식입니다.
서버를 실행하고 브라우저에서 열어보면, 리액트 앱이 잘 작동하고 있는 것을 확인할 수 있습니다.

그런데, 유일한 HTML 파일인 index.html을 살펴보면 내용이 거의 비어 있습니다.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
그리고 실제 화면의 요소를 확인해보면 <div id='root'></div> 사이에 내용이 추가된 것을 확인할 수 있습니다.

이것이 바로 리액트의 동작원리인 SPA (Single Page Application) 구조의 핵심입니다. SPA는 하나의 HTML 파일을 기반으로 동작하며, JS가 필요한 화면을 동적으로 그려주는 방식입니다.
리액트는 index.html에서 단 하나뿐인 root 요소를 기준으로 전체 화면을 구성합니다. 즉, 페이지 이동처럼 보여도 실제로는 전체 HTML이 다시 로드되는 게 아니라, 필요한 부분만 자바스크립트가 교체하면서 작동하는 방식입니다.
🤔 그럼 기존에는 어떤 방식을 사용한 걸까?
기존의 웹 방식인 MPA(Multi Page Application)에서는 사용자가
/products페이지로 이동하면 브라우저는products.html파일을 새로 요청하고, 그에 따라 페이지 전체가 다시 로드되었습니다.
출처: https://www.hestabit.com/blog/single-page-vs-multi-page-web-apps/
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
@reduxjs/toolkit : Redux 공식 툴킷으로 createSlice, configureStore 등을 통해 상태 관리 로직을 더 간결하고 안전하게 작성할 수 있도록 도와줍니다.
redux : 전역 상태 관리를 위한 핵심 라이브러리입니다. @reduxjs/toolkit이 이 위에서 작동합니다.
react-redux : React에서 Redux store와 연결하기 위한 공식 바인딩 라이브러리입니다. useSelector, useDispatch 등의 훅을 제공합니다.
clsx : 조건부로 className을 쉽게 조합할 수 있게 해주는 유틸리티입니다. 예: clsx(isActive && 'active')
@vanilla-extract/css : 타입 안정성이 높은 CSS-in-TypeScript 라이브러리로, .css.ts 파일 내에서 스타일을 정의할 수 있습니다.
@vanilla-extract/css-utils : 바닐라 익스트랙트에서 자주 사용하는 유틸리티 함수 모음입니다.
@vanilla-extract/vite-plugin : 바닐라 익스트랙트를 Vite에서 사용할 수 있도록 해주는 플러그인입니다.
react-icons : Font Awesome, Material Icons 등 다양한 아이콘들을 React 컴포넌트로 사용할 수 있게 해주는 라이브러리입니다. (예: <FaTrash />, <MdEdit />)
uuid : 고유 ID를 생성해주는 유틸리티로, 예를 들어 task나 board의 고유 키 생성 등에 사용됩니다. (예: uuid())
react-beautiful-dnd : 리스트나 카드 등의 요소에 드래그 앤 드롭 기능을 쉽게 구현할 수 있게 해주는 라이브러리입니다.
⚠️ 발생한 오류
현재 프로젝트는
react@"^19.0.0"로react-beautiful-dnd는react@"^16.8.5 || ^17.0.0 || ^18.0.0까지만 지원하기 때문에 나는 오류였습니다.npm error code ERESOLVE npm error ERESOLVE unable to resolve dependency tree npm error npm error While resolving: react-task-app@0.0.0 npm error Found: react@19.1.0 npm error node_modules/react npm error react@"^19.0.0" from the root project npm error npm error Could not resolve dependency: npm error peer react@"^16.8.5 || ^17.0.0 || ^18.0.0" from react-beautiful-dnd@13.1.1 npm error node_modules/react-beautiful-dnd npm error react-beautiful-dnd@"*" from the root project npm error npm error Fix the upstream dependency conflict, or retry npm error this command with --force or --legacy-peer-deps npm error to accept an incorrect (and potentially broken) dependency resolution. npm error npm error npm error For a full report see: npm error C:\Users\MSI\AppData\Local\npm-cache\_logs\2025-04-10T08_57_19_448Z-eresolve-report.txt npm error A complete log of this run can be found in: C:\Users\MSI\AppData\Local\npm-cache\_logs\2025-04-10T08_57_19_448Z-debug-0.log
💡 해결 방법
리액트의 버전을 낮추고 다시 설치를 해주면 됩니다.
npm install react@18 react-dom@18
리액트 컴포넌트끼리 상태를 공유하려면 보통 props로 주고받거나 context API를 써야합니다. 하지만 컴포넌트가 많아질수록 상태 전달이 점점 복잡해지고 관리하기 어려워집니다.
Redux는 이런 문제를 해결하기 위해, 애플리케이션의 상태를 한 곳에서 관리할 수 있도록 도와주는 중앙 저장소(Store)를 제공합니다.
store : 앱에서 사용하는 모든 상태(state)를 보관하는 공간
action : 상태를 어떻게 바꿔야 할지 설명하는 객체
reducer: 액션을 받아서 실제로 상태를 변경해주는 함수
버튼 클릭 → 카트 추가 Action 발생 → Reducer가 store에 상태 반영 → 반영된 상태를 화면에 다시 렌더링

📁 my-vite-app
├── 📁 node_modules # 프로젝트에 설치된 외부 라이브러리
├── 📁 public # 정적 파일 위치
├── 📁 src # 소스코드 루트
│ ├── 📁 assets # 이미지, SVG 등 정적 자산
│ ├── 📁 components # 화면을 구성하는 컴포넌트 모음 (기능 단위로 폴더 나눔)
│ │ ├── 📁 ActionButton
│ │ │ ├── ActionButton.tsx # 액션 버튼 컴포넌트
│ │ │ └── ActionButton.css.ts # 바닐라 익스트랙트로 작성한 버튼 스타일
│ │ ├── 📁 DropDownForm
│ │ │ ├── DropDownForm.tsx # 드롭다운 입력 폼
│ │ │ └── DropDownForm.css.ts # 바닐라 익스트랙트로 작성한 드롭다운 스타일
│ │ ├── 📁 BoardList
│ │ │ └── BoardList.tsx # 여러 보드를 렌더링하는 리스트
│ │ ├── 📁 SideForm
│ │ │ ├── SideForm.tsx # 보조 입력 폼 (사이드바 폼 등)
│ │ │ └── SideForm.css.ts # 바닐라 익스트랙트로 작성한 보조 입력 폼 스타일
│ │ ├── 📁 List
│ │ │ ├── List.tsx # 단일 리스트 (To-do 같은 칼럼)
│ │ │ └── List.css.ts # 바닐라 익스트랙트로 작성한 리스트 스타일
│ │ ├── 📁 ListsContainer
│ │ │ ├── ListsContainer.tsx # 리스트 컨테이너 컴포넌트
│ │ │ └── ListsContainer.css.ts # 바닐라 익스트랙트로 작성한 리스트 컨테이너 스타일
│ │ ├── 📁 LoggerModal
│ │ │ ├── LoggerModal.tsx # 사용자 액션 로그 모달
│ │ │ ├── LoggerModal.css.ts # 바닐라 익스트랙트로 작성한 로그 모달 스타일
│ │ │ └── LogItem.tsx # 단일 로그 항목 컴포넌트
│ │ ├── 📁 ModalEdit
│ │ │ ├── ModalEdit.tsx # 수정용 모달
│ │ │ └── ModalEdit.css.ts # 바닐라 익스트랙트로 작성한 수정용 모달 스타일
│ │ └── 📁 Task
│ │ ├── Task.tsx # 단일 작업(Task) 카드
│ │ └── Task.css.ts # 바닐라 익스트랙트로 작성한 카드 스타일
│ │
│ ├── 📁 hooks
│ │ └── redux.ts # redux 전용 훅
│ │
│ ├── 📁 store
│ │ ├── 📁 reducer
│ │ │ └── reducer.ts # 모든 slice들을 combine해서 root reducer 생성
│ │ └── 📁 slices
│ │ ├── boardsSlice.ts # 보드 관련 상태 관리
│ │ ├── loggerSlice.ts # 로그 관련 상태 관리
│ │ └── modalSlice.ts # 모달 상태 관리
│ │
│ ├── 📁 types
│ │ └── index.ts # 프로젝트 전반에서 사용할 공통 타입 정의
│ │
│ ├── App.css.ts # 바닐라 익스트랙트로 작성한 App 컴포넌트 전역 스타일
│ ├── App.tsx # 최상위 App 컴포넌트
│ ├── index.css # 글로벌 CSS (리셋, 공통 스타일 등)
│ ├── main.tsx # 앱 진입점, 루트 DOM에 App 렌더링
│ └── vite-env.d.ts # Vite 전용 타입 정의
│
├── .gitignore # Git 추적에서 제외할 파일 목록
├── eslint.config.js # ESLint 설정 파일
├── index.html # Vite가 사용하는 HTML 템플릿
├── package.json # 패키지 매니페스트 (설정, 의존성, 스크립트 등)
├── package-lock.json # 의존성 버전 고정 파일 (npm)
├── README.md # 프로젝트 소개 문서
├── tsconfig.app.json # 앱 전용 TypeScript 설정
├── tsconfig.json # TypeScript 기본 설정
├── tsconfig.node.json # Node.js 환경용 TypeScript 설정
└── vite.config.ts # Vite 설정 파일
[store (상태)]
↑ ↓
dispatch useSelector
↑ ↓
[React Component]
이번 강사님은 빠르게 핵심만 짚고 바로 실습을 하시는 타입이라 처음 리액트를 배우는 나에겐 조금 벅차서 따로 블로그에 정리해가면서공부해야겠다.. 