๐Ÿฅ webpack ์ด์ •๋ฆฌ (+vue)

KHWยท2021๋…„ 10์›” 4์ผ
0

Node.js

๋ชฉ๋ก ๋ณด๊ธฐ
19/19

1. webpack

๋ฒˆ๋“ค๋Ÿฌ ์ค‘ ํ•˜๋‚˜๋กœ ์—ฌ๋Ÿฌ ํŒŒ์ผ์„ ์••์ถ•ํ•ด์ฃผ๋Š” ๋ฒˆ๋“ค๋ง ์—ญํ• ์„ ํ•˜๋Š” ๋Œ€์ƒ

์›นํŒฉ ๊ด€๋ จ ๋‹ค์šดํ•  ๊ฒƒ
1. webpack : core ํŒจํ‚ค์ง€
2. webpack-cli : ํ„ฐ๋ฏธ๋„ ๋™์ž‘ ์‹œํ‚ฌ ๊ฒƒ
3. webpack-dev-server : ๊ฐœ๋ฐœ ์„œ๋ฒ„ ์˜คํ”ˆ์šฉ

๐Ÿฅ ๊ด€๋ จ ์ฝ”๋“œ ์‚ฌ์ดํŠธ

์‚ฌ์ดํŠธ๋ฅผ ๋“ค์–ด๊ฐ€๋ฉด ๊ธฐ๋ณธ์ ์ธ package.json๋ถ€ํ„ฐ ์ „๋ถ€ ์ž‘์„ฑ๋˜์–ด์žˆ๋‹ค.

2. ํŒŒ์ผ ๋ถ„์„ํ•˜๊ธฐ

๐Ÿฅ package.json

{
  "name": "webpack-test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "cross-env NODE_ENV=production webpack --progress",
    "start": "webpack serve --progress"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/cli": "^7.13.10",
    "@babel/core": "^7.13.10",
    "@babel/preset-env": "^7.13.10",
    "@vue/compiler-sfc": "^3.2.19",
    "babel-loader": "^8.2.2",
    "clean-webpack-plugin": "^3.0.0",
    "copy-webpack-plugin": "^8.0.0",
    "core-js": "^3.9.1",
    "cross-env": "^7.0.3",
    "css-loader": "^5.1.1",
    "file-loader": "^6.2.0",
    "html-webpack-plugin": "^5.3.0",
    "mini-css-extract-plugin": "^1.3.9",
    "node-sass": "^5.0.0",
    "sass-loader": "^11.0.1",
    "source-map-loader": "^2.0.1",
    "style-loader": "^2.0.0",
    "terser-webpack-plugin": "^5.1.1",
    "url-loader": "^4.1.1",
    "vue": "^3.2.19",
    "vue-loader": "^16.8.1",
    "webpack": "^5.24.3",
    "webpack-cli": "^4.5.0",
    "webpack-dev-server": "^3.11.2"
  }
}

babel๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ css์— sass ๊ทธ์™ธ์—
html-webpack-plugin์— webpack๊ด€๋ จ๋„ ์ค€๋น„๋˜์–ด์žˆ๋‹ค.

webpack ๊ด€๋ จ

webpack
webpack-cli
webpack-dev-server

css sass ๊ด€๋ จ

style-loader
sass-loader
css-loader

html ๊ด€๋ จ

html-webpack-plugin

vue ๊ด€๋ จ

vue
vue-loader
@vue/compiler-sfc

๐Ÿฅ webpack.config.js

์›นํŒฉ ์„ค์ •ํŒŒ์ผ

const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const CopyPlugin = require("copy-webpack-plugin");

const { VueLoaderPlugin } = require('vue-loader')

const webpackMode = process.env.NODE_ENV || 'development';

module.exports = {
	mode: webpackMode,
	resolve : {
		extensions : ['.vue', '.js'],
		alias : {
			'~' : path.resolve(__dirname , 'src')
		}
	},
	entry: {
		main: './src/main.js',
	},
	output: {
		path: path.resolve('./dist'),
		filename: '[name].js'
	},
	// es5๋กœ ๋นŒ๋“œ ํ•ด์•ผ ํ•  ๊ฒฝ์šฐ ์ฃผ์„ ์ œ๊ฑฐ
	// ๋‹จ, ์ด๊ฑฐ ์„ค์ •ํ•˜๋ฉด webpack-dev-server 3๋ฒˆ๋Œ€ ๋ฒ„์ „์—์„œ live reloading ๋™์ž‘ ์•ˆํ•จ
	// target: ['web', 'es5'],
	devServer: {
		overlay: true,
		stats: 'errors-only',
		liveReload: true
	},
	optimization: {
		minimizer: webpackMode === 'production' ? [
			new TerserPlugin({
				terserOptions: {
					compress: {
						drop_console: true
					}
				}
			})
		] : [],
		splitChunks: {
			chunks: 'all'
		}
	},
	// externals: {
		// ๋นŒ๋“œ ๊ณผ์ •์—์„œ ์ œ์™ธํ•˜๊ณ  ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•  ํŒจํ‚ค์ง€ ์ง€์ •
		// ์˜ˆ์‹œ - foo๋ผ๋Š” ์ด๋ฆ„์˜ ๋ชจ๋“ˆ์ด๋ผ๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ ์ž…๋ ฅ
		// foo: "foo"
	// },
	module: {
		rules: [
			{
				test: /\.(css|scss)$/,
				use: [
					process.env.NODE_ENV === 'production' ? MiniCssExtractPlugin.loader : 'style-loader',
					'css-loader',
					'sass-loader'
				]
			},
			{
				test: /\.(png|jpg|gif|svg)$/,
				loader: 'url-loader',
				options: {
					name: '[name].[ext]?[hash]',
					limit: 10000,
				}
			},
			{
				test: /\.js$/,
				loader: 'babel-loader',
				exclude: /node_modules/
			},
			{
				test: /\.js$/,
				enforce: 'pre',
				use: ['source-map-loader'],
			},
			{
				test : /\.vue$/,
				use : 'vue-loader'
			}
		]
	},
	plugins: [
		new webpack.BannerPlugin({
			banner: `
				LICENSE.txt์— ์ถœ๋ ฅํ•  ๋‚ด์šฉ
			`
		}),
		new HtmlWebpackPlugin({
			template: './src/index.html',
			minify: process.env.NODE_ENV === 'production' ? {
				collapseWhitespace: true,
				removeComments: true,
			} : false
		}),
		new CleanWebpackPlugin(),
		...(process.env.NODE_ENV === 'production'
			? [new MiniCssExtractPlugin({ filename: '[name].css' })]
			: []
		),
		new VueLoaderPlugin(),
		// ๋นŒ๋“œ ๋  js์— ํฌํ•จ์‹œํ‚ค์ง€ ์•Š๊ณ  ๊ทธ๋Œ€๋กœ ๊ฐ€์ ธ์™€์„œ ์“ฐ๋Š” ํŒŒ์ผ์€ ์•„๋ž˜์ฒ˜๋Ÿผ ์„ค์ •ํ•˜๊ณ , HTML์—๋„ ์ง์ ‘ ๋„ฃ์–ด์ฃผ์„ธ์š”
		// new CopyPlugin({
		// 	patterns: [
		// 		{ from: "./src/ext/modernizr.js", to: "./modernizr.js" },
		// 	],
		// })
	]
};

resolve

extensions๋ฅผ ํ†ตํ•ด .vue๋‚˜ .js์˜ ๋’ค์˜ ํŒŒ์ผ๋ช…์„ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋‹ค.
alias๋ฅผ ํ†ตํ•ด ์–ด๋–ค ๋ฌธ์ž์— ๋Œ€ํ•ด path๋ฅผ ์ง€์ •ํ•ด ์ค„ ์ˆ˜ ์žˆ๋‹ค.

entry

ํŒŒ์ผ์„ ์ฒ˜๋ฆฌํ•  ๋Œ€์ƒ

output

ํŒŒ์ผ์„ ์ฒ˜๋ฆฌํ•ด ์ถœ๋ ฅํ•  ์œ„์น˜(path)์™€ ๋Œ€์ƒ(filename)

module

์–ด๋– ํ•œ ํŒŒ์ผ์— ๋Œ€ํ•ด ์–ด๋– ํ•œ loader๋ฅผ ์ด์šฉํ•˜์—ฌ ์ฒ˜๋ฆฌํ•  ์ง€
test๋ฅผ ํ†ตํ•ด ํŒŒ์ผ๋ช…์„ ์ง€์ •ํ•˜๊ณ 
loader๋‚˜ use๋ฅผ ํ†ตํ•ด ํ•ด๋‹น loader๋ฅผ ์ง€์ •ํ•œ๋‹ค

plugin

webpack์œผ๋กœ ๋ณ€ํ™˜ํ•œ ํŒŒ์ผ์— ์ถ”๊ฐ€์ ์ธ ๊ธฐ๋Šฅ์„ ๋”ํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉ

3. vue css๊ฐ€ ํฌํ•จ๋œ ๊ฐ„๋‹จํ•œ ์˜ˆ์‹œ ์‹คํ–‰ํ•˜๊ธฐ

์ฃผ์˜ : vue ๊ด€๋ จ ๋‹ค์šด ๋ฐ›์„ ๋•Œ
npm i -D vue@next vue-loader@next @vue/compiler-sfc
๋กœ ์ตœ์‹ ๋ฒ„์ „ @next๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.

๐Ÿฅ ํŒŒ์ผ ๋‚ด์šฉ

  1. index.html
  2. main.css
  3. main.js
  4. App.vue
  5. component/Btn.vue

๐Ÿฅ ํŒŒ์ผ ์ฝ”๋“œ

1. index.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Title</title>
</head>
<body>
<div id="app"></div>
</body>
</html>

๋งค์šฐ ๊ฐ„๋‹จํ•˜๊ฒŒ app id๋ฅผ ๊ฐ€์ง„ divํƒœ๊ทธ๋งŒ ์กด์žฌ

2. main.css

.name{
    background-color:orange;
    width:300px;
    height:100px;
}

name class๋ฅผ ๊ฐ€์ง„ ๋Œ€์ƒ์— ์†์„ฑ์„ ์ฒ˜๋ฆฌ

3. main.js

import './main.css';
import {createApp} from 'vue'
import App from '~/App'
//์ „์—ญ๋“ฑ๋ก
import Btn from '~/component/Btn'

const app = createApp(App)
app.component('Btn',Btn)
app.mount('#app')

์ฃผ์š”๋‚ด์šฉ๋“ค์ด ๋‹ด์•„์˜ค๋Š” ๊ณณ์œผ๋กœ
css๋„ ์ฒ˜๋ฆฌํ•˜๊ณ  vue์™€ App๋ฐ App์— ๋‹ด์„ Btn๋„ ์กด์žฌํ•œ๋‹ค.

4. App.vue

<template>
    <div
            class="name"
            @click="updateName"
    >
        {{ name }}
    </div>
    <Btn></Btn>
</template>

<script>
  import {ref} from 'vue';
  export default{
    setup(){
      const name = ref('Kossie Coder1');

      const updateName = () => {
        name.value = 'Kossie Coder'
      }
      return {
        name,
        updateName
      }
    }
  }
</script>

Kossie Coder1์ด ์ฒ˜์Œ ์ถœ๋ ฅ๋˜๋ฉด์„œ ํด๋ฆญ์‹œ Kossie Code๋กœ ๋ฐ”๋€Œ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.
Btn ๋ฒ„ํŠผ์ด ์ถ”๊ฐ€๋˜๊ฒŒ ๋งŒ๋“œ๋Š” ์—ญํ• 

5. component/Btn.vue

<template>
    <button @click="log">
        Click me!
    </button>
</template>

<script>
  export default {
    methods:{
      log(){
        console.log('Click')
      }
    }
  }
</script>

click ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์ฝ˜์†”์— ์ถœ๋ ฅํ•˜๋Š” ์—ญํ• 

๐Ÿฅ ํŒŒ์ผ ๊ฒฐ๊ณผ ์ฒ˜๋ฆฌํ•˜๊ธฐ

  "scripts": {
    "build": "cross-env NODE_ENV=production webpack --progress",
    "start": "webpack serve --progress"
  }

npm run start

๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์‹คํ–‰

npm run build

์„œ๋ฒ„์— ์—…๋กœ๋“œํ•  ํด๋”์•ˆ์˜ ํŒŒ์ผ ์ƒ์„ฑ

๊ฒฐ๊ณผ๋Š” npm run dev์™€ ๊ฐ™๋‹ค

4. ์ถ”๊ฐ€์ ์ธ ๋‚ด์šฉ

๐Ÿฅ webpack.config.js์˜ module๊ณผ plugin ์ฐจ์ด

  • module

    ํŒŒ์ผ์„ ํ•ด์„ํ•˜๊ณ  ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์— ๊ด€์—ฌํ•˜์—ฌ ๋ชจ๋“ˆ์„ ์ฒ˜๋ฆฌ

  • plugin

    ๋ฒˆ๋“ค๋œ ๊ฒฐ๊ณผ๋ฌผ์„ ์ฒ˜๋ฆฌ
    ex) ๋ฒˆ๋“ค๋œ js๋ฅผ ๋‚œ๋…ํ™” ํ•˜๊ฑฐ๋‚˜ ํŠน์ • ํ…์ŠคํŠธ๋ฅผ ์ถ”์ถœํ•˜๋Š” ์šฉ๋„ ๋“ฑ
    ํ•ด๋‹น ๊ฒฐ๊ณผ๋ฌผ์˜ ํ˜•ํƒœ๋ฅผ ๋ฐ”๊พธ๋Š” ์—ญํ• ์„ ํ•˜๋ฏ€๋กœ ๋ฒˆ๋“ค๋ง๋œ ํŒŒ์ผ์„ ์ฒ˜๋ฆฌ

babel์— webpack์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ 

babel์„ ํ†ตํ•ด ๋งŒ๋“ค์–ด์ง„ js๋Š” require๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ ์šฉ๋ ์ˆ˜ ์—†๋‹ค.
์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ webpack์„ ์„ค์น˜ํ•˜๊ณ  webpack์— babel-loaderํ•œ ํ›„ ๊ฐœ๋ฐœ์„œ๋ฒ„๋กœ ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜ ๋ฒˆ๋“ค๋Ÿฌ๋กœ build๋ฅผ ํ•˜์—ฌ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋™์ž‘ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ์—ญํ• ์„ webpack์ด ๋‹ด๋‹นํ•œ๋‹ค.

5. ์ถœ์ฒ˜

์›นํŒฉ

profile
๋‚˜์˜ ํ•˜๋ฃจ๋ฅผ ๊ฐ€๋Šฅํ•œ ๊ธฐ์–ตํ•˜๊ณ  ์ฆ๊ธฐ๊ณ  ํ›„ํšŒํ•˜์ง€๋ง์ž

0๊ฐœ์˜ ๋Œ“๊ธ€