Module, Webpack 이란? (+React webpack 설정 직접해보기)

aeong98·2022년 10월 8일
1
post-thumbnail

웹팩(webpack) 이란 의존 관계에 있는 리소스들을 하나의 파일로 번들링하는 javascript 모듈 번들러 입니다.
웹팩을 이해하기 위해서는 우선 모듈 이란 개념을 인지하고 있어야 하기에, 관련 개념부터 정리해보려 합니다.

1. 모듈

💡 모듈 정리

  • 모듈은 개별적인 코드 조각 (export, import 를 통해 재사용가능)
  • 태생적으로 자바스크립트는 모듈을 지원하지 않는다.
  • ES6에서는 script 태스에 type=”module” 어트리뷰트를 통해 모듈을 사용할 수 있다.

모듈이란

모듈은 애플리케이션을 구성하는 개별적 요소로서 재사용 가능한 코드 조각을 말합니다. 이때 모듈은 자신만의 파일 스코프를 가집니다.

모듈은 개별적으로 존재했을 때는 재사용이 불가능하므로, 존재의 의미가 없고 다른 모듈에 의해 재사용되어야 의미가 있습니다. 따라서 모듈은 공개가 필요한 자산에 한정해 명시적으로 선택적 공개가 가능합니다. 이를 export 라고 합니다.

그리고 모듈 사용자는 import 를 통해 다른 모듈을 선택해 자신의 스코프 내로 불러들여 재사용할 수 있습니다.

이처럼 모듈을 통해 코드의 단위를 명확히 분리해서 애플리케이션을 구성할 수 있고 재사용성이 좋아서 개발 효율성과 유지보수성을 높일 수 있습니다.

자바스크립트와 모듈

자바스크립트는 태생적으로 모듈 시스템을 지원하지 않습니다. 다시말해 자바스크립트는 모듈이 성립하기 위해 필요한 파일 스코프와 import ,export 를 지원하지 않았습니다.

자바스크립트는 script 태그를 사용해서 외부의 자바스크립트 파일을 로드할 수는 있지만 파일마다 독립적인 파일 스코프를 갖지 않습니다.

다시 말해 script 태그로 분리해서 로그해도 자바스크립트 파일들은 결국 하나의 자바스크립트 파일 내에 있는 것 처럼 동작하게 됩니다.

ES6 모듈 (ESM)

이러한 상황에서 ES6는 클라이언트 사이드 자바스크립트에서도 동작하는 모듈 기능을 추가했습니다. 이제 대부분의 브라우저에서는 ES6 사용할 수 있습니다. 사용법은 아래와같습니다. script 태그의 type=”module” 어트리뷰트를 추가하면 로드된 자바스크립트 파일은 모듈로서 동작합니다. 이때 ESM의 파일 확장자는 mjs 를 사용할 것을 권장한다고 합니다.

<script type="module" src="app.mjs"></script>

2. Webpack

💡 웹팩 정리

  • 여러개의 파일을 하나로 묶어주는 정적 모듈 번들러
  • 디펜던시 그래프를 통해 의존성을 파악, 하나의 번들로 생성
  • loaders를 통해 Javascript 가 아닌 다른 유형의 파일 (ts, css, image 등..) 을 처리
  • plugins을 통해 결과물을 다른 형태로 변환 (ex.html)

Webpack

규모가 있는 시스템에서는 Javascript 파일이 여러개 존재하게 되는데, 이 많은 파일을 하나의 파일로 관리하기엔 어려움이 있습니다. 이렇게 여러개의 파일을 브라우저에서 로딩하는 것은 네트워크 비용이 그만큼 올라가 반응속도가 느려지게 됩니다. 더 나아가 각 파일의 변수 충돌의 위험이 존재하게 됩니다.

이를 해결하기 위해 여러 파일을 하나의 파일로 번들링 해주는 역할을 하는 것이 웹팩의 역할입니다.

Core Concept

1. 디펜던시 그래프 (dependency graph)

webpack이 애플리케이션을 처리하 때, 내부적으로는 필요한 모든 모듈을 매핑하고 하나 이상의 번들을 생성하는 디펜던시 그래프를 만듭니다.

** 디펜던시 그래프 : 하나의 파일이 다른 파일에 의존할 때마다 webpack 은 이것을 의존성으로 취급합니다. 이를 통해 webpack 은 이미지 또는 웹 폰트와 같은 코드가 아닌 에셋을 가져와 애플리케이션에 의존성으로 제공할 수 있습니다. 엔트리포인트에서부터 시작해서 애플리케이션에 필요한 모든 모듈을 포함하는 디펜던시 그래프를 재귀적으로 빌드한 다음 모든 모듈을 브라우저에 의해 로드되는 작은 수의 번들로 묶습니다.

webpack은 의존관계에 있는 자바스크립트, CSS, 이미지 등의 리소스를 하나의 파일로 번들링하는 모듈 번들러입니다. Webpack 을 사용하면 의존 모듈이 하나의 파일로 번들링되므로, 별도의 모듈 로더가 필요 없게 됩니다.

2. 엔트리 포인트 (Entry Point)

entry 는 webpack 이 내부의 디펜던시 그래프의 시작지점으로 사용하기 위한 설정입니다. 엔트리포인트가 직간접적으로 의존하는 다른 모듈과 라이브러리를 찾아냅니다.

// webpack.config.js

module.exports={
	entry: "./path/to/my/entry/file.js"
}

3. Output

output 은 생성된 번들을 내보낼 위치와 이 파일의 이름을 지정하는 방법을 webpack 에 알려주는 역할을 합니다.

// webpack.config.js
const path = require('path'); //pah 모듈은 파일경로를 지정하기 위해 사용되는 Node.js 모듈

module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js',
  },
};

4. Loaders

webpack은 기본적으로 Javascript 와 JSON 파일만 이해하는데, 로더를 사용하면 webpack 이 다른 유형의 파일(HTML, CSS, Images, 폰트 등) 을 처리하거나, 그들을 유효한 모듈로 변환해 디펜던시 그래프에 추가합니다. 로더를 적용하지 않고, css 파일을 임포트 하는 Javascript 파일을 웹팩으로 빌드하게 되면 에러가 발생합니다.

module의 rules 옵션 배열에 test, use 속성을 가진 객체를 가함으로써 webpack 에 loader 를 적용할 수 있습니다.

// webpack.config.js
module.exports = {
  entry: './app.js',
  output: {
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['css-loader']
      }
    ]
  }
}
  • test : 로더를 적용할 파일 유형 (일반적으로 정규 표현식 사용)
  • use : 해당 파일에 적용할 로더의 이름

정리하자면, 위 코드는 .css 로 끝나는 파일에 대해 css-loader 를 적용하겠다는 의미입니다.

5. Plugins

로더는 특정 유형의 모듈을 변환하는데 사용된다면, 플러그인을 활용해서 번들을 최적화하거나, 에셋을 관리하고, 환경 변수 주입과 같은 작업을 수행할 수 있습니다. (해당 결과물의 형태를 바꾸는 역할)

플러그인의 배열에는 생성자 함수로 생성한 객체 인스턴스만 추가될 수 있습니다.

// webpack.config.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  plugins: [
    new HtmlWebpackPlugin(),
    new webpack.ProgressPlugin()
  ]
}
  • HtmlWebpackPlugin : 웹팩으로 빌드한 결과물로 HTML 파일을 생성해주는 플러그인,
  • ProgressPlugin 웹팩의 빌드 진행율을 표시해주는 플러그인

3. Webpack Dev Server

웹팩 데브 서버는 웹 애플리에션을 개발하는 과정에서 유용하게 쓰이는 도구입니다. 웹팩의 빌드 대상 파일이 변경되었을 때, 매번 웹팩 명령어를 실행하지 않아도 코드만 변경하고 저장하면 웹팩으로 빌드한 후 브라우저를 새로고침 해줍니다.

특징

"scripts": {
  "dev": "webpack serve",
  "build": "webpack"
}

웹팩 dev server 를 실행하는 경우에는, 빌드한 결과물이 파일 탐색기나 프로젝트 폴더에서 보이지 않습니다. 구체적으로 얘기하면, 웹팩 데브 서버로 빌드한 결과물은 메모리에 저장되고 파일로 생성하지는 않기 때문입니다.

HMR(Hot Module Replacement)

module.exports = {
  devServer: {
    hot: true
  }
}

HMR은 브라우저를 새로 고치지 않아도 웹팩으로 빌드한 결과물이 웹 애플리케이션에 실시간으로 반영될수 있게 도와주는 설정입니다.

4. 실제로 webpack 설정 config 파일 작성해보기 (react 실행시켜보기)

1. package.json 파일 생성

yarn init - y  // 기본값 기반으로 package.json 생성 (데화형 세션 건너뜀)

2. 웹팩을 사용하기 위한 의존성 설치

yarn add -D 
webpack       // 모듈 번들러인 웹팩
webpack-cli   // 웹팩의 커맨드 라인 인터페이스
webpack-dev-server  // 개발 서버 제공
webpack-merge // 여러개 config 파일 구분해놓고 합쳐서 쓰기 위해서

3. 필요한 webpack 로더, 플러그인 설치

yarn add -D 
babel-loader //es6를 es6로 바꿔주는 바벨을 웹팩에서 사용기 위해
html-loader // html 을 읽기 위해 
html-webpack-plugin // 템플릿을 지정하거나, favicon 을 설정하기 위해
webpack-progress-plugin // webpack 번들 진행과정 확인을 위해

4. 필요한 babel preset 설치

리액트를 사용할것이기 때문에 최신 자바스크립트 문법(ES6), JSX 문법으로 작성된 코드를 ES5 문법으로 변환해주기 위한 바벨 로더들을 설치합니다. (구버전 웹 브라우저와 호환하기 위해서)

yarn add -D 
@babel/core         // 바벨을 사용하기 위해 꼭 포함되어야 하는 패키지
@babel/preset-env   // plugin 들을 포함한 preset 
@babel/preset-react // 리액트와 관련된 plugin 들을 포함한 preset

.babelrc 파일 (지역 설정 팡리) 을 만들고 preset 설정을 적용합니다.

{
    "presets": [
      "@babel/preset-env",
      "@babel/preset-react"
    ]
  }
// preset : 여러 개의 규칙을 한번에 적용할 때
// plugin : 규칙 하나를 미세하게 적용할 때 

5. 웹팩 설정 파일 만들기

웹팩 설정을 개발용(development) 와 배포 (production) 용으로 나누어 적용하기 위해 webpack merge 를 사용해보겠습니다.

webpack.config.js //공통
webpack.dev.config.js    //개발용
webpack.prod.config.js   //배포용

위와 같이 설정 파일을 구분해서 만들어놓고, 특정 상황에 따라 다른 설정파일을 사용하려면 커맨드라인에서 —config 플래그를 사용해 이를 변경할 수 있습니다.

  • webpack.config.js (공통 설정 파일)
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports={
    entry:'./src/index.js',
    module:{
        rules:[
            {
                test: /\.m?(js|jsx)$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                  loader: 'babel-loader',
                  options: {
                    presets: ['@babel/preset-env', '@babel/preset-react']
                  }
                }
              },
              {
                  test: /\.html$/,
                  use:[
                      {
                          loader : 'html-loader',
                          options:{
                              minimize:true,
                          }
                      }
                  ]
              }
        ],
    },
    plugins:[
        new webpack.ProgressPlugin(),
        new HtmlWebpackPlugin({
            template : 'public/index.html',
        })
    ]
}
  • webpack.dev.config.js (개발용)
const { merge } = require('webpack-merge');
const common = require("./webpack.config");

module.exports = merge(common, {
    mode: "development", 
    devServer:{
        host: 'localhost',
        port :3000,
        open: true,
    }
   
})
  • webpack.prod.config.js (배포용)
const { merge } = require('webpack-merge');
const common = require("./webpack.config");

module.exports = merge(common, {
    mode: "production", 
    output: {
        filename: 'bundle.[hash].js',
    },
})

6. 리액트 관련 의존성 설치, index.js, App.js index.html 파일 작성

  • src/App.js
import React from 'react';
const App = () => (
  <div>
    Hello, Webpack!
  </div>
);
export default App;
  • src/index.js
// 리액트 18 기준
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

const container = document.getElementById('app');
const root = createRoot(container);
root.render(<App tab="home" />);
  • public/index.html
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Webpack-for-react</title>
</head>
<body>
  <div id="app"></div>
</body>
</html>

7. package.json 에 개발서버 실행, 배포 실행 script 추가

"scripts": {
    "dev": "webpack-dev-server --config webpack.dev.config.js ",
    "build": "webpack --config webpack.prod.config.js"
  },

을 추가하고 yarn dev를 입력하면 리액트가 실행되는 것을 확인할 수 있어요~!

마치며

이직하고 온보딩 프로젝트를 진행하며 리액트 웹팩 설정을 직접했는데, 내가 사용하는 로더나, 플로그인에 대한 이해 없이 돌아가면 장땡(?) 이란 마인드로 세팅해 사용하지 않는 로더들도 붙여놓고 인지하지 못하는 불상사가 있었습니다.. 똑똑한 동료분들은 역시나 이걸 알아차리시고 이와 관련된 질문을 주셨고 제대로 대답하지 못했습니다. 하지만, 잘 모르면 공부하면되니깐!! 이번 기회에 webpack을 다시 공부해 보자는 생각으로 공식문서도 읽어보고 오랜만에 딥다이브 책도 펼쳐봤고, 내가 했던 webpack 설정파일도 조금 개선해서 다시 작성해 보았습니다. 아직 완전히 이해하지 못한 부분도 많지만 이번 기회로 내가 처음 작성하는 코드나, 사용보는 도구에 대해서는 제대로 알아보고 적용해야한다는 경각심을 가질 수 있어 좋은 시간이었다고 생각합니다.

소스코드 Github 링크

출처

profile
프린이탈출하자

0개의 댓글