Webpack | 기본적인 구성 방법

이진웅·2022년 5월 2일
1

Webpack

목록 보기
1/1
post-thumbnail

모듈

모듈이란 애플리케이션을 구성하는 개별적 요소재사용 가능한 코드 조각을 말합니다. 일반적으로 모듈은 기능을 기준으로 파일단위로 분리합니다.

모듈은 자신만의 파일 스코프를 가지고 있어야 합니다. 모듈 내의 정보는 비공개 상태입니다.

모듈은 애플리케이션과 분리된 개별적 존재이지만, 완전 분리된다면 재사용이 불가능하기 때문에 존재의 의미가 없게됩니다. 그렇기 때문에 모듈은 export를 통해 공개가 필요한 요소를 명시적으로 선택적 공개가 가능합니다. export된 값들은 다른 모듈에서 사용할 수 있게 됩니다. 즉 의존성을 가지게 됩니다.

이렇게 export된 값들을 사용하는 모듈을 모듈 사용자라고하는데, import를 통해 그 값들을 자신의 모듈 스코프로 불러들여 재사용할 수 있습니다.

웹팩?

공식 문서에 따르면 웹팩은 모던 자바스크립트 어플리케이션을 위한 정적인 모듈 번들러로 정의할 수 있습니다.

자바스크립트 개발자들이 아는 모듈과는 다르게 웹팩에서의 모듈은 약간 차이가 있습니다.

웹팩에서의 모듈 구성

  • ES modules - import statement
  • Common JS modules - require() statement
  • AMD modules - define and require statement
  • CSS imports - @import statement inside any css/sass/less files
  • Image URLs - url(...) or <img src=’...’ />

웹팩은 자바스크립트에서 모두 사용할 수 있게 이러한 다른 모듈들을 하나로 통합시키는 역할을 합니다.

웹팩 초기 설정

웹팩을 구성해보기 위해 간단하게 어플리케이션을 구성해봅시다.

  1. 새로운 폴더에 아래의 명령어를 통해 npm프로젝트를 초기화 시킵시다.
npm init -y
  1. 필수 웹팩 패키지를 설치합시다.
npm install --save-dev webpack webpack-cli webpack-dev-server
  1. package.json 파일의 scripts 내용을 수정해줍니다.
{
  "name": "webpack_practice",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "webpack --mode development"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/woongstaa/webpack_practice.git"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/woongstaa/webpack_practice/issues"
  },
  "homepage": "https://github.com/woongstaa/webpack_practice#readme",
  "devDependencies": {
    "webpack": "^5.72.0",
    "webpack-cli": "^4.9.2",
    "webpack-dev-server": "^4.8.1"
  }
}
  1. npm run dev를 실행하면 웹팩의 기본 엔트리포인트로 src/index.js를 찾기 때문에 에러가 발생할겁니다.
  2. 또한 npm run dev를 실행하게 되면 웹팩은 dist폴더에 빌드한 파일들을 추출합니다.
  3. src폴더를 생성한 뒤 내부에 index.js를 생성합니다. console.log(’hello webpack’)index.js에 우선 작성해줍니다.
  4. 다시 npm run dev를 실행하면 오류는 일어나지 않고, dist폴더에 main.js파일이 빌드된 것을 확인할 수 있습니다.

웹팩 설정하기

웹팩을 설정하기 위해선 root 디렉토리에 webpack.config.js라는 파일을 만들어야합니다. 웹팩은 node.js 같은 브라우저가 없는 자바스크립트에서 동작하기 때문에 webpack.config.js 파일에서 환경설정 객체를 추출할 수 있게 만들어 줘야합니다.

자주 사용하는 키워드

  • Entrypoint 모든 의존객체들이 모인 웹팩의 시작지점을 정의합니다.
  • Output 빌드과정에서 JavaScript와 정적 파일들을 어디에 보관할지 정의합니다.
  • Loaders 웹팩이 다양한 파일 확장자를 다룰 수 있도록 도와주는 서드파티 프로그램들입니다. JavaScript파일이 아닌 파일들을 모듈화시켜 줍니다.
  • Plugins 웹팩의 동작 방식을 바꿔주는 서드파티 프로그램들입니다.
  • Mode development, production 모드로 나뉩니다.

엔트리 포인트 변경하기

만약 기본 엔트리 포인트였던 src/index.js에서 source/index.js로 수정하기 원한다면 webpack.config.js 파일에 entry 속성을 추가해 변경할 수 있습니다.

module.exports = {
	entry: './source/index.js',
}

혹은

const path = require('path')

module.exports = {
	entry: {
		index: path.resolve(__dirname, 'source', 'index.js')
	}
}

아웃풋 폴더 변경하기

output 속성을 사용해 빌드 파일이 추출될 위치도 설정 할 수 있습니다.

const path = require('path')

module.exports = {
	output: {
		path: path.resolve(__dirname, 'build'), 
		filename: 'main.js'
	}
}

빌드 파일은 build/main.js에 존재 할 것입니다.

빌드 파일에 HTML 파일 포함하기

모든 웹 어플리케이션에는 최소한 하나의 HTML파일이 존재합니다. HTML을 동작하게 하기 위해 우리는 html-webpack-plugin을 사용합니다.

npm install --save-dev html-webpack-plugin

플러그인의 역할

플러그인은 HTML파일을 읽은 뒤 같은 파일에 번들을 삽입합니다.

이를 확인해보기 위해 간단한 HTML파일을 만들어 웹팩 환결성정을 합시다.

index.htmlsource 폴더에 만들고 기본적인 코드를 작성합시다.

const HTMLWebpackPlugin = require('html-webpack')
const path = require('path')

module.exports = {
	plugins: [
		new HTMLWebpackPlugin({
			template: path.resolve(__dirname, 'source', 'index.html')
		})
	]
}

파일이 준비가 됐으니 받아줄 서버가 필요합니다. 이전에 설치했던 webpack-dev-server로 서버를 켜줍시다.

webpack dev server

webpack-dev-server를 설정하기 위해 package.jsonstart 스크립트를 생성해줍시다.

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

npm run start로 실행시킨뒤 개발자도구를 이용해 localhost:8080을 확인해보면 index.html이 서버로 전달됐고, main.js가 번들링 됐다는 것을 확인 할 수 있습니다.

웹팩 로더 사용하기

loaders는 다른 파일 확장자를 다루기위한 서드파티 프로그램입니다. 로더를 관리하기위해 module이란 키 값을 사용하며 그 속엔 rules라는 배열을 사용해 구성합니다.

rules라는 배열 속에는 testuse를 키 값으로 가진 객체로 구성되어 있습니다. test는 파일의 확장자를 구분하며 use는 어떤 로더들을 사용할지 배열로 구성할 수 있으며 오른쪽에서 왼쪽 방향으로 우선순위를 갖게됩니다.

module.exports = {
  module: {
    rules: [
      {
        test: /\.filename1$/,
        use: ['loader-b', 'loader-a'],
      },
      {
        test: /\.filename2$/,
        use: ['loader-d', 'loader-c'],
      },
    ],
  },
}

로더를 구성하는 일반적인 구문입니다.

CSS 적용하기

웹팩에서 CSS를 적용하기 위해선 css-loaderstyle-loader가 필요합니다.

npm install --save-dev css-loader style-loader

styles.css 파일을 source 폴더에 새로 생성합니다. 스타일 파일에 index.html에 보여줄 기본적인 CSS를 작성 합니다. 이후 CSS파일을 index.jsimport 해줍시다.

그 다음엔 webpack.config.jsloader를 설정해줍시다.

module.exports = {
	module: {
		rules: [
			{
				test: /\.css$/,
				use: ['style-loader', 'css-loader'],
			},
		],
	},
}

css-loader는 CSS 파일을 불러오는데 사용되며, style-loader는 DOM에서 스타일 시트를 불러오는데 사용됩니다.

npm run start를 통해 서버를 다시 실행시키면 브라우저에 반영된 것을 볼 수 있습니다.

모던 자바스크립트 적용하기

웹팩 자체로는 모던 자바스크립트를 모든 브라우저에 호환되는 코드로 변환시킬 수 없습니다. 이를 해결하기위해 바벨을 사용합니다. 실제 엔진 패키지인 @babel/core와 웹팩에서 필요한 로더 babel-loader와 자바스크립트를 ES5로 변환시켜주는 @babel/preset-env를 설치합니다.

npm install --save-dev @babel/core babel-loader @babel/preset-env

다음 단계는 루트 디렉토리에 babel.config.json을 만들어 babel을 설정해줍니다.

{
	"presets": ["@babel/preset-env"]
}

이제 webpack.config.js에 로더를 추가해줍니다.

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: ['babel-loader'],
      },
    ],
  },
}

이제 자바스크립트 코드에 ES6문법을 적용하고 다시 빌드해보면 main.js파일에 자동적으로 브라우저에 호환되는 변환된 코드가 빌드 된 것을 확인할 수 있습니다.

웹팩에서 사용되는 모드

  • prodction
    • 많은 최적화 작업이 적용됩니다.
    • terser-webpack-plugin을 통해 자동으로 최적화하며 번들 사이즈를 줄입니다.
    • process.env.NODE_ENV 같은 환경변수 설정을 통해 퍼포먼스를 향상시킬 수 있습니다.
  • development
    • 프로덕션 모드와 달리 최적화 작업이 없습니다.
    • 이 모드에서 웹팩은 단순히 모든 자바스크립트 코드를 사용하고 브라우저에서 읽어서 어플리케이션을 빠르게 리로드합니다.

production 모드에서 웹팩을 사용하기 위해 package.json에 스크립트를 추가해줍니다. build로 명명하겠습니다.

"scripts": {
    "dev": "webpack --mode development",
    "start": "webpack-dev-server --mode development --open",
    "build": "webpack --mode production"
  },

최적화

Code Splitting이나 Lazy Loading은 큰 번들 사이즈를 피하기 위한 최적화 방법들이며, 의존의 중복도 제거할 수 있습니다. 이 방법을 이용해 사용자가 버튼을 클릭했을때나 라우트가 변경됐을 때 원하는 코드의 조각을 불러올 수 있습니다. 이때 조각들을 chunk라고 부릅니다.

웹팩은 초기 번들의 크기를 244kb 이하로 제한시켜놓았습니다. 이 기준에 맞추기위해 코드를 분리하는 방법이 3가지 있습니다.

  1. 엔트리 포인트를 여러개로 분리시키기
  2. optimization.splitChunks 사용
  3. 동적 import

엔트리 포인트를 분리시키는 방법은 작은 프로젝트에선 잘 작동할 수 있으나 복잡하거나 규모가 큰 프로젝트에선 좋은 방법은 아닙니다.

optiamization.splitChunks 사용

어플리케이션에서 의존성이 높아질 때가 있습니다. 이런 상황을 연출해보기위해 Moment라는 라이브러리를 사용해봅시다.

npm install moment

설치를 한 뒤 index.js에 import 합시다. 그리곤 npm run build를 해봅시다. 아래와 같은 경고문이 노출될 것 입니다.

이를 해결하기위해 optimization이라는 속성을 추가한 뒤, 그 속에 splitChunks라는 속성도 추가해줍니다.

module.exports = {
  optimization: {
    splitChunks: { chunks: 'all' },
  },
}

엔트리 포인트가 상당히 줄어든 것을 확인할 수 있습니다.

동적 imports

동적 임포트는 조건에 맞는 코드를 불러옵니다. 이런 접근법은 리액트나 뷰에서 널리 사용되고 있습니다. 사용자 반응에 기반해 코드를 불러오거나 라우트가 바뀔때 그에 맞는 코드를 불러옵니다.

해당 기능을 도입해보기 위해 페이지에 버튼을 추가하겠습니다. 이 버튼을 클릭하면 포스트 목록을 전달합니다. 이 로직은 index.js와는 분리된 파일에 존재할 것이며, 필요할 때 동적으로 import 될 것 입니다.

source/api.js를 생성해 fetch함수로 API를 호출합니다.

export const fetchTodos = () => {
    return fetch('https://jsonplaceholder.typicode.com/posts')
        .then(response => response.json())
        .then(json => json);
}

source/index.html에 버튼을 만들어 id값을 btn으로 설정해줍니다.

<button id="btn">Click me to load</button>

source/index.js 파일에는 ./api.js를 import 하기 위해 동적 import 를 작성해줍니다.

const getTodos = () => import('./api')

그리곤, 아래 코드를 추가해 데이터 로그를 보여주는 로직도 추가해줍니다.

const btn = document.getElementById('btn')
btn.addEventListener('click', () => {
  getTodos().then(({ fetchTodos }) => {
    fetchTodos().then(resp => console.log(resp))
  })
})

위 코드는 getTodos를 통해 파일을 import해온 뒤, 구조분해해서fetchTodos 함수를 호출해 성공에 대한 response를 로그에 보여주는 역할을 합니다.

이제 다시 파일을 빌드해 서버를 열어보면 자바스크립트 파일이 동적으로 불러와졌음을 확인 할 수 있습니다.

Reference

https://betterprogramming.pub/learn-webpack-in-under-10-minutes-efe2b2b10b61

https://serzhul.io/JavaScript/learn-webpack-in-under-10minutes/

2개의 댓글