번들링과 웹팩

hyena_lee·2023년 3월 22일
0

Webpack

목록 보기
1/1
post-thumbnail

번들링의 필요성

내가 작성한 HTML, CSS, JavaScript 파일을 그대로 전송하는 것이 무슨 문제가 있을까요? 큰 문제가 없을 수 있습니다. 하지만 아래와 같은 상황들이 일어난다면, 어려움에 처할 수 있습니다.

  • 두 개의 .js 파일에서 같은 변수를 사용하고 있어서, 변수 간 충돌이 일어났습니다.
  • 딱 한 번 불러오는 프레임워크 코드가 8MB라서, 인터넷 속도가 느린 국가의 모바일 환경에서 사용자가 불편을 호소합니다.
  • 번들 파일 사이즈를 줄이기 위해서 파일의 공백을 모두 지웠는데, 가독성이 너무 떨어져서 코딩하기가 어렵습니다. 결국 그대로 공백을 되돌려서 코딩합니다.
  • 배포 코드가 너무 읽기 쉬워 개발을 할 줄 아는 사용자가 프론트엔드 애플리케이션을 임의로 조작하여 피해가 발생했습니다.

이 중에서 용량이나 조작에 관한 문제는 프론트엔드 개발에서만 생기는 일은 아닙니다. 게임에서도 충분히 생길 수 있는 일입니다.

Webpack이란

웹팩은 오픈 소스 자바스크립트 모듈 번들러로써 여러개로 나누어져 있는 파일들을 하나의 자바스크립트 코드로 압축하고 최적화하는 라이브러리입니다.

웹팩 장점은 무엇인가요?

  • 여러 파일의 자바스크립트 코드를 압축하여 최적화 할 수 있기 때문에 로딩에 대한 네트워크 비용을 줄일 수 있습니다.
  • 모듈 단위로 개발이 가능하여, 가독성과 유지보수가 쉽습니다.

많은 자바스크립트 파일과 module과 sass 파일이든지 이미지 에셋들을 배포를 위한 정적인 에섯들로 만들어 줍니다.

Create-React-App 패키지를 이용하면

리액트를 설치할 때 내부에서 이미 웹팩을 사용해서 Development Environment 개발 환경을 생성합니다. 그래서 리액트를 사용할 때 아무런 설정없이 다른 파일에 있는 함수를 import 하고 이미지를 사용할수 있고 CSS 그리고 소스 코드를 적용하면 바로 반영이 되는 등의 효과를 가져올수 있습니다.

여기에서는
Create-React-App을 이용하지 않고 웹팩을 처음부터 이용해서 개발환경을 만들어 보도록 하겠습니다.

- Entry

의존성 그래프의 시작점을 웹팩에서는 엔트리(Entry)라고 한다.
웹팩은 엔트리를 통해서 필요한 모듈을 로딩하고 하나의 파일로 묶는다.
여러 개의 엔트리가 존재할 수 있다.

- Output

엔트리에 설정한 자바스크립트 파일을 시작으로 하나로 묶는다. 그 후 번들된 결과물을 처리할 위치를 output에 기록한다.

- Loader

웹팩은 오직 JavaScript와 Json만 이해할 수 있다.
로더는 다른 Type의 파일(img, font, stylesheet 등)을 웹팩이 이해하고 처리 가능한 모듈로 변환시키는 작업을 한다.

- Plugin

로더가 파일 단위로 처리하는 반면 플러그인은 번들된 결과물을 처리한다.
로더가 변환하는 동안 플러그인은 bundle optimization, asset management and injection of environment과 같은 일을 진행할 수 있다.

- Module

프로그램을 구성하는 구성 요소의 일부
관련된 데이터와 함수들이 묶여서 모듈을 형성하고 파일 단위로 나뉘는 것이 일반적이다.
모듈화 프로그래밍은 기능별로 파일을 나눠가며 프로그래밍을 하는 것으로 유지보수가 쉽다는 장점이 있다.

바벨이란 무엇인가요?

최신 자바스크립트 문법을 지원하지 않는 브라우저들을 위해서 최신 자바스크립트 문법을 구형 브라우저에서도 돌수 있게 변환 시켜주는 라이브러리



// Babel Input: ES6(ES2015) arrow function
[1, 2, 3].map((n) => n + 1);


// Babel Output: ES5 equivalent
[1, 2, 3].map(function(n) {
  return n + 1;
});

폴더구조

dist - src 에 들어 있는 코드들이 배포를 위해서 정적인 에셋들로 모이게 되는 공간입니다.
react를 사용할때는 npm run build 명령어를 사용해서 나오는 폴더와 같은 공간입니다.
결국은 dist에 있는 파일을 이용해서 화면에 UI나 기능들이 보이게 됩니다.

src - 애플리케이션을 위해 작성해야하는 코드는 이 source 폴더 안으로 들어갑니다.

webpack 설치

npm init -y
npm i -D webpack webpack-cli
npm run build

이미 만들어진 라이브러리 import 해보기

npm install nanoid --save

npm run build

webpack 설정 파일 생성

webpack.config.js

  • 파일이나 디렉터리의 경로를 다룰 때 Node.js에서 있는 path 모듈을 사용할 수 있습니다.

  • path 모듈은 Node.js에 내장되어 있어 있기 때문에
    별도의 라이브러리 설치 없이 바로 불러와서 사용할 수 있습니다.

  • resolve()를 이용해서 경로를 만들수 있습니다.
    ex) path.resolve("Users", "john", "index.html")
    => 'Users/john/index.html'

const path = require('path');

module.exports = {
  mode: 'delelopment',
  // 시작점
  
  entry: path.resolve(__dirname, 'src/index.js'),
  
  // 웹팩 작업을 통해 생성된 결과물
  output: {
    path: path.resolve(__dirname, 'dist),
    filename: 'main.js',
  }
}
const path = require('path')

console.log(__dirname_)
// "/Users/hyena/Desktop/webpack-basic'
console.log(path.resolve(__dirname, 'src/index.js'))
// "/Users/hyena/Desktop/webpack-basic/src/index.js'

만약 여러개의 entry point를 가지는 경우

const path = require('path');
module.exports = {
  mode: 'development';
  //시작점
  entry: {
  	main: path.resolve(__dirname, 'src/index.js'),
},
  // 웹팩 작업을 통해 생성된 결과물
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  }

}
    

Webpack Loader

  • 로더(Loader)는 웹팩이 웹 애플리케이션을 해석할 때 자바스크립트 파일이 아닌 웹 자원(HTML, CSS, Images, 폰트 등)들을 변환할 수 있도록 도와주는 속성입니다.
npm i -D css-loader style-loader sass sass-loader

Styling

  • style-loader는 DOM에 스타일로 모듈 내보내기를 추가합니다.
  • css-loader는 리졸브된 가져오기로 CSS 파일을 로드하고 CSS 코드를 반환합니다.
  • less-loader는 LESS 파일을 로드하고 컴파일합니다.
  • sass-loader는 SASS/SCSS 파일을 로드하고 컴파일합니다.
  • postcss-loader는 PostCSS 을 사용해 CSS/SSS 파일을 로드하고 변환합니다.
  • stylus-loader는 Stylus 파일을 로드하고 컴파일합니다.

scss 파일 생성 및 import


npm run build

scss 파일 생성 및 import

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
        // Create `style` nodes from JS strings
          'style-loader',
          // Translates CSS into Common JS
          "css-loader',
          // Compiles Sass to css
          "sass-loader",
        ],
      },
    ],
  },
};

  • 역순으로 로더들이 작동 되기 때문에
    css-loader => style-loader
    style-loader는 css-loader가 css확장자의 파일을 번들링에 포함한 후에 태그를 이용해 해당 css 값들을 html문서에 적용시킬 수 있기 때문에, 이 순서를 지켜야한다.
npm i -D css-loader style-loader sass sass-loader
  • The css-loader interprets @import and url() like import/require() and will resolve them.

  • css-loader는 @import 및 url()을 import/require()와 같이 해석하고 해결합니다.

  • jsx처럼 자바스크립트를 이용한 마크업에 스타일링을 입힐 때는 import "style.css"와 같이 css 파일을 임포트 합니다.
    임포트 하는 것은 css 파일을 하나의 모듈로 취급하여 js 파일에서 불러 사용한다는 것입니다.
    여기서 css 파일을 모듈로 부르기 위해서 사용하는 로더가 css-loader이다.

style-loader

  • css-loader를 이용해 웹팩 의존성 트리에 추가한 후에 css에 작성한 string 값들을 style-loader를 이용해서 돔에 로 넣어줍니다.

  • style-loader는 css-loader를 이용해서 import 구문을 이용해 js 파일에 불러들여진 스타일 파일이 html 파일 안에 style 태그로 존재할 수 있게 합니다.

HTML Webpack Plugin

Plugin

  • 웹팩은 로더와 플러그인의 확장 기능이 있습니다.

  • 웹팩의 플러그인은 로더가 할 수 없는 다른 작업을 수행할 목적으로 제공됩니다.

  • 로더는 모듈을 output으로 만들어가는 과정에서 사용합니다.

  • 플러그인은 webpack으로 변환한 파일에 추가적인 기능을 더하고 싶을 때 사용합니다.
    (최종적인 결과물을 변형시킵니다.)

  • 플러그인은 웹팩의 기본적인 동작에 추가적인 기능을 제공하는 속성입니다.

HTML Webpack Plugin 종류

HTML Webpack Plugin

  • HtmlWebPackPlugin 은 웹팩이 html 파일을 읽어서 html 파일을 빌드할 수 있게 해 준다.
npm i -D html-webpack-plugin
plugins: [
	new HtmlWebpackPlugin({
    	filename: 'index.html',
      	template: 'src/index.html',
    })
]
  • 플러그인을 다양한 용도로 Configuration에서 여러 번 사용할 수 있으므로 new 연산자로 호출하여 플러그인의 인스턴스를 생성해서 사용합니다.
  • template에 있는 src/index.html 에 있는 소스코드가 filename에 있는 dist/index.html 로 만들어집니다.

Webpack Caching

  • 웹팩(Webpack) 컴파일로 생성된 파일에서 변경된 내용이 없다면 브라우저는 캐시 상태를 유지하고 그대로 사용하게 된다.
  • 여기서 브라우저가 변경 사항을 확인하는 방법 중 하나는 파일 이름입니다.
  • 그렇기 때문에 파일을 생성할 때 해쉬값을 줄 수 있습니다.

파일 이름에 Hash 값 주는법

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

  module.exports = {
    entry: './src/index.js',
    plugins: [
      new HtmlWebpackPlugin({
-       title: 'Output Management',
+       title: 'Caching',
      }),
    ],
    output: {
-     filename: 'bundle.js',
+     filename: '[name].[contenthash].js',
      path: path.resolve(__dirname, 'dist'),
      clean: true,
    },
  };
npm run build

소스 코드 변경 없이 빌드 => 아무런 파일도 생성되지 않습니다.

소스 코드 변경 후 빌드

console.log(nanoid());
console.log(getRandomAddress());
console.log(nanoid());
console.log(nanoid());
console.log(nanoid());

새로운 빌드 파일 생성

이전 해시 파일을 자동으로 지워주려면?

// 웹팩 작업을 통해 생성된 결과물
output: {
 	path: path.resolve(__dirname, 'dist'),
    filename: '[name][contenthash].js',
    clean: true
},
  

Webpack development server

const path = require('path');

module.exports = {
  //...
  devServer: {
    static: {
      directory: path.join(__dirname, 'public'),
    },
    compress: true,
    port: 9000,
  },
};

devServer: {
  static: {
    directory: path.join(__dirname, 'dist'),
  },
    compress: true,
    port: 3000,
    open: true,
  },
  • 제공되는 모든 항목에 대해 gzip 압축을 활성화합니다.

  • 서버가 시작된 후 브라우저를 열도록 dev-server에 지시합니다. 기본 브라우저를 열려면 true로 설정하세요.

devServer 를 이용하는 Script 생성

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

gzip 압축

  • 압축은 대역폭을 절약하고 사이트 속도를 높이는 간단하고 효과적인 방법입니다.
  • 원래는 구형 브라우저의 문제 때문에 자바스크립트 속도를 높일 때 gzip 압축을 권장하기가 힘들었습니다.
  • 하지만 이제는 대부분 신형 브라우저를 사용하기 때문에 gzip 압축을 사용합니다.

gzip 압축 없이 브라우저에서 데이터 보여주는 방법

  • 브라우저: 이봐, /index.html 경로에 있는 데이터를 전달해줘.
  • 서버: 알겠습니다. index.html의 데이터가 주변에 있는지 확인하겠습니다…
  • 서버: 찾았다! 다음은 응답 코드(200 OK)이며 파일을 보냅니다.
  • 브라우저: 100KB? 아야… 기다려, 기다려… 알았어, 로딩됐어.

여기서의 문제점은 ?
시스템은 작동하지만 그다지 효율적이지 않습니다. 100KB는 많은 텍스트이고 HTML은 중복됩니다.
모든 <html> ,<table> 및 <div> 태그에는 거의 동일한 닫는 태그가 있습니다.
문서 전체에 걸쳐 단어가 반복됩니다.

이러한 문제점을 해결하기 위해서 zip을 해주면 됩니다.

  • 브라우저: 안녕하세요, index.html을 가져올 수 있습니까?
    당신이 그것을 가지고 있다면 나는 압축 버전을 가져갈 것입니다.
  • 서버: 파일을 찾아보겠습니다. 네, 여기 있습니다.
    그리고 압축 버전을 사용하시겠습니까?
  • 서버: 알겠습니다. index.html(200 OK)을 찾았습니다. 압축하여 전송 중입니다.
  • 브라우저: 좋아요! 10KB에 불과합니다. 압축을 풀고 사용자에게 보여 드리겠습니다.

일반 이전 index.html 대신 .zip 파일을 브라우저(index.html.zip)로 보낼 수 있다면 대역폭과 다운로드 시간을 절약할 수 있습니다. 브라우저는 압축 파일을 다운로드하고 압축을 풀고 페이지가 빠르게 로드되어 사용자에게 보여줄 수 있습니다.

Babel Loader

  • ES6이상의 자바스크립트 코드는 인터넷 익스플로러 혹은 구버전 브라우저에서 지원이 안되는 경우가 있습니다.
    그렇기 때문에 이러한 구버전 브라우저에서도 최신 자바스크립트 코드로 이루어진 앱을 이용할 수 있게 ES5 이하의 코드로 프랜스파일링 하도록 하는 기능이 바벨 입니다.
    그리고 웹팩으로 파일을 번들링(bundling)할 때도 바벨을 사용할수 있게 해주는 것이 babel-loader 입니다.
npm install -D babel-loader @babel/core @babel/preset-env

바벨 로더 사용방법

module: {
  rules: [
    {
      test: /\.m?js$/,
      exclude: /(node_modules|bower_components)/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env']
        }
      }
    }
  ]
}
{
	tset: /\.js$/,
      exclude: /node_modules/,
      use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
      }
}

Resource Asset

  • 현재 png, svg, jpeg등의 에셋들을 사용하려고 하면 에러가 납니다.

해결 방법

webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
+ module: {
+   rules: [
+     {
+       test: /\.png/,
+       type: 'asset/resource'
+     }
+   ]
+ },
};
src/index.js

import mainImage from './images/main.png';

img.src = mainImage; // '/dist/151cfcfa1bd74779aadb.png'

모든 .png 파일은 출력 디렉토리로 내보내지고 해당 경로는 번들에 주입됩니다.

{
 	test: /\.(png|svg|jpg|jpeg|gif)$/,
    type: 'asset/resourse'
}

bundle analyzer

# NPM
npm install --save-dev webpack-bundle-analyzer
# Yarn
yarn add -D webpack-bundle-analyzer

사용방법

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
}

이 모듈 사용의 장점

  • 번들 내부에 무엇이 있는지 파악
  • 크기를 가장 많이 차지하는 모듈 알아보기
  • 최적화
  • 축소된 번들을 지원
  • 번들 모듈의 실제 크기를 얻기 위해 구문 분석
  • gzipped 크기를 확인
profile
실수를 두려워 말고 계속 도전 하는 개발자의 여정!

0개의 댓글