프론트엔드 개발자를 위한 웹팩

#webpack

캡틴판교님의 프론트엔드 개발자를 위한 웹팩 강의를 수강하였다. 자세한 내용은 제공해주시는 웹팩 핸드북에 있지만, 개인적으로 강의를 수강하며 메모한 내용을 정리했다.


1. Node.js와 NPM

웹팩을 사용하기 위해서는 Node.js와 NPM이 컴퓨터에서 설치되어 있어야 한다.

Node.js

Node.js브라우저 밖에서도 자바스크립트를 실행할 수 있는 런타임 환경을 의미한다. 안정적인 LTS(Long Term Support) 버전 사용을 권장한다.

Node 명령어

// 버전 확인 $ node -v

NPM(Node Package Manager)

NPM(Node Package Manager)은 자바스크립트 라이브러리를 설치하고 관리할 수 있는 도구이다. (Node.js 설치 시 동반 설치)

NPM을 사용하는 이유

과거 script 태그 방식과의 차이점이다.

  • 관리 효율성: package.json 파일 하나로 어떤 라이브러리를 썼는지 한눈에 확인 가능
  • 의존성 해결: 라이브러리끼리 서로 필요한 버전이 꼬이는 문제를 NPM이 자동으로 해결
  • 번거로움 해소: 매번 라이브러리 배포 페이지에서 CDN 주소를 복사해올 필요가 없음

NPM 명령어

// 버전확인 npm -v // 프로젝트 초기화 (package.json 자동 생성) npm init -y // 라이브러리 설치 (node_modules 생성) npm install [패키지명] // 라이브러리 삭제 npm uninstall [패키지명]

NPM 설치 옵션

  • 전역 설치 -g 또는 —global 시스템 레벨 설치, CLI 도구(명령어) 사용 시 유용 (전역 설치 경로 window %USERPROFILE%\AppData\Roaming\npm\node_modules mac /usr/local/lib/node_modules)

  • 지역(배포용) —save-prod (기본값) dependencies에 등록, 앱 로직과 직접 연관된 라이브러리

  • 지역(개발용) -D 또는 —save-dev devDependencies에 등록, 빌드 도구/테스트 도구 등 개발 시에만 필요

dependencies와 devDependencies의 차이 설치되는 경로는 로컬 node_modules로 같다. dependencies는 애플리케이션의 로직과 직접적인 연관이 있는 배포용 라이브러리, devDependencies는 개발을 할 때 필요한 개발용 라이브러리를 의미한다. 개발용 라이브러리는 배포시 애플리케이션 코드에서 빠지게 되므로 주의해서 옵션을 입력한다. 반대로 배포용 라이브러리에 다 집어넣으면 빌드 시간이 너무 오래걸린다.


2. Webpack

개념

웹팩은 여러 개의 자바스크립트 파일과 웹 자원(CSS, 이미지 등)을 하나의 파일로 합쳐주는 모듈 번들러이다.

웹팩에서 지칭하는 모듈이라는 개념은 자바스크립트 코드 단위를 포함해 웹 애플리케이션을 구성하는 모든 자원을 의미한다.

모듈 번들러란 웹 애플리케이션을 구성하는 자원(HTML, CSS, Javscript, Images 등)을 모두 각각의 모듈로 보고 이를 조합해서 병합된 하나의 결과물을 만드는 도구를 의미합니다.

참고 개념) import와 export는 자바스크립트의 모듈을 언어레벨에서 잘 구현해놓은 스펙으로, 파일 단위로 변수 스코프가 생긴다. 변수 또는 함수를 import, export 가능하다.

바벨은 자바스크립트의 최신 문법을 최대한 많은 브라우저가 호환할 수 있도록 변환해주는 도구이다.

(더 자세한 내용: 바벨 공식 페이지 - Learn ES2015)

웹팩 튜토리얼

기본 튜토리얼

  1. package.json 파일 만들고($ npm init -y) webpack 설치하기($npm i webpack webpack-cli -D)
  1. index.html 파일 만들어 main.js를 불러오는 html 코드 넣기
  2. index.js 파일 만들어 자바스크립트 코드 넣기
  3. webpack.config.js의 script 속성에 “build”: “webpack” 추가하기
  4. 프로젝트 빌드하기($ npm run build)

1. 명령어 실행

$ npm init -y $ npm i webpack webpack-cli -D $ npm i lodash

2. index.html 추가

<html>
  <head>
    <title>Webpack Demo</title>
  </head>
  <body>
    <script src="./dist/main.js"></script>
  </body>
</html>

3. src/index.js 추가

import _ from 'lodash'

function component() {
  var element = document.createElement('div');

  /* lodash is required for the next line to work */
  element.innerHTML = _.join(['Hello','webpack'], ' ');

  return element;
}

document.body.appendChild(component());

4. package.json 커스텀 명령어(scripts)에 “build”: “webpack” 추가

5. npm run build 실행 (package.json type을 지워야 실행됨)

기본 튜토리얼 + webpack.config.js

계속 package.json의 커스텀 명령어에 webpack 옵션들을 추가하면 복잡하므로, webpack.config.js 파일을 추가한다.

var path = require('path'); // commonjs 문법

module.exports = {
  mode: 'none',
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  }
};

웹팩 모드 세가지: development, production, none 이 모드 중 none을 적용하면 “build”: “webpack —mode=none” 워닝메시지가 없어지고, dist/main.js의 코드 형태가 난독화가 아닌 알아볼 수 있게 바뀐다.

빌드 결과 분석

웹팩 전후 결과를 개발자 도구-네트워크 패널 열어서 확인할 수 있다. 웹팩을 사용하기 전에는 lodash cdn 라이브러리 요청 후 lodash.js를 받아와 다른 것 포함 5번의 요청이 있는 것을 확인할 수 있다. 웹팩을 사용한 후에는 파일이 합쳐지며 lodash cdn 라이브러리 요청이 없어져 3번의 요청으로 요청 수가 줄었다.

main.js를 열어보면 내부에 즉시 실행 함수로 component 함수와 loadsh 함수가 각각 들어가 있다. 즉시 실행 함수 표현(IIFE, Immediately Invoked Function Expression)은 정의되자마자 즉시 실행되는 Javascript Function 를 말한다.

(function () {
  // …
})();

(() => {
  // …
})();

(async () => {
  // …
})();

웹팩 심화(소개 영상)

Webpack: an ahead of time compiler for the browser, it’s not just for js 브라우저를 위한 사전 컴파일러, 자바 스크립트 뿐만 아니라 웹 페이지를 돌리는 모든 리소스에서 사용 가능하다.

웹팩은 기존의 자동화 도구들과 접근 방식에서 큰 차이가 있다.

  • 도구 (Gulp): 파일별로 작업을 자동화하는 태스크(Task) 기반 방식으로, 개발자가 최적화 과정을 위해 일일이 스크립트를 작성해야 한다.

  • 웹팩 (Webpack): ES6 모듈 시스템을 기반으로 하며, 코드 간의 의존성 관계를 웹팩이 스스로 파악하여 최적화와 모듈 번들링을 자동으로 진행한다.

참고) 개발자도구 - 네트워크의 Throttling을 설정하면 딜레이를 임의적으로 줄 수 있다. 이를 통해 저속의 네트워크 환경에서 얼마나 시간이 걸리는지 체크 가능하다. 렌더링 파이프라인 = 타임라인

웹팩 등장 배경

  1. 파일 단위의 자바스크립트 모듈 관리 자바스크립트의 변수는 기본적으로 전역 범위를 갖는다. 파일 단위로도 구분이 되지 않기 때문에, 변수의 이름이 같으면 다른 파일에도 영향을 받아 변수의 값이 원치 않게 바뀔 수 있다. 이전에는 네임스페이스 패턴이나 commonjs requires 등을 이용해서 파일 별로 변수를 구분했다. 최근에는 ES6의 모듈 개념으로 변수를 구분한다.

  2. 웹 개발 작업 자동화 도구 웹팩 = 웹 개발 작업 자동화 도구 + 웹 모듈 번들러

  3. 웹 애플리케이션의 빠른 로딩 속도와 높은 성능 레이지 로딩(필요할 때 해당 모듈을 넣어주는 기법)의 혜택까지 웹팩으로 볼 수 있다.

웹팩은 기본적으로 필요한 자원은 미리 로딩하는게 아니라 그 때 그 때 요청하자는 철학을 갖고 있습니다.

3. Webpack 주요 속성

entry

entry는 webpack의 진입점 정보를 담고 있는 속성으로, 진입하는 파일의 이름이나 경로가 들어간다(보통 index.html).

output

output은 webpack으로 빌드한 결과물에 대한 정보를 담고있는 속성으로, 빌드되는 파일의 이름이나 경로가 들어간다.

동적 파일 이름 옵션 웹팩은 대괄호([])를 사용하여 파일 이름에 특정 값을 동적으로 치환해 넣을 수 있는 토큰들을 제공한다.

  • [name]: 엔트리 포인트(entry point)의 이름을 가져온다. (entry: { app: ’./src/index.js’ }라면 app이 이름이 됨)
  • [id]: 웹팩 내부적으로 사용하는 모듈의 고유 ID를 붙여준다.
  • [chunkhash]: 해당 청크(파일)의 내용물(Content)을 바탕으로 생성된 고유한 해시값으로, 파일 내용이 조금이라도 바뀌면 이 해시값도 완전히 변한다.

이 설정의 가장 큰 목적은 브라우저 캐싱(Browser Caching) 문제를 해결하는 것이다. 같은 파일 이름으로 계속 빌드를 하면 내부의 파일 내용과 관계 없이 브라우저 캐싱 때문에 같은 파일을 화면에 뿌려주기 때문에, 강제로 캐시를 비워줘야하는 상황이 생긴다. 따라서 내용이 변환이 됐을 때는 chunkhash로 어떤 고유값, 구분자를 줘서 이 파일이 변화가 됐다는걸 인식시키면 사용자가 강제로 캐시를 비워주지 않아도 결과물을 잘 확인할 수 있다.

loader

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

loader는 js가 아닌 다른 확장자 파일을 번들링할 때 추가한다. 예를들어 css를 번들링할 때는 css-loader와 style-loader를 사용한다. loader는 webpack.config.js의 modules.rules 위치에 추가된다. (entry->output으로 갈 때 중간에 개입하는 속성) loader는 배열의 오른쪽에서 왼쪽으로 적용되므로, 추가할 때 위치에 유의해야한다.

웹팩 실습(Code Splitting)

튜토리얼 내용 (package.json의 type 행 지워야 빌드됨)

빌드하고 실행하면 원본 코드에서는 html head에 style이 없는데, 웹 개발자 도구 Elements에서는 인라인 style이 적용되어있다. (를 주석하면 파란색 적용 안됨)

빌드된 dist/bundle.js 를 살펴보면, 여러 모듈 중에 p { color: blue;} 가 문자열로 들어가 있는 곳이 있는데, 이것이 바로 Loader이다. 자바스크립트가 아닌 파일에 대해서 웹팩 안으로 변환할 수 있게 적용해주는 속성이 바로 Loader이다.

// webpack.config.js
var path = require('path');

module.exports = {
  mode: 'none', // production(배포시에는 꼭), development, none
  entry: './index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: { // = loader
    rules: [
      {
        test: /\.css$/, // 모든 css의 파일을 대상으로
        use: ['style-loader', 'css-loader'] // 이 loader를 적용하겠다
      }
    ]
  },
}

module 속성을 추가하지 않으면 ./base.css에 적절한 로더를 추가하라는 에러 메시지가 뜬다. ‘css-loader’만 추가하면 빌드는 오류가 나지 않지만, 웹페이지에 색상이 적용되지 않는다. ‘style-loader’를 ‘css-loader’ 뒤에 추가하면 빌드 에러가 난다. [‘style-loader’, ‘css-loader] 이 순서로 추가해야 빌드 에러도 나지 않고 웹페이지에 색상도 적용된다. (배열의 오른쪽에서 왼쪽으로 적용)

css-loader는 웹팩 안에 css파일을 넣어주는 역할, style-loader는 웹팩안에 들어간 style 코드를 head 안에 넣어주는 역할을 한다.

웹팩 공식 사이트에 documentation을 보면 loaders 내용이 문서화 되어있다.

plugin

플러그인은 웹팩의 기본적인 번들링 동작에 추가적인 기능을 더해주는 도구이다. 로더(Loader)가 파일을 해석하고 변환하는 데 집중한다면, 플러그인은 결과물의 형태를 바꾸거나 최적화하는 등 훨씬 강력한 제어권을 가진다.

MiniCssExtractPlugin을 사용하면 자바스크립트 파일(bundle.js)과 별개로 main.css 같은 결과물이 따로 생성된다. 이렇게 분리된 CSS 파일은 웹 브라우저가 스스로 찾지 못하므로, 처음에는 <link rel="stylesheet" href="./dist/main.css">와 같이 직접 HTML에 적어주어야 색상이 적용되는 것을 확인할 수 있다.

HtmlWebpackPlugin은 빌드된 자산(번들 파일)을 HTML에 자동으로 연결하기 위해 웹팩이 생성한 번들 파일들(JS, CSS 등)을 알아서 index.html 파일에 스크립트나 링크 태그로 추가해주는 역할을 해 수동으로 관리할 필요가 없어진다.

웹팩 공식 사이트에 documentation을 보면 plugins 내용이 문서화 되어있다.

(참고)resolve

resolve 속성은 웹팩으로 파일 간의 연관관계를 해석해나갈 때 그 파일의 해석 방식을 정의할 수 있는 것이다. vue$를 사용하는 경우, 뷰 내부에서 vue$를 썼을 때 뷰 밑에 있는 view.esm.js 파일을 연결을 시키겠다는 의미이다.

4. Webpack Developer Tools

Webpack sourcemap

webpack.config.js에서 devtool: ‘source-map’ 옵션을 추가하면 개발자 도구에서 console에 뜨는 메시지 링크를 클릭 해 원본 코드를 확인할 수 있다. 단, 배포시에는 보안을 침해할 수 있으므로 sorucemap 기능을 반드시 제거하고 배포해야한다.

Webpack Dev Server

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

dev 서버는 개발 중 빌드를 할 때, 파일을 파일 시스템이 아닌 메모리에 저장해 빌드 속도를 빠르게 해주는 도구이다. dev 서버를 사용하면 빌드한 파일이 보이지 않고, 코드를 바꾸고 저장할 때 바로바로 브라우저에서 확인이 가능하다.