PRACTICAL STARTER GUIDE
React + TypeScript + Vite
프로젝트 시작 실무 가이드
처음 프로젝트를 만들거나 기존 React 프로젝트에 합류할 때 필요한 설정만 추린 원페이지 가이드. 설치부터 alias, TypeScript 타입, Router, 환경변수, API 호출, 코드 품질 설정까지 한 번에 확인한다.
React
TypeScript
Vite
React Router
ESLint
Prettier
1. 프로젝트 생성
Vite의 react-ts 템플릿으로 시작하면 React + TypeScript 기본 설정이 이미 포함된다.
Node.js 버전 확인
node -v
npm -v
현재 Vite 기준으로 Node.js는 20.19+ 또는 22.12+가 필요하다. 프로젝트에서 별도 버전을 지정했다면 팀의
.nvmrc, Volta, package.json의 engines 설정을 우선한다.
프로젝트 생성
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm run dev
현재 폴더에 바로 만들고 싶다면:
npm create vite@latest . -- --template react-ts
처음 확인할 파일
| 파일 | 역할 |
|---|---|
src/main.tsx | React 앱 시작점. root DOM에 App을 렌더링한다. |
src/App.tsx | 기본 최상위 컴포넌트. |
vite.config.ts | Vite 설정, alias, plugin 등을 설정한다. |
tsconfig*.json | TypeScript 검사 및 모듈 해석 설정. |
package.json | 의존성 및 실행 명령어 관리. |
2. 실무형 폴더 구조
초기에는 너무 복잡하게 나누지 말고, 역할이 명확한 정도로만 시작하는 것이 좋다.
src/
├─ assets/ # 이미지, 아이콘, 폰트
├─ components/ # 공통 UI 컴포넌트
│ ├─ Button/
│ │ ├─ Button.tsx
│ │ └─ Button.module.css
│ └─ Modal/
├─ pages/ # 화면 단위 컴포넌트
│ ├─ Home/
│ └─ Login/
├─ layouts/ # 공통 레이아웃
├─ hooks/ # custom hooks
├─ services/ # API 호출
├─ types/ # 공통 TypeScript 타입
├─ utils/ # 공통 함수
├─ constants/ # 상수
├─ router/ # Router 설정
├─ styles/ # global.css 등
├─ App.tsx
└─ main.tsx
프로젝트가 작을 때는
stores/, features/, domains/ 같은 폴더를 처음부터 억지로 만들 필요는 없다. 실제 필요가 생겼을 때 확장한다.
3. @/ alias 설정
상대경로가 깊어지는 것을 막기 위해 @를 src에 연결한다.
vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { fileURLToPath, URL } from 'node:url'
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': fileURLToPath(
new URL('./src', import.meta.url)
),
},
},
})
tsconfig.app.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
이제:
import Button from '@/components/Button/Button'
import { formatDate } from '@/utils/date'
처럼 사용할 수 있다.
4. TypeScript에서 처음 알아야 할 것
React 프로젝트 초반에는 아래 타입 패턴만 익혀도 대부분의 컴포넌트를 읽고 작성할 수 있다.
기본 변수
const name: string = '홍길동'
const age: number = 30
const active: boolean = true
배열 / 객체
const names: string[] = ['A', 'B']
type User = {
id: number
name: string
active: boolean
}
const user: User = {
id: 1,
name: '홍길동',
active: true,
}
optional
type User = {
id: number
name?: string
}
name?은 값이 없어도 된다는 의미다.
Union Type
type ButtonVariant =
| 'primary'
| 'secondary'
| 'outline'
함수 타입
function add(a: number, b: number): number {
return a + b
}
실무에서는 모든 변수에 타입을 직접 붙이려고 하지 않아도 된다. TypeScript가 충분히 추론할 수 있는 값은 추론에 맡기고, props / API 응답 / 함수 인자 / 공용 데이터 구조를 명확히 타입화하는 것이 중요하다.
5. React 컴포넌트 + Props 타입
기본 컴포넌트
type ButtonProps = {
label: string
disabled?: boolean
onClick?: () => void
}
export default function Button({
label,
disabled = false,
onClick,
}: ButtonProps) {
return (
<button
type="button"
disabled={disabled}
onClick={onClick}
>
{label}
</button>
)
}
children이 있을 때
import type { ReactNode } from 'react'
type CardProps = {
children: ReactNode
}
export default function Card({
children,
}: CardProps) {
return <div>{children}</div>
}
이벤트 타입
import type {
ChangeEvent,
MouseEvent,
} from 'react'
function handleChange(
event: ChangeEvent<HTMLInputElement>
) {
console.log(event.target.value)
}
function handleClick(
event: MouseEvent<HTMLButtonElement>
) {
console.log(event.currentTarget)
}
useState
const [count, setCount] = useState(0)
const [name, setName] =
useState<string>('')
const [user, setUser] =
useState<User | null>(null)
6. CSS 구성
프로젝트 정책이 없다면 공통 스타일은 global CSS, 컴포넌트 스타일은 CSS Modules 방식이 관리하기 편하다.
src/
├─ styles/
│ ├─ reset.css
│ ├─ variables.css
│ └─ global.css
└─ components/
└─ Button/
├─ Button.tsx
└─ Button.module.css
Button.tsx
import styles from './Button.module.css'
export default function Button() {
return (
<button className={styles.button}>
확인
</button>
)
}
main.tsx
import '@/styles/reset.css'
import '@/styles/global.css'
7. React Router
SPA에서 URL에 따라 화면을 나누려면 Router를 추가한다.
설치
npm install react-router
간단한 BrowserRouter 방식
// main.tsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { BrowserRouter } from 'react-router'
import App from './App'
createRoot(
document.getElementById('root')!
).render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>
)
// App.tsx
import {
Route,
Routes,
} from 'react-router'
import Home from '@/pages/Home/Home'
import Login from '@/pages/Login/Login'
export default function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
</Routes>
)
}
기존 프로젝트에서
react-router-dom을 사용 중이라면 팀 프로젝트의 버전과 기존 import 방식을 그대로 따른다. 새 프로젝트는 현재 공식 문서의 react-router 기준을 확인하면 된다.
8. 환경변수
Vite에서 브라우저 코드에 노출할 환경변수 이름은 반드시 VITE_로 시작해야 한다.
.env
VITE_API_URL=https://api.example.com
사용
const apiUrl =
import.meta.env.VITE_API_URL
타입 선언
// src/vite-env.d.ts
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_URL: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
VITE_ 환경변수는 빌드 결과에서 브라우저가 읽을 수 있다. API 비밀키나 서버 전용 secret을 넣으면 안 된다.
9. API 호출 기본 구조
처음에는 fetch만으로도 충분하다. 프로젝트에서 Axios를 이미 쓰고 있다면 그 규칙을 따른다.
응답 타입
// types/user.ts
export type User = {
id: number
name: string
email: string
}
service
// services/userService.ts
import type { User } from '@/types/user'
const API_URL =
import.meta.env.VITE_API_URL
export async function getUsers(): Promise<User[]> {
const response =
await fetch(`${API_URL}/users`)
if (!response.ok) {
throw new Error('사용자 조회 실패')
}
return response.json()
}
컴포넌트에서 호출
const [users, setUsers] =
useState<User[]>([])
useEffect(() => {
async function loadUsers() {
try {
const data = await getUsers()
setUsers(data)
} catch (error) {
console.error(error)
}
}
loadUsers()
}, [])
10. ESLint / Prettier
Vite React TS 템플릿에는 ESLint 구성이 기본 포함될 수 있다. Prettier는 팀에서 사용한다면 추가한다.
Prettier 설치
npm install -D prettier
.prettierrc
{
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all"
}
.prettierignore
node_modules
dist
coverage
새 팀에 합류했다면 개인 취향으로 설정을 바꾸기 전에 반드시 기존
eslint.config.js, .prettierrc, EditorConfig를 먼저 확인한다.
11. 반드시 알아야 할 npm 명령어
| 명령어 | 용도 |
|---|---|
npm install | package.json 기준 의존성 설치 |
npm run dev | 개발 서버 실행 |
npm run build | TypeScript 검사 + production build 확인 |
npm run lint | ESLint 검사 |
npm install 패키지명 | 운영 의존성 추가 |
npm install -D 패키지명 | 개발 의존성 추가 |
npm uninstall 패키지명 | 패키지 제거 |
npm outdated | 패키지 버전 상태 확인 |
협업 프로젝트에서는 특별한 이유가 없다면
package-lock.json을 임의로 삭제하지 않는다.
12. VS Code 추천 확장
- ESLint — 코드 규칙 오류 표시
- Prettier - Code formatter — 코드 포맷팅
- EditorConfig for VS Code — 팀 들여쓰기/개행 규칙
- ES7+ React/Redux/React-Native snippets — React 코드 스니펫, 선택 사항
- GitLens — Git 변경 이력 확인, 선택 사항
저장 시 자동 포맷 예
// .vscode/settings.json
{
"editor.formatOnSave": true,
"editor.defaultFormatter":
"esbenp.prettier-vscode"
}
단, 회사 프로젝트에서는 저장 시 ESLint fix 정책이 별도로 있을 수 있으니 repository 설정을 우선한다.
13. Git 시작 전 체크
.gitignore 주요 항목
node_modules
dist
.env.local
.env.*.local
처음 프로젝트를 받았을 때
git clone 프로젝트주소
cd 프로젝트폴더
npm install
npm run dev
npm run lint
npm run build
dev 서버만 뜬다고 끝이 아니다. 기존 프로젝트에 합류했다면 첫날에
npm run build까지 성공하는지 확인하는 것이 중요하다.
14. 첫날 실무 체크리스트
- □ Node.js 버전 확인
- □ npm / pnpm / yarn 중 무엇을 쓰는지 확인
- □ lock 파일 확인
- □
npm install성공 확인 - □
npm run dev실행 확인 - □
npm run build성공 확인 - □ ESLint / Prettier 설정 확인
- □
@/alias 규칙 확인 - □ Router 구성 확인
- □ API base URL 위치 확인
- □ 환경변수 파일 규칙 확인
- □ 공통 컴포넌트 위치 확인
- □ CSS 방식 확인
- □ 상태관리 라이브러리 확인
- □ API 라이브러리(fetch/Axios) 확인
- □ Git branch / commit 규칙 확인
최소 학습 우선순위
| 순서 | 내용 | 중요도 |
|---|---|---|
| 1 | JSX / Props / 컴포넌트 | ★★★★★ |
| 2 | useState / 이벤트 처리 | ★★★★★ |
| 3 | 배열 map / filter / find | ★★★★★ |
| 4 | TypeScript props / object / union | ★★★★★ |
| 5 | useEffect / API 호출 | ★★★★☆ |
| 6 | React Router | ★★★★☆ |
| 7 | Context / 전역 상태 | ★★★☆☆ |
| 8 | useMemo / useCallback 최적화 | ★★☆☆☆ |
핵심: 처음에는 React API를 전부 외울 필요가 없다.
props → state → 이벤트 → 조건부 렌더링 → 배열 렌더링 → useEffect → API → Router 흐름을 먼저 익히면 실무 코드를 읽는 속도가 훨씬 빨라진다.