ν™ˆ Webpack κΈ°λ³Έ μ„€μ •
포슀트
μ·¨μ†Œ

Webpack κΈ°λ³Έ μ„€μ •

πŸ’» Webpack μ΄λž€?

Webpack은 ν”„λ‘œμ νŠΈμ— μž„ν¬νŠΈλœ μ—¬λŸ¬ λͺ¨λ“ˆλ“€μ„ μ˜μ‘΄μ„±μ— 따라 λ¬Άμ–΄μ£Όμ–΄, ν•˜λ‚˜ μ΄μƒμ˜ λ²ˆλ“€μ„ 생성해 μ£ΌλŠ” λ²ˆλ“€λŸ¬(bundler)의 ν•œ μ’…λ₯˜ μž…λ‹ˆλ‹€. ν”„λ‘œμ νŠΈμ˜ 규λͺ¨κ°€ μ»€μ§€κ²Œλ˜κ³  μž„ν¬νŠΈν•˜λŠ” λͺ¨λ“ˆλ“€μ΄ λ§Žμ•„μ§ˆμˆ˜λ‘ λ²ˆλ“€λŸ¬μ˜ μ€‘μš”μ„±μ΄ μ»€μ§€κ²Œ λ˜μ—ˆλŠ”λ° κ·Έ 이유λ₯Ό μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€.

μ•„λž˜μ™€ 같은 λ‘κ°œμ˜ JavaScript 파일이 μžˆλ‹€κ³  κ°€μ •ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

1
2
3
4
5
// src/intro.js

function intro() {
  console.log("this is intro page");
}
1
2
3
// src/index.js

intro();

intro.jsλŠ” λ‹¨μˆœνžˆ 메세지 좜λ ₯ν•˜λŠ” ν•¨μˆ˜λ₯Ό μ •μ˜ν•œ 파일이며, index.jsλŠ” μ •μ˜ν•œ ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. 이제 두 νŒŒμΌμ„ html νŒŒμΌμ— μž„ν¬νŠΈ ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

1
2
3
4
5
<!-- index.html -->
<body>
  <script src="./src/intro.js"></script>
  <script src="./src/index.js"></script>
</body>

μœ„ λ‹¨κ³„κΉŒμ§€ 마친 ν›„ html νŒŒμΌμ„ 싀행해보면, μ½˜μ†”μ°½μ— this is intro page λΌλŠ” 메세지가 좜λ ₯λ©λ‹ˆλ‹€.

λ§Œμ•½ html νŒŒμΌμ— μž„ν¬νŠΈ μˆœμ„œλ₯Ό λ°”κΎΈκ²Œλ˜λ©΄, μ˜¬λ°”λ₯Έ κ²°κ³Όκ°€ λ‚˜μ˜€μ§€ μ•Šκ²Œλ©λ‹ˆλ‹€. index.js νŒŒμΌμ€ intro ν•¨μˆ˜λ₯Ό μ‹€ν–‰ν•˜λ € ν•˜λŠ”λ°, intro ν•¨μˆ˜κ°€ μ •μ˜λœ intro.js νŒŒμΌμ€ μ‹€ν–‰μ‹œμ μ— 아직 μž„ν¬νŠΈ λ˜μ§€ μ•Šμ•˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€. μœ„ μ˜ˆμ‹œμ—μ„œλŠ” 파일의 κ°―μˆ˜κ°€ 적어 λ¬Έμ œμ μ„ λ°”λ‘œ 찾을 수 μžˆμ—ˆμ§€λ§Œ ν”„λ‘œμ νŠΈ 규λͺ¨κ°€ 컀져 html νŒŒμΌμ— μž„ν¬νŠΈν•΄μ•Ό ν•˜λŠ” 파일이 μˆ˜λ°±κ°œμ— 이λ₯΄κ²Œ 되면, 문제λ₯Ό μ°ΎκΈ° μ–΄λ €μ›Œ μ§‘λ‹ˆλ‹€. λͺ¨λ“  파일의 μ˜μ‘΄μ„±μ„ κ³ λ €ν•΄ μ˜¬λ°”λ₯Έ μˆœμ„œλ‘œ μž„ν¬νŠΈλ₯Ό ν•΄μ£Όμ–΄μ•Ό ν•˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

Webpackκ³Ό 같은 λ²ˆλ“€λŸ¬λ₯Ό μ‚¬μš©ν•˜κ²Œ 되면, μœ„ 문제λ₯Ό μ‰½κ²Œ ν•΄κ²°ν•  수 μžˆμŠ΅λ‹ˆλ‹€. Webpack이 μ˜μ‘΄μ„± 관계λ₯Ό λ”°μ Έ ν•˜λ‚˜ μ΄μƒμ˜ λ²ˆλ“€λ§λœ νŒŒμΌμ„ λ§Œλ“€κ²Œ 되면, κ°œλ°œμžλŠ” λ§Œλ“€μ–΄μ§„ 파일만 μž„ν¬νŠΈν•˜λ©΄ λ©λ‹ˆλ‹€.

μ§€κΈˆκΉŒμ§€ webpack이 무엇이며, μ™œ ν•„μš”ν•œμ§€λ₯Ό μ‚΄νŽ΄ λ³΄μ•˜μŠ΅λ‹ˆλ‹€. 이제 webpack을 μ‚¬μš©ν•˜κΈ° μœ„ν•œ 섀정듀을 μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

πŸ’» webpack, webpack-cli μ„€μΉ˜

섀정을 μ•Œμ•„λ³΄κΈ° 전에, webpackκ³Ό webpack-cliλ₯Ό devDependencies둜 μ„€μΉ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€. webpack-cli의 경우 package.json의 script λͺ…λ Ήμ–΄λ₯Ό μ‚¬μš©ν•΄ webpackκ³Ό κ΄€λ ¨λœ κΈ°λŠ₯(λΉŒλ“œ, μ‹€ν–‰λ“±)을 μ‹€ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ–Š λ§Œμ•½ webpack 섀정없이 webpack λͺ…λ Ήμ–΄λ₯Ό μ‹€ν–‰ν•˜κ²Œλ˜λ©΄, κΈ°λ³Έ μ„€μ •μœΌλ‘œ λΉŒλ“œλ˜κ²Œ λ©λ‹ˆλ‹€.

πŸ’» entry, output

제일 λ¨Όμ € μ‚΄νŽ΄λ³Ό webpack μ„€μ • ν”„λ‘œνΌν‹°λŠ” entry, output μž…λ‹ˆλ‹€.
entryμ—λŠ” λΉŒλ“œκ°€ μ‹œμž‘λ˜λŠ” μ‹œμž‘μ  파일, 즉 μ˜μ‘΄μ„± κ·Έλž˜ν”„μ—μ„œ μ΅œμƒλ‹¨μ— μœ„μΉ˜ν•˜λŠ” 파일λ₯Ό μ§€μ •ν•˜κ²Œ 되며, outputμ—λŠ” λΉŒλ“œκ°€ μ™„λ£Œλœ 결과물의 이름과 결과물이 μœ„μΉ˜ν•  ν΄λ”μ˜ 경둜 및 ν΄λ”μ˜ 이름등을 μ„€μ •ν•΄μ£Όκ²Œ λ©λ‹ˆλ‹€.

1
2
3
4
5
6
7
8
9
// src/intro.js
function intro() {
  console.log("this is intro page");
}

// src/index.js
import intro from "./intro.js";

intro();

μœ„μ˜ μ˜ˆμ‹œμ—μ„œ entryλŠ” src/index.jsκ°€ λ©λ‹ˆλ‹€. λ”°λΌμ„œ webpack 섀정을 μ•„λž˜μ™€ 같이 지정할 수 μžˆμŠ΅λ‹ˆλ‹€. λΉŒλ“œμ˜ μ‹œμž‘μ μ΄ λ˜λŠ” νŒŒμΌμ€ λ°˜λ“œμ‹œ ν•˜λ‚˜μΌ ν•„μš”λŠ” μ—†μŠ΅λ‹ˆλ‹€. ν•˜λ‚˜κ°€ μ•„λ‹Œ μ˜ˆμ‹œλŠ” 이후에 μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€.

1
2
3
4
5
// webpack.config.js

module.exports = {
  entry: "./src/intro.js",
};

output은 μ•„λž˜μ™€ 같이 μ„€μ •ν•΄ 쀄 수 μžˆμŠ΅λ‹ˆλ‹€.

1
2
3
4
5
6
module.exports = {
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "dist"),
  },
};

μœ„ μ„€μ •μ˜ μ˜λ―ΈλŠ” λΉŒλ“œκ°€ μ™„λ£Œλœ 결과물을 bundle.js둜 λͺ…λͺ…ν•˜κ³ , νŒŒμΌμ„ μœ„μΉ˜λ₯Ό dist/bundle.js에 μœ„μΉ˜ μ‹œν‚€λΌλŠ” μ˜λ―Έμž…λ‹ˆλ‹€.

μœ„ μ„€μ •λŒ€λ‘œ λΉŒλ“œλ₯Ό ν•˜κ²Œ 되면 κ²°κ³ΌλŠ” μ•„λž˜μ™€ 같이 λ‚˜μ˜€κ²Œ λ©λ‹ˆλ‹€.

entry-output build

πŸ–Š Webpack μ„€μ • νŒŒμΌμ€ 파일λͺ…을 webpack.config.js둜 지정해 루트 폴더에 μƒμ„±ν•˜λ©΄, webpack이 μžλ™μœΌλ‘œ μ„€μ •νŒŒμΌμ„ μΈμ‹ν•˜κ²Œ λ©λ‹ˆλ‹€.

πŸ–Š Browser Cache

μš°λ¦¬κ°€ μ‚¬μš©ν•˜λŠ” μ›Ή λΈŒλΌμš°μ €λ“€μ€ μΊμ‹œλ₯Ό μ‚¬μš©ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. ν•œλ²ˆ λ„€νŠΈμ›Œν¬ μš”μ²­μ„ 보낸 νŒŒμΌλ“€μ€ κΈ°μ–΅ν•΄ λ†“μ•˜λ‹€κ°€ λ‹€μŒ μš”μ²­μ΄ λ“€μ–΄μ˜¬ λ•Œ, μΊμ‹œμ—μ„œ νŒŒμΌμ„ κ°€μ Έμ˜΅λ‹ˆλ‹€. 이λ₯Ό 톡해 λ‘œλ”©μ‹œκ°„μ„ λ‹¨μΆ•μ‹œν‚¬ 수 μžˆλ‹€λŠ” μž₯점이 μžˆμŠ΅λ‹ˆλ‹€.
ν•˜μ§€λ§Œ μΊμ‹œλ‘œ 인해 λ¬Έμ œλ„ λ°œμƒν•©λ‹ˆλ‹€. μ–΄λ–€ μƒν™©μ—μ„œ λ¬Έμ œκ°€ λ°œμƒν•˜λŠ”μ§€ ν•œκ°€μ§€ 상황을 κ°€μ •ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

μ–΄λ–€ μ‚¬μš©μžκ°€ 자주 μ‚¬μš©ν•˜κ³  μžˆλŠ” μ›Ή μ„œλΉ„μŠ€κ°€ μžˆμŠ΅λ‹ˆλ‹€. 이 μ„œλΉ„μŠ€μ˜ κ°œλ°œμžλŠ” μƒˆλ‘œμš΄ λ””μžμΈμ„ μ μš©ν•΄ λΉŒλ“œ ν›„ λ°°ν¬ν–ˆμ§€λ§Œ, μ‚¬μš©μžλŠ” 바뀐 λ””μžμΈμ„ λ°”λ‘œ 보지 λͺ»ν•©λ‹ˆλ‹€. λΈŒλΌμš°μ €μ˜ μΊμ‹œλ‘œ 인해 μ˜ˆμ „ html νŒŒμΌμ„ κΈ°μ–΅ν•˜κ³  있기 λ•Œλ¬Έμž…λ‹ˆλ‹€.
이 λ¬Έμ œλŠ” ν•΄κ²°ν•˜κΈ° μœ„ν•œ λ°©λ²•μœΌλ‘œλŠ” λΉŒλ“œλœ 파일λͺ…에 hash 값을 μΆ”κ°€ν•˜λŠ” 방법이 μžˆμŠ΅λ‹ˆλ‹€. output.filename에 [contenthash]λ₯Ό μΆ”κ°€ν•˜λ©΄ λΉŒλ“œλ  λ•Œλ§ˆλ‹€ 파일λͺ…에 hash 값이 μΆ”κ°€λ˜μ–΄ 맀번 λ‹€λ₯Έ μ΄λ¦„μ˜ 파일이 μƒμ„±λ©λ‹ˆλ‹€. 이λ₯Ό 톡해 λΈŒλΌμš°μ €μ˜ μΊμ‹œλ₯Ό ν”Όν•  수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ’» Asset Modules

Webpack은 μž„ν¬νŠΈν•œ λͺ¨λ“ˆ 쀑, JavaScript만 μ•Œμ•„ λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€. λ‹€μ‹œλ§ν•΄ 이미지 νŒŒμΌμ΄λ‚˜, ν…μŠ€νŠΈ 파일, CSS νŒŒμΌλ“± JavaScriptκ°€ μ•„λ‹Œ νŒŒμΌμ„ μž„ν¬νŠΈν•˜κ²Œ 되면 λΉŒλ“œμ‹œ 였λ₯˜κ°€ μƒκΈ°κ²Œ λ©λ‹ˆλ‹€. λ‹€λ₯Έ μ’…λ₯˜μ˜ νŒŒμΌλ“€μ„ μž„ν¬νŠΈν•˜λ €λ©΄ μΆ”κ°€μ μœΌλ‘œ 섀정을 ν•΄μ£Όμ–΄μ•Ό ν•©λ‹ˆλ‹€.
Asset Module은 이미지, κΈ€κΌ΄, ν…μŠ€νŠΈμ™€ 같은 asset νŒŒμΌμ„ webpack이 해석할 수 μžˆλ„λ‘ λ„μ™€μ£ΌλŠ” 역할을 ν•©λ‹ˆλ‹€. κ³Όκ±°μ—λŠ” url-loader, file-loader등을 μ„€μΉ˜ν•΄μ•Ό ν–ˆμ§€λ§Œ, webpack5λΆ€ν„°λŠ” κΈ°λ³Έ κΈ°λŠ₯으둜 ν†΅ν•©λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

Asset Module은 asset 파일 μž„ν¬νŠΈ 방식에 따라 세가지 μ’…λ₯˜λ‘œ λ‚˜λˆŒ 수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ‘¨β€πŸ’» asset/resource

λΉŒλ“œμ‹œ λͺ¨λ“ˆμ΄ 적용된 νŒŒμΌμ„ output 디렉토리에 μƒμ„±ν•©λ‹ˆλ‹€. μ΄λ•Œ λ³„λ„μ˜ 파일λͺ…을 μ§€μ •ν•˜μ§€ μ•ŠμœΌλ©΄, ν•΄μ‰¬λœ 값이 파일λͺ…μœΌλ‘œ μ§€μ •λ˜κ²Œ λ©λ‹ˆλ‹€.
이 λͺ¨λ“ˆμ„ μ μš©ν•  경우, νŒŒμΌμ„ λΆˆλŸ¬μ˜¬λ•Œ http μš”μ²­μ„ 보내며, 보톡 크기가 큰 νŒŒμΌμ„ κ°€μ Έμ˜¬λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€. 섀정은 μ•„λž˜μ™€ 같이 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
13
// webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        // ν™•μž₯μžκ°€ png, jpg인 νŒŒμΌμ— λͺ¨λ“ˆ 적용
        test: /\.(png|jpg)$/,
        type: "asset/resource",
      },
    ],
  },
};

μœ„ μ„€μ •λŒ€λ‘œ λΉŒλ“œλ₯Ό ν•˜κ²Œλ˜λ©΄ κ²°κ³ΌλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

asset-resource

μœ„μ—μ„œ μ–ΈκΈ‰ν–ˆλ“―μ΄ μž„ν¬νŠΈλœ μ΄λ―Έμ§€νŒŒμΌμ΄ output 디렉토리에 λ”°λ‘œ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

πŸ–Š μƒμ„±λ˜λŠ” asset 파일의 μ΄λ¦„μ΄λ‚˜ 경둜λ₯Ό λ°”κΎΈκ³  μ‹Άλ‹€λ©΄ Custom output filename을 μ°Έμ‘°ν•˜λ©΄ λ©λ‹ˆλ‹€.

πŸ‘¨β€πŸ’» asset/inline

λ³„λ„μ˜ νŒŒμΌμ„ output 디렉토리에 μƒμ„±ν•˜μ§€ μ•Šκ³ , νŒŒμΌμ„ data url(base64) ν˜•μ‹μœΌλ‘œ λ°”κΎΈμ–΄ λ²ˆλ“€λ§λœ JavaScript νŒŒμΌμ— 직접 λ„£μŠ΅λ‹ˆλ‹€. 보톡 크기가 μž‘μ€ νŒŒμΌμ„ μ²˜λ¦¬ν•  λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
// webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg)$/,
        type: "asset/inline",
      },
    ],
  },
};

μœ„ μ„€μ •μœΌλ‘œ λΉŒλ“œλ₯Ό ν•  λ•Œ, λΉŒλ“œ 결과물의 μš©λŸ‰μ΄ asset/resource둜 λΉŒλ“œν–ˆμ„λ•Œλ³΄λ‹€ μ»€μ§€λŠ” 것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€. λ²ˆλ“€λ§λœ 결과물에 data url이 μΆ”κ°€λ˜μ—ˆκΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

asset-inline-bundle-size

πŸ‘¨β€πŸ’» asset

asset/resourceκ³Ό asset/inline을 ν•©μΉœ λ°©μ‹μž…λ‹ˆλ‹€. νŠΉμ • 크기 이상이면 asset/resource둜 λΉŒλ“œν•˜κ³  미만일 κ²½μš°λŠ” asset/inline으둜 λΉŒλ“œν•©λ‹ˆλ‹€. 기쀀이 λ˜λŠ” 값은 8kb이며, 직접 μ„€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg)$/,
        type: "asset",
        parser: {
          dataUrlCondition: {
            // 3kb 이상일 경우, asset/resource둜 λΉŒλ“œ
            maxSize: 3 * 1024,
          },
        },
      },
    ],
  },
};

πŸ‘¨β€πŸ’» asset/source

파일의 ν…μŠ€νŠΈλ₯Ό λ¬Έμžμ—΄λ‘œ 읽어 JavaScript에 μ£Όμž…ν•˜κΈ° μœ„ν•΄ μ‚¬μš©ν•©λ‹ˆλ‹€. asset/inlineκ³Ό λ™μΌν•˜κ²Œ output 디렉토리에 μƒˆλ‘œμš΄ νŒŒμΌμ„ μƒμ„±ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
// webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.txt$/,
        type: "asset/source",
      },
    ],
  },
};

πŸ–Š publicPath

output ν”„λ‘œνΌν‹°μ— publicPathλΌλŠ” μ˜΅μ…˜μ΄ μžˆμŠ΅λ‹ˆλ‹€. 이 μ˜΅μ…˜μ€ μƒμ„±λœ νŒŒμΌλ“€μ„ λΈŒλΌμš°μ €μ— λ‘œλ”©ν•˜κΈ° μœ„ν•΄ μ–΄λ–€ url을 μ‚¬μš©ν•΄μ•Όν•˜λŠ”μ§€ webpack에 μ•Œλ €μ£ΌλŠ” 역할을 ν•©λ‹ˆλ‹€.
publicPath에 μ•„λ¬΄λŸ° 섀정을 해주지 μ•Šκ³  asset/resourceλ°©μ‹μœΌλ‘œ 이미지λ₯Ό μž„ν¬νŠΈ ν–ˆλ‹€κ³  κ°€μ •ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€. 개발자 λ„κ΅¬μ—μ„œ ν•΄λ‹Ή μ΄λ―Έμ§€μ˜ src 속성을 보면 http://[domain name]/dist/[image name][ext] ν˜•μ‹μœΌλ‘œ λ˜μ–΄μžˆλŠ” 것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” webpack5μ—μ„œ publicPathκ°€ auto이기 λ•Œλ¬Έμž…λ‹ˆλ‹€. Webpack4의 κ²½μš°λŠ” 빈 λ¬Έμžμ—΄(β€œβ€) μ΄λ―€λ‘œ λ”°λ‘œ 섀정을 해주지 μ•ŠμœΌλ©΄, 파일이 λ‘œλ“œλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

Webpack5μ—μ„œ μžλ™μœΌλ‘œ assetλ“€μ˜ 경둜λ₯Ό μ„€μ •ν•΄ μ€Œμ—λ„ λ”°λ‘œ μ§€μ •ν•΄μ•Όν•˜λŠ” μ΄μœ κ°€ μžˆμ„κΉŒμš”?
이 μ˜΅μ…˜μ€ 정적 νŒŒμΌλ“€μ„ cdnμ„œλ²„λ‚˜, νŠΉμˆ˜ν•œ prefixκ°€ 뢙은 μ„œλ²„μ— μ €μž₯ν–ˆμ„ 경우 μœ μš©ν•©λ‹ˆλ‹€.

1
2
3
4
5
6
7
8
9
// webpack.config.js

module.exports = {
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "dist"),
    publicPath: "http://~~~cdn.com/",
  },
};

μœ„μ™€ 같이 publicPathλ₯Ό μ„€μ •ν•΄μ£Όκ²Œ 되면, 이미지 파일의 κ²½λ‘œλŠ” μ•„λž˜μ™€ 같이 λ‚˜μ˜€κ²Œλ©λ‹ˆλ‹€.

publicPath-cdn

πŸ’» loader

loaderλŠ” λͺ¨λ“ˆλ‘œ κ°€μ Έμ˜¬ 수 μ—†λŠ” νŒŒμΌλ“€μ„ λ‘œλ“œ ν•  λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€. λŒ€ν‘œμ μΈ loaderλ‘œλŠ” CSS νŒŒμΌμ„ λ‘œλ“œν•  λ•Œ μ‚¬μš©ν•˜λŠ” style-loader 와 css-loader, 폴리필을 ν•  λ•Œ μ‚¬μš©ν•˜λŠ” babel-loaderκ°€ μžˆμŠ΅λ‹ˆλ‹€.

πŸ‘¨β€πŸ’» css-loader

css-loaderλŠ” CSS νŒŒμΌμ„ 읽어 λ²ˆλ“€λ§ 된 JavaScript μ½”λ“œμ— μŠ€νƒ€μΌμ„ λ„£μ–΄μ£ΌλŠ” 역할을 ν•©λ‹ˆλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
// webpack.config.js

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

μœ„μ™€ 같이 μ„€μ •ν•œ ν›„ λΉŒλ“œλ₯Ό μ‹€ν–‰ν•˜κ²Œ 되면, λ²ˆλ“€λ§λœ 결과에 CSS 파일이 μ½”λ“œλ‘œ λ³€ν™˜λœ 것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ μŠ€νƒ€μΌμ„ μ½”λ“œλ‘œ λ³€ν™˜ν•˜κΈ°λ§Œ ν•΄μ„œλŠ” μŠ€νƒ€μΌμ„ μ μš©ν•  수 μ—†μŠ΅λ‹ˆλ‹€. λ³€ν™˜λœ κ²°κ³Όλ₯Ό νƒœκ·Έλ₯Ό 톡해 νŽ˜μ΄μ§€μ— μ‚½μž…ν•΄μ•Ό ν•©λ‹ˆλ‹€.

πŸ‘¨β€πŸ’» style-loader

css-loaderλ₯Ό 톡해 λ³€ν™˜λœ CSS νŒŒμΌμ„ style νƒœκ·Έμ— λ„£μ–΄μ£ΌλŠ” 역할을 ν•©λ‹ˆλ‹€.

1
2
3
4
5
6
7
8
9
10
// webpack.config.js

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

μœ„μ™€ 같이 μ„€μ •ν•œ ν›„ λΉŒλ“œλ₯Ό μ‹€ν–‰ν•˜κ²Œ 되면 μ•„λž˜μ™€ 같이, head νƒœκ·Έ ν•˜μœ„μ— style νƒœκ·Έκ°€ 생긴것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

style-loader

πŸ–Š loader μ μš©μˆœμ„œ

style-loaderλ₯Ό μ μš©ν•œ webpack 섀정을 보면 use ν”„λ‘œνΌν‹°μ—

1
["style-loader", "css-loader"];

순으둜 μ μš©ν•œκ²ƒμ„ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€. loaderλŠ” λ°°μ—΄μ˜ μ—­μˆœμœΌλ‘œ 적용이 λ˜κΈ°λ•Œλ¬Έμ— μœ„ μˆœμ„œλ₯Ό 지킀지 μ•Šμ„μ‹œ μ œλŒ€λ‘œ λ™μž‘ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
μœ„ 선언을 말둜 ν’€μ–΄ μ„€λͺ…ν•˜μžλ©΄, css-loaderλ₯Ό μ‚¬μš©ν•΄ CSS νŒŒμΌμ„ 읽어 λ²ˆλ“€λ§λœ JavaScript μ½”λ“œμ— 넣은 ν›„, style-loaderλ₯Ό μ‚¬μš©ν•΄ style νƒœκ·Έμ— λ„£μ–΄μ€€λ‹€λŠ” 의미 μž…λ‹ˆλ‹€.

πŸ‘¨β€πŸ’» sass-loader

sass-loaderλŠ” SCSS λ‚˜ SASS νŒŒμΌμ„ 읽어듀일 수 μžˆκ²Œλ” ν•΄μ£ΌλŠ” loader μž…λ‹ˆλ‹€. 이 loaderλŠ” css-loader보닀 λ¨Όμ € μ μš©λ˜κ²Œλ” μ„€μ •ν•΄ μ£Όμ–΄μ•Ό ν•©λ‹ˆλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
// webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/,
        use: ["style-loader", "css-loader", "sass-loader"],
      },
    ],
  },
};

πŸ‘¨β€πŸ’» babel-loader

babel-loader의 κ²½μš°λŠ” μ΅œμ‹  JavaScript 문법을 λͺ¨λ“  λΈŒλΌμš°μ €μ—μ„œ μ‚¬μš©ν•  수 μžˆλ„λ‘ 폴리필을 λ„μ™€μ£ΌλŠ” loader μž…λ‹ˆλ‹€. μ‚¬μš©ν•˜κ³ μžν•˜λŠ” pluginλ“€μ΄λ‚˜, preset듀을 ν•¨κ»˜ 섀정해쀄 수 μžˆμŠ΅λ‹ˆλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: "babel-loader",
          presets: ["@babel/env"],
          plugins: ["@babel/plugin-proposal-class-properties"],
        },
      },
    ],
  },
};

πŸ’» plugin

loaderλŠ” νŒŒμΌμ΄λ‚˜ λͺ¨λ“ˆμ„ μž„ν¬νŠΈν•˜λŠ”λ° μ‚¬μš©ν–ˆλ‹€λ©΄, plugin의 κ²½μš°λŠ” κ·Έ μ™Έμ˜ 좔가적인 κΈ°λŠ₯을 μˆ˜ν–‰ν•˜κ²Œλ” λ„μ™€μ€λ‹ˆλ‹€. λŒ€ν‘œμ μΈ plugin의 μ’…λ₯˜μ™€ 역할을 μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

πŸ‘¨β€πŸ’» terser-webpack-plugin

λ²ˆλ“€λ§λœ JavaScript 파일의 크기λ₯Ό μ€„μ—¬μ£ΌλŠ” plugin μž…λ‹ˆλ‹€. Webpack4μ—μ„œλŠ” npm λͺ¨λ“ˆλ‘œ μ„€μΉ˜ν•΄μ£Όμ–΄μ•Ό ν–ˆμœΌλ‚˜, webpack5μ—μ„œλŠ” λ‚΄μž₯λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

μ•„λž˜μ™€ 같이 μ„€μ •ν•΄ μ‚¬μš©ν•  수 있으며, 결과물은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

1
2
3
4
5
6
// webpack.config.js
const TerserPlugin = require("terser-webpack-plugin");

module.exports = {
  plugins: [new TerserPlugin()],
};

terser-plugin

terser-webpack-plugin의 경우 κ°œλ°œμ€‘μΌλ•ŒλŠ” μ‚¬μš©ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λΉŒλ“œ μ‹œκ°„μ΄ 였래걸리기 λ•Œλ¬Έμž…λ‹ˆλ‹€. λ˜ν•œ μ‹€μ œ 배포λ₯Ό ν•  κ²½μš°μ—λŠ” μžλ™μœΌλ‘œ 기본섀정이 μ μš©λ˜μ–΄ λΉŒλ“œλ©λ‹ˆλ‹€.

πŸ‘¨β€πŸ’» mini-css-extract

css-loader와 style-loaderλ₯Ό μ‚¬μš©ν•˜κ²Œλ˜λ©΄, CSS속성듀이 λ²ˆλ“€λ§λœ νŒŒμΌμ— λ“€μ–΄κ°€κΈ° λ•Œλ¬Έμ— 결과물의 μš©λŸ‰μ΄ μ»€μ§€κ²Œ λ©λ‹ˆλ‹€. λ§Œμ•½ CSS만 λ”°λ‘œ μΆ”μΆœν•œλ‹€λ©΄, λ²ˆλ“€λ§λœ 결과물의 μš©λŸ‰μ΄ 쀄일 수 μžˆμŠ΅λ‹ˆλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// webpack.config.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

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

  plugins: [
    new MiniCssExtractPlugin({
      filename: "style.[name].css",
    }),
  ],
};

μœ„μ™€ 같이 μ„€μ •ν•  수 있으며, λΉŒλ“œλœ κ²°κ³ΌλŠ” μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.

mini-css-extract

μœ„ κ·Έλ¦Όκ³Ό 같이 μž„ν¬νŠΈν•œ CSS파일이 λ³„λ„μ˜ 파일둜 λΆ„λ¦¬λœκ²ƒμ„ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ–Š mini-css-extract μ˜΅μ…˜

λ²ˆλ“€λ§λœ CSS의 파일λͺ…μ΄λ‚˜ μœ„μΉ˜λ₯Ό λ³€κ²½ν•˜κ³  μ‹Άλ‹€λ©΄, μΈμŠ€ν„΄μŠ€ μƒμ„±μ‹œμ— μ˜΅μ…˜λ“€μ„ 인자둜 λ„˜κ²¨μ£Όλ©΄ λ³€κ²½ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ‘¨β€πŸ’» clean-webpack-plugin

λΉŒλ“œκ°€ μ‹€ν–‰λ˜κΈ°μ „ output 디렉토리에 μžˆλŠ” 이전 결과물듀을 λͺ¨λ‘ μ§€μ›Œμ£ΌλŠ” 역할을 ν•©λ‹ˆλ‹€.

1
2
3
4
5
6
// webpack.config.js
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
  plugins: [new CleanWebpackPlugin()],
};

πŸ‘¨β€πŸ’» html-webpack-plugin

μ§€κΈˆκΉŒμ§€λŠ” λΉŒλ“œ 결과물둜 λ‚˜μ˜¨ JavaScript νŒŒμΌμ΄λ‚˜ CSS νŒŒμΌλ“€μ„ 직접 html νŒŒμΌμ— μž„ν¬νŠΈν•΄ μ‹€ν–‰ν–ˆμŠ΅λ‹ˆλ‹€. 결과물둜 λ‚˜μ˜¨ 파일의 κ°―μˆ˜κ°€ μ κ±°λ‚˜ 이름이 λ‹¨μˆœν•˜λ‹€λ©΄ 큰 λ¬Έμ œκ°€ λ˜μ§€ μ•Šμ§€λ§Œ, κ·Έ λ°˜λŒ€μ˜ κ²½μš°λŠ” λΆˆνŽΈν•  수 μžˆμŠ΅λ‹ˆλ‹€. 이런 문제λ₯Ό html-webpack-plugin으둜 ν•΄κ²°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
html-webpack-plugin의 경우 λ²ˆλ“€λ§ 결과둜 λ‚˜μ˜¨ νŒŒμΌλ“€μ„ μžλ™μœΌλ‘œ htmlνŒŒμΌμ— λ„£μ–΄μ£ΌλŠ” 역할을 ν•©λ‹ˆλ‹€.

1
2
3
4
5
6
// webapck.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  plugins: [new HtmlWebpackPlugin()],
};

πŸ’» mode

ν˜„μž¬κΉŒμ§€μ˜ μ„€μ •μœΌλ‘œ λΉŒλ“œλ₯Ό 싀행해보면 터미널에 μ•„λž˜μ™€ 같은 κ²½κ³  메세지가 λ‚˜μ˜€λŠ”κ²ƒμ„ λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

mode

mode ν”„λ‘œνΌν‹°λ₯Ό μ„€μ •ν•΄μ•Ό ν•œλ‹€λŠ” κ²½κ³ λ¬Έμž…λ‹ˆλ‹€. mode ν”„λ‘œνΌν‹°μ— 값을 μ„€μ •ν•΄ 쀌으둜써, κ°’κ³Ό μΌμΉ˜ν•˜λŠ” webpack μ΅œμ ν™” λ‘œμ§μ„ λ™μž‘μ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, mode에 developmentλ₯Ό μ„€μ •ν•œ 경우 νŠΉλ³„ν•œ μ΅œμ ν™” 둜직이 λ™μž‘ν•˜μ§€ μ•Šμ§€λ§Œ production을 μ„€μ •ν•  경우 λ‚΄λΆ€μ μœΌλ‘œ TerserPlugin등이 λ™μž‘ν•©λ‹ˆλ‹€. mode의 μžμ„Έν•œ 차이점듀은 μ΄κ³³μ—μ„œ 확인해볼 수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ–Š Webpack 파일 λΆ„λ¦¬ν•˜κΈ°

mode ν”„λ‘œνΌν‹°μ˜ 경우 λ°°ν¬μ‹œμ—λŠ” production으둜 κ°œλ°œμ‹œμ—λŠ” development둜 섀정을 ν•΄μ£Όμ–΄μ•Ό ν•˜λŠ”λ°, 맀번 값을 λ°”κΎΈκΈ°λŠ” λ²ˆκ±°λ‘­μŠ΅λ‹ˆλ‹€. 이 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄, 배포용 webpack 파일과 개발용 webpack νŒŒμΌμ„ 뢄리해 관리해주면 λ©λ‹ˆλ‹€. λΉŒλ“œ λͺ…령은 --config ν”Œλž˜κ·Έμ™€ ν•¨κ»˜ webpack의 경둜λ₯Ό λͺ…μ‹œν•΄ μ£Όλ©΄ λ©λ‹ˆλ‹€.

πŸ’» dev server

μ§€κΈˆκΉŒμ§€λŠ” λΉŒλ“œλ₯Ό ν•œ ν›„, html-webpack-plugin으둜 μƒμ„±λœ html νŒŒμΌμ„ 직접 μ‹€ν–‰μ‹œμΌœ κ²°κ³Όλ₯Ό ν™•μΈν–ˆμŠ΅λ‹ˆλ‹€. μ½”λ“œμ˜ 변경이 μžˆμ„λ•Œ λ§ˆλ‹€ 맀번 λΉŒλ“œ ν›„ μ‹€ν–‰ν•˜λŠ” 것은 번거둭고 λΆˆνŽΈν•©λ‹ˆλ‹€. 이λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ webpack-dev-serverλ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
webpack-dev-serverλŠ” 개발용 μ„œλ²„λ₯Ό μ œκ³΅ν•΄ μ½”λ“œμ˜ λ³€κ²½ 사항이 μžˆμ„ λ•Œ λ§ˆλ‹€ μžλ™μœΌλ‘œ λΉŒλ“œ 및 reloadμ‹œμΌœ λ°”λ‘œ κ²°κ³Όλ₯Ό 확인할 수 있게 ν•΄μ£ΌλŠ” 도ꡬ μž…λ‹ˆλ‹€. μ§€κΈˆλΆ€ν„° webpack-dev-server의 μ‹€ν–‰ 방법과 μ„€μ • 방법을 μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€.
dev-serverλŠ” webpack serve --config [config 파일 경둜]둜 μ‹€ν–‰ν•  수 있으며 --hot ν”Œλž˜κ·Έλ₯Ό λΆ™μΌκ²½μš° HMR(Hot Module Replace)λ₯Ό μˆ˜ν–‰ν•©λ‹ˆλ‹€. 단 webpack-dev-server v4.0.0 λΆ€ν„°λŠ” 기본적으둜 ν•΄λ‹Ή μ˜΅μ…˜μ΄ ν™œμ„±ν™” λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

webpack-dev-server섀정은 μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
13
module.exports = {
  devServer: {
    // 포트번호
    port: 9000,
    // 정적 파일 제곡 μœ„μΉ˜ (보톡 output directory와 동일)
    static: path.resolve(__dirname, "dist"),
    devMiddleware: {
      // html νŒŒμΌμ„ μ œκ³΅ν•  μœ„μΉ˜
      index: "index.html",
      writeToDisk: true,
    },
  },
};

ν•œκ°€μ§€ λˆˆμ—¬κ²¨ λ³Ό 점은 writeToDiskν•„λ“œ μž…λ‹ˆλ‹€. webpack-dev-serverλŠ” in memory둜 λ™μž‘ν•˜κΈ° λ•Œλ¬Έμ— 싀행을 해도 결과물이 ν”„λ‘œμ νŠΈλ‚΄μ— λ‚˜νƒ€λ‚˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λ§Œμ•½ 결과물을 직접 ν™•μΈν•˜κ³  μ‹Άλ‹€λ©΄, ν•΄λ‹Ή μ˜΅μ…˜μ„ true둜 μ„€μ •ν•˜λ©΄ λ©λ‹ˆλ‹€.

이 κΈ°μ‚¬λŠ” μ €μž‘κΆŒμžμ˜ CC BY 4.0 λΌμ΄μ„ΌμŠ€λ₯Ό λ”°λ¦…λ‹ˆλ‹€.

μΌλ ‰νŠΈλ‘  Main Process - app

TypeScript νƒ€μž… μ§‘ν•©μœΌλ‘œ μƒκ°ν•˜κΈ°