React 프로젝트 설정하기
React 앱이 실행되기 전에 최소한의 도구 설정이 필요합니다. JSX를 브라우저가 실행할 수 있는 JavaScript로 변환하는 작업도 필요하고, 개발하는 동안 그 결과를 제공하는 서버도 필요합니다. Vite는 둘 다 해결하고, 약 30초 안에 동작하는 프로젝트를 만들어 줍니다. 이 장에서는 빈 폴더에서 출발해 실행 중인 앱까지 갔다가, 가장 중요한 한 가지 경로를 따라가봅니다: 컴포넌트가 화면에 나타나는 방식입니다.
프로젝트 생성하기
Vite는 프로젝트 생성기가 내장된 빌드 도구입니다. npm create로 실행하고 두 가지 질문에 답하면 프로젝트가 생깁니다:
npm create vite@latest my-react-app
cd my-react-app
npm install
npm run dev첫 번째 명령은 어떤 프레임워크를 사용할지 묻습니다 (React 선택), 그리고 어떤 형식인지 묻습니다 (JavaScript 선택, 또는 TypeScript를 사용한다면 TypeScript). my-react-app이라는 폴더를 만들고 React 프로젝트에 필요한 모든 것을 넣습니다. npm install은 package.json에 나열된 의존성을 내려받습니다. 여기에 React 자체도 포함됩니다. npm run dev는 개발 서버를 시작하고 로컬 주소를 출력합니다. 보통 http://localhost:5173입니다. 브라우저에서 이 주소를 열면 시작용 앱이 실행됩니다.
생성된 폴더에는 초반에 중요한 파일들이 몇 개 있습니다:
index.html: 브라우저가 로드하는 단일 HTML 페이지입니다.src/main.jsx: 진입점입니다. React가 페이지에 붙는 곳입니다.src/App.jsx: 최상위 컴포넌트입니다. 가장 먼저 수정할 파일입니다.src/assets/: 컴포넌트에 import하는 이미지와 다른 정적 파일들입니다.package.json: 의존성과dev,build,preview스크립트입니다.vite.config.js: 빌드 설정입니다. 한동안은 건드릴 필요가 없습니다.
각 부분이 연결되는 방식
세 개의 파일이 일렬로 서로 연결됩니다. index.html부터 시작해봅시다. 의도적으로 거의 비어있습니다:
<body>
<div id="root"></div>
<!-- 그 다음에 <script type="module" src="/src/main.jsx"> 태그가 있습니다 -->
</body>이 빈 <div id="root">는 React가 채울 수 있는 페이지의 자리입니다. 앱이 렌더링하는 모든 것이 여기 안에 들어갑니다. 아래의 module <script> 태그가 main.jsx를 로드합니다. React가 여기서 역할을 시작합니다:
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'
import './index.css'
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>
)이 호출을 오른쪽에서 왼쪽으로 읽어봅시다. document.getElementById('root')는 빈 div를 찾습니다. createRoot는 그것을 React root로 감쌉니다. 즉, React가 그 요소의 내용을 소유하게 되고 컴포넌트와 동기화 상태로 유지합니다. .render(...)는 거기에 무엇을 놓을지 알려줍니다: App 컴포넌트입니다.
<StrictMode>는 개발 전용 래퍼로, 내부의 모든 것에 대해 추가 검사를 실행하고 버그를 유발할 가능성이 있는 패턴에 대해 경고합니다. 페이지에는 아무것도 추가하지 않으며, 프로덕션 빌드에서는 자동으로 꺼집니다. 가장 눈에 띄는 효과는 Effects에서 나타나는데, 개발 중에 내부의 각 컴포넌트를 의도적으로 두 번 마운트합니다.
App.jsx는 자신을 export하는 일반적인 컴포넌트입니다:
export default function App() {
return <h1>Hello from React</h1>
}이것이 전부입니다. 브라우저가 index.html을 로드하고, 스크립트가 main.jsx를 로드하고, main.jsx가 root div에서 createRoot를 호출해서 <App />를 렌더링하고, App이 제목으로 보이는 JSX를 반환합니다. 여기서부터 작성하는 모든 컴포넌트는 App 내부 어딘가에 있으므로, 이 같은 경로를 통해 페이지에 도달합니다. main.jsx는 보통 한 번 작성되고 거의 다시 건드리지 않습니다. 대부분의 작업은 App.jsx와 그것이 끌어들이는 컴포넌트들에서 일어납니다.
버전 참고
React 17 이전에는 ReactDOM.render(<App />, document.getElementById('root'))를 진입점으로 사용했습니다. React 18에서 createRoot가 도입되었는데, 이것이 동시성 렌더링을 활성화하고, 이전 호출은 계속 작동하지만 deprecated 경고를 표시했습니다. React 19에서는 제거되어 ReactDOM.render가 이제 오류를 발생시킵니다. 여전히 오래된 것을 사용하는 튜토리얼에서는 createRoot가 그 자리에 쓸 줄입니다. History and versions에서 바뀐 내용을 확인하세요.
개발 서버
npm run dev는 Vite의 개발 서버를 시작하고, 작업하는 동안 터미널에서 계속 실행됩니다. 역할은 앱을 제공하고 수정 사항에 반응하는 것입니다.
App.jsx를 수정해서 저장하면 브라우저가 거의 즉시 업데이트됩니다. 수동으로 새로고침할 필요가 없습니다. 이것이 hot module replacement입니다: Vite가 변경된 모듈을 페이지에 푸시하고 제자리에 교체하면서, 나머지 앱은 계속 실행됩니다. 클릭해서 7까지 올린 카운터는 보통 수정 후에도 여전히 7을 표시하므로, 마크업을 수정하면서 설정한 상태를 유지할 수 있습니다. 일부 변경 사항은 여전히 전체 새로고침을 강제하며, Vite가 그것을 결정합니다.
프로젝트에는 다른 스크립트 두 개가 있습니다. npm run build는 dist 폴더에 최적화된 번들을 생성합니다. 이것이 배포하는 것입니다. npm run preview는 그 빌드된 결과를 로컬에서 제공하므로 배포 전에 확인할 수 있습니다. 개발 중에는 npm run dev만 필요합니다.
정적 자산 import하기
이미지는 src/assets 아래에 있고 import를 통해 컴포넌트로 들어옵니다:
import logo from './assets/logo.png'
export default function Header() {
return <img src={logo} alt="Company logo" />
}import는 그 이미지의 최종 URL을 담은 변수를 제공하고, 그것을 중괄호 안에서 src에 전달합니다. src="./assets/logo.png"를 평문 문자열로 작성하면 보통 망가집니다. 소스 폴더의 경로가 빌드된 결과의 경로와 거의 같지 않기 때문입니다.
대신 파일을 import하면 빌드 도구가 관여합니다. 코드가 logo.png에 의존한다는 것을 보고, 그것을 결과로 복사하고, 그 파일이 최종적으로 위치할 모든 URL을 제공합니다. 컴포넌트를 다른 폴더로 이동해도 상대 경로 import는 여전히 해석됩니다. 파일명을 잘못 입력하면 즉시 알아챕니다. import가 실패하기 때문입니다. 프로덕션에서 깨진 이미지를 발견하는 것이 아닙니다. 같은 패턴은 SVG, 폰트, 그리고 빌드 도구가 처리하는 법을 아는 다른 모든 파일에서 작동합니다.
index.html에 id root인 빈 div가 있고, main.jsx가 createRoot로 그 div를 잡아서 <App />를 그 안에 렌더링하고, App.jsx가 나타나는 것이 무엇인지 말해줍니다. 이 경로를 따라갈 수 있으면, 자신의 코드가 정확히 어디에 연결되는지 알게 됩니다. 다음: Components, 그 App.jsx를 채울 빌딩 블록들입니다.

