npx create-react-app
을 통해 리액트 프로젝트를 많이 구축해봤을 것이다.
오늘은 CRA
의 도움 없이, 직접 리액트 프로젝트를 구축해보며 Javascript, Typescript의 배경지식도 함께 늘려보자.
CRA
는 다양한 라이브러리를 기본적으로 설치해준다.
각 라이브러리별 역할을 좀 설명하자면 다음과 같다.
리액트는 js
환경이기에, 결국 브라우저에서 이 js
, html
, css
등 다양한 파일을 다운로드 받아야 한다.
리액트는 js 만으로 코드를 짜는데 어떻게 이미지 같은 리소스들과, js, html, css 들이 제공되는 것일까?
바로 번들러를 통해 제공되는 것이다.
번들러에는 vite
, webpack
이 제일 대표적이다.
오늘은 webpack
을 써보겠다.
브라우저는 익스플로러, 크롬, 파이어폭스 등.. 다양하게 있고, 이 브라우저 마저도 여러 버전이 존재한다.
즉, 어떤 구식 컴퓨터는 최신 javascript 버전의 문법을 이해 못하고, 최신식은 모든 환경의 javascript을 이해할 수가 있다.
이런 차이를 없애기 위해, 바벨은 es5 이하의 javascript 버전으로 변환하는 역할을 한다고 이해하면 쉽다.
리액트를 사용하니 리액트를 설치해야한다.
react
는 리액트의 코어 라이브러리로, jsx나 컴포넌트를 정의하는 이런 모든 문법들이 정의되어 있다.
즉 UI 를 구성하는 방법을 정의한 것이다.
react-dom
은 이렇게 react
에서 정의한 컴포넌트들을 렌더링 시키는 역할을 한다.
vDOM을 diff 알고리즘을 통해 Real DOM에 반영하는 것도 이 친구가 한다.
왜 따로 라이브러리를 만들었냐 하면, 웹이든 모바일이든 환경에 관계없이 렌더링 시키기 위함이라고 한다.
정리하자면 react
라이브러리는 단순히 UI를 구성하는 방법을 정의한 것이고, react-dom
에서 이런 UI를 렌더링 하는 방법을 정의한 것이다.
js 말고 ts로 개발하는 경우가 요즘 많다.
그래서 타입스크립트를 설정하는 것도 같이 있어야한다.
위의 요구사항이 되게 많다.
저 모든 것들이 보일러 플레이트 코드가 되어서 CRA
를 만든 것이지만.. 공부를 위해 해보자.
npm init -y
node.js 환경의 자바스크립트 프로젝트를 초기화 한다.
npm install react react-dom
리액트를 쓰니까 당연히 위 두개는 설치해야한다.
npm install -D @babel/core @babel/preset-react @babel/preset-env @babel/preset-typescript
@babel/core
: 바벨 코어
@babel/preset-react
: jsx를 js로 변환
@babel/preset-env
: ES6+를 ES5 이하로 변환
@babel/preset-typescript
: 타입스크립트를 자바스크립트로 변환
npm install -D webpack webpack-cli webpack-dev-server webpack-merge style-loader css-loader babel-loader html-webpack-plugin clean-webpack-plugin
webpack
: 웹팩 코어
webpack-cli
: 웹팩을 cli 환경에서 사용할 수 있도록 함
webpack-dev-server
: 리액트는 실시간으로 개발하며 서버를 돌릴 수 있어서 이를 지원함
webpack-merge
: 웹팩을 개발환경(dev), 배포환경(production) 을 분리해 사용할 수 있도록 함
style-loader
:.css
파일을<style>
태그로 바꿔서 html에 넣어줌
css-loader
:.css
파일을 자바스크립트가 이해할 수 있도록 변환
babel-loader
: jsx, es6+ 의 문법을 트랜스컴파일링
html-webpack-plugin
: 번들링을 하게 되면 보통dist/bundle.js
같은 게 생기는데 이를index.html
에 자동으로 넣어주는 등 도와줌
clean-webpack-plugin
: 번들링할 때 덮어씌우기 쉽도록, 이전에 번들링한 것들을 지움
이외에도 JSON-loader, SVG 등등 다양한 로더와 플러그인이 있는데 입맛대로 설치하자.
npm install -D typescript @types/react @types/react-dom
typescript
: 말 그대로 타입스크립트
@types/react
: 타입스크립트 환경의 react의 타입들을 지원
@types/react-dom
: 타입스크립트 환경의 react-dom의 타입들을 지원
@types/react 이런거를 설치했다고, react, react-dom을 설치하지 않아도 되는 것은 아니다.
타입스크립트 환경에는 타입을 명시해야 하기에, 기존 리액트의 types 들을 정의한 것을 라이브러리로 만들어 둔 것이라고 이해하자.
mjs
와 js
두 가지가 있는데, node.js 환경에서 configuration과 관련된 것은 comonJS 방식을 아직까지 선호하는 경향이 있다고 한다.(ChatGPT)
또한, webpack-merge
로 prod
환경과 dev
환경을 구분해서 하기에 3가지 파일이 필요하다.
// webpack.common.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: './src/index.tsx',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
resolve: {
extensions: ['.tsx', '.ts', '.jsx', '.js'],
},
module: {
rules: [
{
test: /\.(ts|tsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
},
},
},
{
test: /\.(png|jpe?g|gif)$/,
use: [
{
loader: "file-loader",
},
],
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
}),
new CleanWebpackPlugin(),
],
};
//webpack.dev.js
const path = require('path');
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
static: {
directory: path.join(__dirname, 'public')
},
compress: true,
port: 3000
}
});
// webpack.prod.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'production',
devtool: 'source-map'
});
각각의 내용이 궁금하면 웹팩 부분을 참고하자.
바벨을 사용하기에 어떤 라이브러리를 쓰는지 묶어줘야한다.
module.exports = {
presets: [
"@babel/preset-react",
"@babel/preset-env",
"@babel/preset-typescript",
],
};
타입스크립트를 사용하기 때문에, tsconfig.json
도 설정해야한다.
{
"compilerOptions": {
"target": "es5", /* 프로젝트의 타겟 JavaScript 버전 */
"lib": ["dom", "dom.iterable", "esnext"], /* 사용 가능한 라이브러리 목록 */
"allowJs": true, /* JavaScript 파일을 컴파일할 때 허용 여부 */
"skipLibCheck": true, /* .d.ts 파일의 검사를 건너뛰는 여부 */
"esModuleInterop": true, /* CommonJS 모듈과 함께 ES 모듈 인터프레이스를 사용할 수 있게 함 */
"allowSyntheticDefaultImports": true,/* import x from 'foo' 형식의 구문에서도 default가 없는 모듈을 기본 모듈로 인식 */
"strict": true, /* 엄격한 타입 검사 옵션 활성화 */
"forceConsistentCasingInFileNames": true, /* 파일명의 일관된 캐싱 강제 */
"noFallthroughCasesInSwitch": true, /* switch 문에서의 case 절에서의 case 절 누락 확인 */
"module": "esnext", /* ECMAScript 모듈 시스템 지정 */
"moduleResolution": "node", /* 모듈 해결 전략 지정 */
"resolveJsonModule": true, /* .json 파일을 import할 수 있게 함 */
"isolatedModules": true, /* 각 파일을 독립된 모듈로 처리 */
"jsx": "react", /* JSX 구문 사용 설정 */
"noEmit": true /* 출력 파일 생성하지 않음 */
},
"include": ["src"] /* 컴파일할 파일이 위치한 디렉토리 */
}
ChatGPT로 만들었다.. tsconfig.json 은 좀 더 공부해서 적용해보겠음!!
{
"scripts": {
"start": "webpack serve --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
},
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
// ...
}
webpack
을 사용하기 때문에, 환경을 분리한 dev
, prod
를 기준으로 start
, build
명령어를 만든다.
my-react-app/
├── public/
│ └── index.html
├── src/
│ ├── App.tsx
│ ├── index.tsx
│ └── styles.css
├── package.json
├── tsconfig.json
├── babel.config.js
├── webpack.common.js
├── webpack.dev.js
└── webpack.prod.js
이러한 구조가 되도록 만들어 줘야한다.
index.html
이 있어야 여기에 렌더링을 하기 때문에 public/index.html
을 만들고 src/
디렉토리 또한, react 코드를 넣어줘야하기에 위처럼 생성한다.
<!-- public/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>React App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
// src/index.tsx
import React from 'react';
import ReactDOM from "react-dom/client";
import App from './App';
>
const rootElement = document.getElementById('root');
const root = ReactDOM.createRoot(rootElement as HTMLElement);
>
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// src/App.tsx
import React from 'react';
const App: React.FC = () => {
return (
<div className="app">
<h1>Hello, React 18!</h1>
<p>This is a basic React TypeScript example with React 18.</p>
</div>
);
};
export default App;
npm run start
성공 적으로 잘 되는 것을 볼 수 있다.