Skip to content
This page has been auto-translated and may contain errors.View in English

React 프로젝트 설정하기

React 앱이 실행되기 전에 최소한의 도구 설정이 필요합니다. JSX를 브라우저가 실행할 수 있는 JavaScript로 변환하는 작업도 필요하고, 개발하는 동안 그 결과를 제공하는 서버도 필요합니다. Vite는 둘 다 해결하고, 약 30초 안에 동작하는 프로젝트를 만들어 줍니다. 이 장에서는 빈 폴더에서 출발해 실행 중인 앱까지 갔다가, 가장 중요한 한 가지 경로를 따라가봅니다: 컴포넌트가 화면에 나타나는 방식입니다.

프로젝트 생성하기

Vite는 프로젝트 생성기가 내장된 빌드 도구입니다. npm create로 실행하고 두 가지 질문에 답하면 프로젝트가 생깁니다:

bash
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 installpackage.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부터 시작해봅시다. 의도적으로 거의 비어있습니다:

html
<body>
  <div id="root"></div>
  <!-- 그 다음에 <script type="module" src="/src/main.jsx"> 태그가 있습니다 -->
</body>

이 빈 <div id="root">는 React가 채울 수 있는 페이지의 자리입니다. 앱이 렌더링하는 모든 것이 여기 안에 들어갑니다. 아래의 module <script> 태그가 main.jsx를 로드합니다. React가 여기서 역할을 시작합니다:

jsx
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하는 일반적인 컴포넌트입니다:

jsx
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 builddist 폴더에 최적화된 번들을 생성합니다. 이것이 배포하는 것입니다. npm run preview는 그 빌드된 결과를 로컬에서 제공하므로 배포 전에 확인할 수 있습니다. 개발 중에는 npm run dev만 필요합니다.

정적 자산 import하기

이미지는 src/assets 아래에 있고 import를 통해 컴포넌트로 들어옵니다:

jsx
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, 폰트, 그리고 빌드 도구가 처리하는 법을 아는 다른 모든 파일에서 작동합니다.

브라우저는 .jsx 파일이 무엇인지 모릅니다. 어떤 JavaScript 엔진에도 JSX 파서가 없으며, <App />는 평문 JavaScript에서는 구문 오류입니다. 브라우저가 받는 것은 이미 변환된 것입니다. Vite는 모든 .jsx 파일을 esbuild를 통해 실행하는데, 이것이 JSX를 함수 호출로 다시 씁니다: React 19의 자동 런타임에서 <App />_jsx(App, {}) 정도의 호출이 됩니다. 어떤 헬퍼를 얻을지는 모드에 달려있습니다. 프로덕션 변환은 react/jsx-runtime에서 jsx를 import하고, 개발 변환은 react/jsx-dev-runtime에서 jsxDEV를 import하고 소스 파일과 줄 번호를 지니는 추가 인자를 전달합니다. 이것이 React 경고가 콘솔에서 코드의 정확한 위치를 지적할 수 있는 방식입니다. .jsx 확장자는 대부분 이 파일이 그 변환이 필요한 구문을 포함한다는 신호를 도구에 보내는 것입니다. 페이지에 도달하는 시점에는 일반적인 JavaScript 모듈이 일반적인 함수를 호출합니다.

개발 서버와 프로덕션 빌드는 그 결과에 도달하는 서로 다른 경로를 취합니다. 개발에서 Vite는 네이티브 ES 모듈을 제공하고 요청 시 각 파일을 변환하므로, 시작 시간은 프로젝트 크기와 함께 거의 증가하지 않고 단일 수정은 그 한 모듈만 다시 확인합니다. npm run build는 Rollup으로 전환하는데, 전체 그래프를 번들하고, 사용하지 않는 export를 제거하고, 최소화하고, 코드를 chunks로 분할합니다. 두 경우 사이에 동작이 약간 다를 수 있으므로 npm run preview가 존재합니다.

이미지 import는 번들러가 비-JavaScript 파일도 모듈 그래프의 일부로 취급하기 때문에 작동합니다. import logo from './assets/logo.png'는 실제 JavaScript 모듈 import가 아닙니다. Vite가 이를 차단하고, 파일을 dist/assets으로 내보내고 내용 해시를 이름에 넣고, import를 그 최종 경로의 문자열 리터럴로 교체합니다. 예를 들어 /assets/logo-4f2a1c8b.png 같은 것입니다. 해시는 공격적인 캐싱을 안전하게 만드는 것입니다: 이미지를 바꾸면 파일명이 바뀌므로, 오래된 사본은 CDN에서 살아남지 않습니다. 크기 임계값 아래의 파일들 (기본값 4KB)은 별도의 요청을 건너뛰고 base64 data URL로 인라인됩니다. 개발에서 같은 import는 개발 서버가 제공하는 평문 경로로 해석되므로, devtools에서 보는 URL이 npm run dev와 실제 빌드 사이에 다릅니다.

Juno페이지에서 컴포넌트까지 한 가지 경로 설정은 네 가지 명령이고, 그 다음엔 잊을 수 있습니다. 붙들어야 할 것은 이 체인입니다: index.html에 id root인 빈 div가 있고, main.jsxcreateRoot로 그 div를 잡아서 <App />를 그 안에 렌더링하고, App.jsx가 나타나는 것이 무엇인지 말해줍니다. 이 경로를 따라갈 수 있으면, 자신의 코드가 정확히 어디에 연결되는지 알게 됩니다.
Juno페이지에서 컴포넌트까지 한 가지 경로npm create vite@latest, React를 고르고, install하고, npm run dev를 합니다. 거기서부터 index.html에서 main.jsx에서 App.jsx로: 진입 파일이 root div에서 createRoot를 한 번 호출하고 최상위 컴포넌트를 렌더링하며, 그 다음부터는 그 줄 아래에서 시간을 보냅니다. 빌드 도구가 경로를 해석하고 이미지에 fingerprint를 붙일 수 있도록 이미지를 import합니다.
Juno페이지에서 컴포넌트까지 한 가지 경로 설정이 존재하는 이유는 JSX가 런타임을 갖지 않기 때문입니다: esbuild는 <App />를 런타임 호출로 다시 쓰고, 빌드에서 react/jsx-runtimejsx, 개발에서 react/jsx-dev-runtimejsxDEV를 호출합니다. 브라우저에 도달하기 전이고, 자산 import는 번들러가 방출하는 hashed URL로 해석됩니다. 개발은 esbuild를 통해 번들되지 않은 ES 모듈을 실행하고, 프로덕션은 Rollup을 실행하므로, npm run preview를 둘이 동의하는지 확인하는 검사로 취급합니다. createRoot는 React 18 진입점이고, 트리를 동시성 렌더링에 옵트인하는 것입니다.

다음: Components, 그 App.jsx를 채울 빌딩 블록들입니다.