React 핵심 #01 | create-react-app 없이 시작하기
2022년 10월 05일 09:33
개요
리액트를 사용하면서 JSX, 가상Dom, Router 등등.. 다양한 개념에 대해 인지는 하고 있었지만 이것이 어떤 원리로 되는지 이해하지 못한 채로 매번 프로젝트를 진행하게 되니 내가 리액트를 활용하는 게 아닌, 프론트 개발을 하기 위한 레시피로 받아들이게 되는 것 같았다. (실제로 그동안의 개발에서 내 실수)
그래서 리액트 프로젝트를 처음 시작할 때 사용하는 create-react-app을 시작으로 React를 이해해보려 한다. 먼저 npx create-react-app {프로젝트 이름}을 통해 프로젝트를 시작하면 아래와 같은 구조를 띄게 된다.
이처럼 create-react-app(줄여서 CRA)는 번거로운 react 초기 셋팅 작업을 미리 해주고, 그 환경을 다시 패키징해주는 유용한 Boiler Plate이다. 생성된 각 폴더와 파일들을 파악하고, 유사하게 동작하는 node js 프로젝트를 만들어보자.
개념 및 원리
📌 create-react-app 패키지 구성
create-react-app을 실행한 직후 프로젝트 폴더의 구성은 다음과 같다.
프로젝트
├── README.md
├── package-lock.json
├── package.json
├── node_modules
│ └── ...
├── public
│ ├── 🗑 favicon.ico
│ ├── index.html
│ ├── 🗑 logo192.png
│ ├── 🗑 logo512.png
│ ├── 🗑 manifest.json
│ └── 🗑 robots.txt
└── src
├── 🗑 App.css
├── App.js
├── 🗑 index.css
├── index.js
├── 🗑 logo.svg
├── 🗑 reportWebVitals.js
└── 🗑 setupTests.js위 구성에서 파일명 앞에 🗑 아이콘이 붙은 파일은 학습 단계에서 삭제해도 실행에 영향을 미치지 않는 파일이다. src 폴더의 파일들은 App.js와 index.js가 참조하고 있으니, 삭제 후 코드를 수정해주어야 한다.
node_modules 폴더: node.js의 패키지 구성요소 중 하나로 외부 모듈을 저장하는 폴더이다. react도 외부 모듈이므로 여기에 저장된다. package.json파일과 package-lock.json파일이 있다면 npm install을 통해 해당 폴더가 생기며 모듈들이 다운로드 되므로 .gitignore파일에 명세해주자.
public 폴더: public 폴더 안의 파일은 Webpack이 후처리하지 않고, 빌드 폴더에 그대로 복사된다. (하지만 index.html은 bundle.js파일이 추가된다.)
favicon.ico: 웹 사이트의 아이콘- ²
index.html: 홈 화면에 표시되는 html로, 페이지 템플릿이다. logo192.png / logo512.png / manifest.json: 모바일 웹브라우저에서 사이트를 홈 화면에 추가 할 때 사용robots.txt: 검색 엔진이 웹사이트를 수집할 때 수집 할 것과 하지 말아야 할 것을 명시해주는 파일
src 폴더: 프로젝트 빌드시 Webpack에 의해 하나의 파일로 처리되는 코드들이며 주로 여기에 코드를 작성하여 react를 활용한다.
App.css / App.js: 앱이 실행되는 메인 코드와 그에 따른 stylesheet이다. js파일이지만 JSX로 작성되어 있다.App.test.js: App.js를 테스트 할 때 사용하는 파일이다.- ¹
index.js / index.css: index.html과 연결된 js파일과 그의 stylesheet. 자바스크립트의 진입점이다. setupTest.js: 테스팅 라이브러리를 import하는 파일logo.svg: 앱에서 사용하고 있는 svg파일. 삭제가능reportWebVitals.js: 앱의 퍼포먼스시간들을 분석해주는 기능을 담은 파일
³package.json & package-lock.json: 현재 프로젝트 패키지가 어떤 설정을 갖고있는지, 어떤 패키지를 사용하고 있는 지 확인할 수 있다. 패키지들을 연결하는 중요한 역할을 하고 있기 때문에 신중하게 수정해야 하고, 백업 없이 섣불리 삭제해서는 안된다.
📌 ¹src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<App color="blue" shadowSize={2}>
<div>hello</div>
</App>
);위의 코드에서 react와 react-dom모듈을 불러들인 것을 확인할 수 있는데, 이 모듈들은 package.json의 “dependencies”에서도 확인할 수 있다.
ReactDom모듈에서는 createRoot 메소드를 활용해 가상돔을 만들고, 이를 통해 나온 객체에서 render 메소드를 통해 public/index.html 파일의 #root에 해당하는 태그에 가상 dom을 렌더링 하는 것으로 보인다.
React모듈은 어디에서 쓰일까? 아마 <App/> 처럼 JSX를 사용하는 데 쓰이는 것처럼 보인다. 참고로 React 공식 문서에서 위의 코드는 Babel을 통해서 아래처럼 컴파일 된다고 한다.
React.createElement(
App,
{
color: "blue",
shadowSize: 2,
},
React.createElement("div", null, "hello")
);📌 ²public/index.html
index.html파일 내부에는 아래와 같은 div 태그를 확인 할 수 있다.
<!-- public/index.html -->
...
<div id="root"></div>
...해당 태그가 사용되는 곳은 ¹src/index.js파일에서 확인할 수 있다.
/* src/index.js */
...
const root = ReactDOM.createRoot(document.getElementById('root'));
...-
브라우저가 url을 통해 페이지를 요청하면 public폴더에서
index.html파일을 반환한다.정확히는 번들러(Webpack)에 의해 header에 <script defer src=“/static/js/bundle.js”></script>가 붙여진 번들링된 index.html이 반환된다.
- 해당 파일의
#root에 해당하는 태그 하위에src/index.js가 가상DOM을 활용하여 실행된 결과들이 들어간다.
📌 ³package.json
...
"dependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
...dependencies를 보니… 눈씻고 찾아봐도 번들링/컴파일 관련 모듈은 없는 것 같다. 그러면 어떻게 npm start를 할 때 번들링 된 파일이 반환되는 것일까? scripts를 찾아보면
...
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
...react-scripts, 이 놈이 범인이구나! 확인할 수 있다. 이 모듈에 babel, Webpack등을 사용하기 위한 public, src 같은 경로 설정, JSX를 자바스크립트 함수로 바꾸는 설정, HMR.. 등이 있을 것으로 예상이 된다.
구현 과정
앞으로 react-create-app을 활용하긴 할 것이지만 최소한의 코드로 이 패키지를 이해해보자.
0️⃣ 프로젝트 구조 및 환경
프로젝트
├── package.json ...(1)
├── public
│ ├── index.html ...(2)
└── src
├── index.js ...(3)
└── App.js$ npm install react react-dom react-scripts1️⃣ package.json에 scripts 추가하기
“0. 프로젝트 구조 및 환경”에서 패키지들을 설치했기 때문에 3개의 dependencies들이 추가되어 있을 것이다. 추가로 scripts에 아래 두개를 추가하자.
// public/package.json
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build"
},2️⃣ index.html 생성하기
<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html><div id=“root”></div> 가 들어간 간단한 html파일을 public폴더에 작성한다,.
3️⃣ index.js 작성하기
// src/index.js
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);마지막으로 App.js에 아래를 추가한다.
// src/App.js
export default function App() {
return <div>hello</div>;
}결과
엄청난 것처럼 작성해놨지만 사실 create-react-app이 하는 일은 3개의 패키지를 설치해서 그것들을 활용한 코드를 세팅해주는 것이 전부였다.(이외에도 web-vitals이나 test패키지들이 있긴 하다.) Boiler Plate가 하는 게 원래 그런 거긴 한데, 모르고 사용하는 것과 어느정도 알고 사용하는건 많이 다를 것 같다.
사실 create-react-app보다 이것으로 설치되는 3개의 패키지들이 리액트의 핵심 기능들로 볼 수 있다. 이제 다음 시간에는 [react-scripts 모듈 없이 시작하는 실습]을 진행하며 react-scripts를 파헤쳐보자.