CRA 없이 웹팩을 사용하여 React+TypeScript 개발 환경 세팅하기(1)

지누·2023년 8월 2일
1
post-thumbnail

1편에서는 React 개발 환경을 세팅하고, 2편에서 TypeScript와 함께 사용할 수 있는 개발 환경을 설정한다.


Create-React-App은 초심자용!

React를 처음 배울때, 그리고 지금 타입스크립트와 함께 사용하면서도 언제나 npx create-react-app 를 통해 사용했다. 개인 프로젝트를 진행하며 npm을 사용하여 패키지를 설치할 때 개발 의존성 패키지와 일반 의존성 패키지의 정확한 차이를 알아보다 CRA의 진실을 알게 되었다.

React로 프로젝트를 진행하려면 웹팩, 바벨, 번들, 로더, 플러그인 등 복잡한 개념이 많은데, 이를 편하게 하기 위한 것이 Create-React-App이었던 것이다. 내가 지금 진행하는 프로젝트도 복잡한 프로젝트는 아니지만, 웹팩, 바벨 등 세부 설정을 한번쯤 건드려 보는것도 좋을 것 같아서 시도하게 되었다.

1. 프로젝트 폴더 생성 및 초기 세팅

npm을 이용해서 진행할 것이므로, node.js가 설치되었다고 가정하자.

package.json 생성 및 초기화

npm init -y
  • npm이란 Node Package Manager의 약자로, node.js에서 사용하는 패키지를 관리해주는 툴이다.
  • -y 옵션은 초기화 과정에서 생기는 y/n 질문에서 모두 yes를 누르는 것이다.

올바르게 설치했다면 다음과 같은 package.json이 생성 되었다.

{
  "name": "frontend",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

2. 라이브러리 설치 - 프로덕션 의존성

라이브러리를 설치할 때는 세 가지 옵션을 선택할 수 있다.

1. 프로덕션 의존성(기본) - production dependencies

프로덕션 의존성으로 설치된 라이브러리는 개발 및 배포단계에서 사용된다.

npm i [라이브러리이름] 으로 설치할 수 있으며. package.json의 dependencies 프로퍼티에서 확인할 수 있다.

2. 개발 의존성 - development dependencies

개발 의존성으로 설치된 라이브러리는 개발 단계에서만 사용되며, 배포 단계에서는 포함되지 않는다.
일반 사용자들이 개발에 사용되는 패키지를 내려받을 필요가 없으므로, 프로그램 성능 향상에 도움이 된다.

npm i -D [라이브러리명] 또는 npm i --save-dev [라이브러리명]으로 설치할 수 있으며, package.json의 devDependencies 프로퍼티에서 확인할 수 있다.

3. 전역 설치

위의 두 방식은 프로젝트 내에 로컬로 설치하는 방법이고, 프로젝트 루트 디렉터리에 존재하는 node_modules 폴더 내에 패키지가 설치된다.

npm i -g [라이브러리명] 을 통해 전역으로 설치하는 경우 내 OS의 시스템 폴더에 존재하는 node_modules 폴더로 설치되며, 로컬에 설치하지 않아도 사용할 수 있게 된다.

다만 협업을 하는 경우 라이브러리가 설치되지 않아 오류가 발생할 수 있으니 권장하지 않는다.

이번에 설치할 라이브러리들은 React 애플리케이션이 동작하는데 필요한 라이브러리들이다.
npm i react react-dom react-router-dom

올바르게 설치했다면 package.json에 다음과 같은 dependencies 프로퍼티가 추가되었을 것이다.

{
  ...
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.14.2"
  },
}

3. 라이브러리 설치 - 개발 의존성

지금부터 설치할 라이브러리와 패키지들은 React 애플리케이션 동작과 직접적인 연관은 없지만, 개발에 필요한 것들이다.

3-1. 바벨(Babel) 설치

바벨(Babel)이란?

React에서는 ES6를 지원한다. 그러나 최신 문법인 ES6를 이해하지 못하는 브라우저를 위해 ES5이하의 하위 문법으로 트랜스파일링 해 주는 역할을 수행한다.

또한 React에서 사용하는 JSX문법도 브라우저는 이해하지 못하기 때문에, 일반적인 JavaScript코드로 변환해준다.

npm i -D @babel/core @babel/preset-react @babel/preset-env

  • @babel/core : es6 -> es5로 트랜스파일링 해주는 바벨의 핵심 라이브러리
  • @babel/preset-react : jsx->js로 트랜스파일링 해주는 라이브러리
  • @babel/preset-env : ECMAScript 버전(ES6, ES7..)을 지정하지 않아도 자동으로 탐지

프리셋 설정

최상위 루트에 babel.config.js 를 생성하고 다음과 같이 작성한다.

module.exports = {
 presets: ['@babel/preset-react', '@babel/preset-env'],
};

babel.config.js

config.js는 라이브러리, 패키지의 설정 파일의 한 형식이다. 객체 형태로 정의한다. .babelrc 파일 형태로 정의할 수도 있다.
babel.config.js는 바벨에 대한 플러그인들을 설정해주는 파일이다.

플러그인(plugin)

플러그인은 소프트웨어나 프레임워크의 기능을 확장하는데 사용되는 모듈이다. babel에서는 특정 기능의 변환을 수행하는데 사용되는 작은 코드 조각이나 라이브러리를 의미한다.

프리셋(preset)

babel 자체로는 동작하지 않고, 무언가 하게 하려면 플러그인을 설치해야 한다.
매번 플러그인을 설치하지 않기 위해 만들어 진 기능이 preset이며, presets는 플러그인들을 포함한 번들파일이다. preset에 한 번만 설정해 주면 플러그인들이 자동으로 설치된다.


3-2. 웹팩(Web Pack) 설치

웹팩(Web pack)이란?

웹을 개발하다보면 다양한 확장자의 파일이 여러개 존재하게 된다. 그리고 다양한 소스는 어플리케이션을 무겁게 만들고, 충돌 위험이 올라간다. 이를 해결하기 위해 등장한 도구가 번들러(Bundler)이다.

번들러는 여러개의 파일을 하나로 묶어주는 정리 방법이고, 대표적인 기술로 웹팩이 존재한다.
웹팩을 통해 하나의 js파일에 css, 이미지 파일 등의 모듈을 몰아넣을 수 있다.

npm i -D webpack webpack-cli webpack-dev-server

  • webpack : 웹팩의 코어 라이브러리
  • webpack-cli : 웹팩 커맨드라인을 사용할 수 있다.
  • webpack-dev-server : 웹팩으로 개발 서버를 구동할 수 있다.

스크립트 설정

웹팩을 사용하여 개발 서버를 실행할 것이므로, package.json의 스크립트를 다음과 같이 수정한다.

"scripts": {
    "dev": "webpack serve --mode development --open --hot",
    "build": "webpack --mode production"
  },

이후 npm run dev 명령어를 통해 개발 서버를 실행할 수 있다.

3-3. 로더(Loader) 설치

로더(loader)란?

로더는 웹팩과 같은 모듈 번들러에서 사용되는 도구로써, 웹 어플리케이션의 소스코드나 파일을 모듈로 해석하고 변환하는 역할을 한다.

웹팩이 여러개의 파일을 하나의 js파일에 넣을 수 있게 하는 것이고, 로더는 각각의 파일 형식에 대한 처리 방식을 지정하고 변환하여 웹팩, 웹 어플리케이션에서 사용할 수 있는 형태로 만들어 준다!

아래는 웹팩 번들링에 필요한 로더들이다.
npm i -D babel-loader style-loader css-loader file-loader

  • babel-loader : jsx 및 ES6 문법을 변환 해준다.
  • css-loader : css파일을 자바스크립트가 이해할 수 있도록 변환해준다.
  • style-loader : 변환된 css파일을 <style> 태그로 감싸서 삽입해준다.
  • file-loader : 이미지 및 폰트 등의 파일을 변환해준다.

3-4. 플러그인 설치

웹팩 플러그인은 웹팩의 기능을 확장하고 보완하여 더 많은 작업을 수행할 수 있게 해준다.
아래의 플러그인은 웹팩 번들링에 필요한 플러그인이다.
npm i -D html-webpack-plugin clean-webpack-plugin

  • html-webpack-plugin : HTML파일에 번들링 된 js파일을 삽입해주고, 번들링 결과를 저장하는 폴더로 옮긴다.
  • clean-webpack-plugin : 번들링 할 때마다 이전 번들링 결과를 지워준다.

3-5. 라이브러리 설치 완료

모든 라이브러리를 설치했다면 package.json이 다음처럼 되어있을 것이다.

{
  "name": "frontend",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.14.2"
  },
  "devDependencies": {
    "@babel/core": "^7.22.9",
    "@babel/preset-env": "^7.22.9",
    "@babel/preset-react": "^7.22.5",
    "babel-loader": "^9.1.3",
    "clean-webpack-plugin": "^4.0.0",
    "css-loader": "^6.8.1",
    "file-loader": "^6.2.0",
    "html-webpack-plugin": "^5.5.3",
    "style-loader": "^3.3.3",
    "webpack": "^5.88.2",
    "webpack-cli": "^5.1.4",
    "webpack-dev-server": "^4.15.1"
  }
}

4. 웹팩 설정 - webpack.config.js

babel의 설정 파일이었던 babel.config.js 처럼 웹팩의 설정 파일인 webpack.config.js 를 생성하자.
그리고 webpack.config.js 의 코드를 하나하나 파헤쳐 보자.

1. 웹팩 생성

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const port = 3000;

module.exports = {
  // webpack 설정 코드 작성. 작성된 코드는 module.export로 내보냅니다.
};
  • 웹팩과 html-webpack-plugin을 사용한다.
  • "const port = process.env.PORT || 3000;" 처럼 환경변수를 사용하여 지정하기도 한다.

2. mode

...
module.exports = {
  mode: 'development',
};
  • mode옵션은 웹팩 설정이 development 모드인지 production모드인지 정한다.
  • 배포단계가 아닌 개발 단계이므로 development를 사용했다.

3. entry, output


module.exports= {
  ...
  entry:'./src/index.js',
  output:{
      path: __dirname + '/dist',
      filename: 'bundle.[hash].js'
  }
}
  • entry 옵션은 앱이 있는 위치와 번들링 프로세스가 시작되는 지점이다.
  • webpack4 버전부터는 entry옵션을 생략 할 수 있고, src/index.js 가 기본값이다.
    실제 파일명이 index.jsx이어도 문제 없다!
  • output 옵션은 번들링 프로세스가 끝난 뒤 번들링된 파일을 저장할 장소와, 번들링된 파일의 이름을 지정한다.
  • __dirname은 node.js에서 사용하는 전역 변수로, 현재 실행중인 스크립트 파일의 디렉토리 경로를 나타낸다. 즉 현재위치/dist/ 에 번들링 파일을 저장한다는 뜻이다.
  • 웹팩은 번들 파일의 내용이 변경될 때 마다 새로운 해시 값을 생성하여 브라우저를 캐싱하고, 최신 파일을 유지한다. (이 부분은 이해가 잘 가지 않음..)

4. module

..
module.exports = {
  ...
  module: {
    rules: [

      // 첫 번째 룰
      {
        test: /\.(js)$/,
        exclude: /node_modules/,
        use: ['babel-loader']
      },

      // 두 번째 룰
      {
        test: /\.css$/,
        use: [
          {
            loader: 'style-loader'
          },
          {
            loader: 'css-loader',
            options: {
              modules: true,
              camelCase: true,
            }
          }
        ]
      }
    ]
  },
};
  • module 옵션은 번들링 과정에서 사용하는 규칙을 정의한다. 각 모듈에 적용할 로더를 지정한다.
  • rules 옵션에 규칙을 정의하며, 여러개의 규칙을 배열로 정의 가능하다.
    • test : 어떤 파일에 적용할 것인지, 적용할 파일의 확장자
      실제 파일명이 index.jsx이어도 문제 없음!
    • exclude : 로더에서 제외할 파일
    • loader : 적용할 로더를 설정
    • use : 적용할 로더가 두 개 이상이면 use 배열로 설정
  • 첫 번째 룰 예시
    node_modules 폴더를 제외한 모든 .js 파일을 babel-loader 규칙인 babel.config.js 규칙을 통해 변환한다.

5. plugin

module.exports = {
 ...
 plugins: [
   new HtmlWebpackPlugin({
     template: 'public/index.html',
   })
 ],
};
  • plugins 옵션은 웹팩 번들과정에 적용할 플러그인을 설정한다.
  • 위의 코드는, 번들링 된 파일 bundle.[hash].jsindex.html에 자동 삽입하라는 뜻이다.

6. devServer

module.exports = {
  ...
  devServer: {
    host: 'localhost',
    port: port,
    open: true,
    historyApiFallback: true
  }
};
  • devServer는 개발 서버를 정의하는 옵션이다.
    • host : 로컬 호스트로 지정
    • port : 포트 설정, 상단에port 변수가 존재함
    • open(true)는 서버를 실행할 때 자동으로 브라우저를 열어주는 옵션이다.
    • historyApiFallback : 브라우저에서 url을 변경할 수 있도록 도와주는 옵션이다.

최종 webpack.config.js 코드

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const port = 3000;

module.exports = {
    mode: 'development',
    entry:'./src/index.js', 
    output:{
        path: __dirname + '/dist',
        filename: 'bundle.[hash].js',
        publicPath: '/'
    },
    module: {
        rules: [
        // 첫 번째 룰
        {
            test: /\.(js)$/,
            exclude: /node_modules/,
            use: ['babel-loader']
        },
        // 두 번째 룰
        {
            test: /\.css$/,
            use: [
                {
                    loader: 'style-loader'
                },
                {
                    loader: 'css-loader',
                    options: {
                        modules: true,
                        localsConvention: 'camelCase',
                    }
                }
            ]
        }
    ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: 'public/index.html',
        })
    ],
    devServer: {
        host: 'localhost',
        port: port,
        open: true,
        historyApiFallback: true,
        hot: true
    }
};

5. 리액트 어플리케이션 만들기

index.html 작성

index.html은 public 폴더 내에 작성하도록 한다.

<!DOCTYPE html>
<html lang="en">
<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>webpack-react-</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>

App.jsx 생성

App.jsxindex.jsx는 src 폴더 내에 작성하도록 한다.

import React from "react";

const App = () => {
  return <div className="container">React!</div>;
};

export default App;

index.jsx 생성

import React from 'react';
import ReactDom from 'react-dom';
import App from './App';

ReactDom.render(<App />, document.getElementById('root'));

최종 파일 구조

6. 실행

npm run dev를 통해 개발 서버를 실행시키면 Create-React-App으로 만든 리액트 개발 화면이 나타난다..!

지금까지는 React 개발 환경을 세팅한 것이고, 원하는 라이브러리(styled-components 등)를 알아서 설치하면 된다.

대부분 처음 하는 내용이라 너무 힘들고 어렵다. 하루종일 시도하고 게시글 작성하는 데만 6시간이 걸렸다. 하지만 글로 작성하다 보니 완벽하게 이해를 할 수 있는 것 같아서 좋다!

타입스크립트로 프로젝트를 진행하는 방법은 다음 게시글에 이어서 하겠음!

profile
열심히 좀 살자😱

2개의 댓글

comment-user-thumbnail
2023년 8월 2일

이런 유용한 정보를 나눠주셔서 감사합니다.

답글 달기
comment-user-thumbnail
2023년 8월 3일

유익한 글 감사합니다 ~~ ☺️

답글 달기