CRA를 사용하지 않고 React 프로젝트 생성하기

CRA (create-react-app)는 매우 편리한 리액트 프로젝트 빌드 도구입니다.

Webpack, Babel 등 설정하기가 까다롭고 시간이 들어가는 작업을 한번에 해주니까요.

그러나 Babel 혹은 Webpack의 설정을 건드려야 할 경우, eject를 통해 숨겨져 있던 설정 파일들을 끄집어 내야 하는 번거로움이 존재합니다.

리액트 프로젝트를 생성할 때 Webpack과 Babel을 어떻게 구성해보는지 알아보기 위해서
한번 직접 리액트 프로젝트를 구성해 봤습니다.

다만 제가 구성한 방법으로는 Debugging에 몇몇 문제가 있어 실제 제작 프로젝트에는
CRA를 이용해 boiler plate를 생성하려고 합니다.

설치

이 프로젝트에서는 yarn을 사용합니다.

최초로 yarn init명령을 실행해 package.json 파일을 생성합니다.

yarn init -y

리액트의 핵심인 모듈들을 설치해줍니다.

이 때 build 이후에 module은 사용하지 않으므로 (개발환경에서만 사용하므로) devDependency로 설치합니다.

yarn add -D react react-dom

그리고 모듈 번들러인 Webpack을 설치합니다.

yarn add -D webpack webpack-cli webpack-dev-server

웹팩에 번들링에 필요한 바벨을 설치합니다.

yarn add -D babel-loader css-loader style-loader file-loader
  • babel-loader : JSX 및 ES6+ 문법을 트랜스파일링
  • css-loader : CSS 파일을 자바스크립트가 이해할 수 있도록 변환
  • style-loader : 변환된 CSS 파일을 style 태그로 감싸서 삽입
  • file-loader : 이미지 및 폰트 등의 파일 로딩

마지막으로 웹팩으로 번들링 할 때 필요한 플러그인들을 설치합니다.

  • html-webpack-plugin : HTML 파일에 번들링된 자바스크립트 파일을 삽입해주고 번들링된 결과가 저장되는 폴더에 옮겨줌
  • clean-webpack-plugin : 번들링을 할 때마다 이전 번들링 결과를 제거함
yarn add -D html-webpack-plugin clean-webpack-plugin

폴더 구조

이 보일러 플레이트에서 사용할 폴더들은 다음과 같습니다.

  • dist : 빌드된 파일들이 생성
  • src : 컴포넌트, 유틸 등 소스 파일들이 위치함
  • public : 빌드할 때 참고할 html 등 정적 파일

여기서 dist 폴더의 경우 build 명령을 수행할 때 자동으로 만들어 주므로 생성하지 않아도 괜찮습니다.

mkdir src public dist

바벨 설정

바벨 설정 파일인 babel.config.js 를 작성합니다.

module.exports = function (api) {
  api.cache(true);

  const presets = ["@babel/preset-env", "@babel/preset-react"];

  const plugins = [];

  return {
    presets,
    plugins,
  };
};

웹팩 설정

웹팩 설정 파일인 webpack.config.js 를 작성합니다.

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
  mode: "development",
  entry: {
    main: "./src/index.js",
  },
  resolve: {
    extensions: [".js", ".jsx"],
  },
  devtool: "eval-cheap-source-map", // source-map을 설정하는 부분
  devServer: {
    contentBase: path.join(__dirname, "dist"), // 이 경로에 있는 파일이 변경될 때 번들을 다시 컴파일
    compress: true, // Enable gzip compression for everything served
    port: 8080, // 각자의 portNumber 작성
    hot: true, // 모듈의 변화된 부분만 자동으로 리로딩하는 HMR(Hot Module Replacement)
    overlay: true, // 에러가 발생했을 때 브라우저에 띄울 것인지
    writeToDisk: true, // 메모리 뿐만 아니라 직접 파일로 만들 것인지
    open: true, // Tells dev-server to open the browser after server had been started. Set it to true to open your default browser.
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/, // 컴포넌트 파일을 읽어오는 규칙입니다.
        exclude: "/node_modules/",
        loader: "babel-loader",
      },
      {
        test: /\.css$/, // 스타일 속성 파일을 읽어오는 규칙입니다.
        use: [{ loader: "style-loader" }, { loader: "css-loader" }],
      },
      {
        test: /\.jfif$/,
        loader: "file-loader",
        options: {
          name: "[name].[ext]",
        },
      },
    ],
  },
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js",
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      // index.html에 output에서 만들어진 bundle.js를 적용하여, dist에 새로운 html 파일 생성
      template: `./public/index.html`,
    }),
  ],
};

몇가지 중요한 속성을 살펴보겠습니다.

  • entry : 모듈의 의존성이 시작되는 부분으로 이름을 지정할 수 있고 여러개를 만들 수 있음.
  • resolve : 웹팩이 모듈을 처리하는 방식 정의하는 것으로 확장자를 생략하고도 인식하게 함.
  • devtool : 참고링크 source-map을 설정하는 부분으로 에러가 발생했을 때 번들링된 파일에서 어느 부분에 에러가 났는지를 쉽게 확인할 수 있게 해주는 도구.
  • devServer : webpack-dev-server의 옵션을 설정해주는 부분. 자세한 설명은 주석으로 작성했습니다.

package.json 에 script 추가

webpack-dev-server 에 --progress 옵션을 주는 경우, console에 결과가 나타납니다.

(Output running progress to console)

{
  "scripts": {
    "start": "webpack-dev-server --progress --mode development",

    "build": "webpack --progress --mode production"
  }
}

리액트 컴포넌트 생성

public 폴더에 다음과 같은 index.html 파일을 생성합니다.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>리액트 프로젝트 시작</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

src 폴더에 entry point 파일과 (index.js), 컴포넌트 파일을 생성합니다. (App.jsx)

// index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";

ReactDOM.render(<App />, document.querySelector("#root"));
// App.jsx
import React from "react";

const App = () => {
  return <div>hello world!</div>;
};

export default App;

실행

개발환경의 경우 다음 명령어를 통해 실행해 볼 수 있습니다.

yarn start

빌드

다음 명령어를 통해 빌드해 볼 수 있습니다.

yarn build

타입스크립트 개발 환경 구성해보기

우선 타입스크립트와, 타입 정의 파일들을 설치해야합니다.

yarn add -D typescript @types/react @types/react-dom

타입스크립트 명령어를 사용하면 typescript 설정 파일을 생성할 수 있습니다.

npx typescript --init

tsconfig.json 파일이 자동으로 생성됩니다.

여기서 리액트 jsx 코드를 사용하기 위해서는 compilorOptions의 jsx 속성에 "react" 값을 추가합니다.

{
  "compilorOptions": {
    "jsx": "react"
  }
}

Webpack으로 빌드하기 위해 ts-loader를 설치합니다.

yarn add -D ts-loader

그리고 Webpack 설정 파일에 다음 내용들을 추가합니다.

module.exports = {
  // 엔트리 포인트
  entry: "./src/index.tsx",

  // 빌드 결과물을 dist/main.js에 위치
  output: {
    filename: "main.js",
    path: __dirname + "/dist",
  },

  resolve: {
    // 파일 확장자 처리
    extensions: [".ts", ".tsx", ".js"],
  },

  module: {
    rules: [
      // .ts나 .tsx 확장자를 ts-loader가 트랜스파일
      { test: /\.tsx?$/, loader: "ts-loader" },
    ],
  },
};

+ Recent posts