Webpack

Webpack - 로더(Loader)

shin96bc 2024. 3. 10. 20:52

❓로더의 역할은?

Webpack은 모든 파일을 모듈로 바라봅니다. Javascript로 만든 모듈 뿐만 아니라 StyleSheet, Image, Font까지도 전부 모듈로 보기 때문에 import 구문을 사용하면 Javascript 코드 안으로 가져올 수 있습니다.

 

이것이 가능한 이유는 Webpack의 로더 덕분입니다. 로더는 Typescript 같은 다른 언어를 Javascript 문법으로 변환해 주거나 이미지를 data URL 형식의 문자열로 변환합니다. 뿐만아니라 CSS 파일을 Javascript에서 직접 로딩할 수 있도록 해줍니다.

 

🔨 간단한 커스텀 로더 만들기

로더를 사용하기 전에 동작 원리를 이해하기 위해 로더를 직접 만들어 봅시다.

 

my-webpack-loader.js 파일 생성

module.exports = function myWebpackLoader (content) {
	console.log('myloader가 동작함');
	return content;
}
// 로더는 함수로 만들수가 있는데 로더가 읽은 파일의 내용이 함수 인자 content로 전달됩니다.
// 로더가 동작하는지 확인하는 용도로 console.log만 찍고 content를 그대로 반환해줍니다.
// 이제 직접 만든 로더를 사용하려면 webpack 설정 파일의 module이라는 객체에 추가해줘야 합니다.

 

webpack.config.js 파일 수정

// node의 path모듈
const path = require('path');

// node의 모듈 시스템을 기반으로 작성합니다.
module.exports = {
	mode: 'development',
	entry: {
		main: './src/app.js'
	},
	output: {
		path: path.resolve('./dist'),
		filename: '[name].js'
	},
	/**
	 * 로더는 module 객체에 rules라는 배열에다가 추가할 수 있습니다.
	 * rules라는 배열은 test, use라는 두개의 key를 갖는 객체가 들어갑니다.
	*/
	module: {
		rules: [
			{
				// test에는 로더가 처리해야될 파일들의 패턴을 지정합니다.
				// (예시로 .js 확장자로 끝나는 모든 파일을 지정)
				test: /\.js$/,
				// use에는 사용할 로더를 지정합니다.(절대경로로 지정)
				use: [
					path.resolve('./my-webpack-loader.js')
				]
			}
		]
	}
}

 

이제 my-webpack-loader에 기능을 추가해 봅시다.

module.exports = function myWebpackLoader (content) {
	// 로더가 읽은 Javascript 파일의 내용중에서
	// console.log(라는 문자열을 만나면 alert(로 변경해서 반환하도록 하는 기능을 추가합니다.
	// 즉 로더가 읽은 모든 Javascript 파일의 console.log는 alert으로 변경되어서 
	// log가 찍히는 대신에 alert창이 뜨게 됩니다.
	return content.replace('console.log(', 'alert(');
}

 

🤗 자 이제 자주 사용하는 로더들을 알아봅시다.

css-loader

webpack은 모든 것을 모듈로 바라보기 때문에 Javascript 뿐만 아니라 StyleSheet도 import 구문으로 불러 올 수 있습니다.

 

app.js

import './app.css';

 

app.css

body {
	background-color: green;
}

 

위의 코드처럼 CSS 파일을 Javascript에서 불러와 사용하려면 CSS를 모듈로 변환하는 작업이 필요합니다.

 

css-loader가 그러한 역할을 합니다. css-loader는 우리 코드에서 CSS 파일을 모듈처럼 불러와서 사용할 수 있도록 해줍니다.

 

먼저 css-loader를 설치합니다.

npm install -D css-loader

 

webpack.config.js에 css-loader를 추가합니다.

// node의 path모듈
const path = require('path');

// node의 모듈 시스템을 기반으로 작성합니다.
module.exports = {
	mode: 'development',
	entry: {
		main: './src/app.js'
	},
	output: {
		path: path.resolve('./dist'),
		filename: '[name].js'
	},
	module: {
		rules: [
			{
				// .css 확장자로 끝나는 모든 파일을 지정
				test: /\.css$/,
				// 로더에 css-loder를 지정
				use: [
					'css-loader'
				]
			}
		]
	}
}

 

하지만 이렇게 css-loader를 사용해서 css를 모듈로 변환해도 아직 실제 브라우저에는 적용되지 않습니다.

 

왜냐하면 HTML 코드가 DOM이라는 모습으로 변환되어야 브라우저에서 보이듯이 CSS 코드도 CSSOM(CSS Object Model)이라는 형태로 바뀌어야만 브라우저에 적용되기 때문입니다.

 

CSSOM이라는 형태로 변환되려면 HTML 파일에서 CSS를 직접 불러오거나, 인라인 스크립트로 넣어줘야 하는데 아직 그런 처리가 되어있지 않고, Javascript 파일에만 CSS 코드가 있는 상태이기 때문에 브라우저에 적용되지 않는 것 입니다.

 

그래서 CSS를 모듈로 변환할 때는 css-loader와 style-loader를 함께 사용해야 합니다.

 

 

style-loader

style-loader는 Javascript로 변경된 CSS 코드들을 HTML에 style태그를 사용해서 넣어주는 로더입니다.

 

먼저 style-loader를 설치합니다.

npm install style-loader

 

webpack.config.js에 style-loader를 추가합니다.

// node의 path모듈
const path = require('path');

// node의 모듈 시스템을 기반으로 작성합니다.
module.exports = {
	mode: 'development',
	entry: {
		main: './src/app.js'
	},
	output: {
		path: path.resolve('./dist'),
		filename: '[name].js'
	},
	module: {
		rules: [
			{
				// .css 확장자로 끝나는 모든 파일을 지정
				test: /\.css$/,
				// 로더에 style-loder를 추가
				// 로더는 한 파일에 대해서 여러개가 실행될 수 있는데, 순서는 뒤에서부터 앞으로 실행됩니다.
				use: [
					'style-loader', // 2번으로 실행
					'css-loader'    // 1번으로 실행
				]
			}
		]
	}
}

 

 

file-loader(v4 사용법)

CSS뿐만 아니라 소스코드에서 사용하는 모든 파일을 모듈로 사용하게끔 할 수 있습니다.

 

파일을 모듈 형태로 지원하고 webpack output에 파일을 옮겨주는 것이 file-loader가 하는 일입니다. 가령 CSS에서 url() 함수에 이미지 파일 경로를 지정할 수 있는데 webpack은 file-loader를 이용해서 이 파일을 처리합니다.

 

app.css

body {
	background-image: url(bg.png);
}

// 배경 이미지를 bg.png 파일로 지정했습니다.
// webpack은 entry 포인트인 app.js가 로딩하는 app.css 파일을 읽을 것입니다.
// 그리고 이 StyleSheet는 url() 함수로 bg.png를 사용하는데 이 때 file-loader를 동작시킵니다.

 

먼저 file-loader를 설치합니다.

npm install file-loader

 

webpack.config.js에 file-loader를 추가합니다.

// node의 path모듈
const path = require('path');

// node의 모듈 시스템을 기반으로 작성합니다.
module.exports = {
	mode: 'development',
	entry: {
		main: './src/app.js'
	},
	output: {
		path: path.resolve('./dist'),
		filename: '[name].js'
	},
	module: {
		rules: [
			{
				test: /\.css$/,
				use: [
					'style-loader', // 2번으로 실행
					'css-loader'    // 1번으로 실행
				]
			},
			{
				// 새로운 파일 형식을 지정합니다. 
				// 위에서 png이미지를 사용했기 때문에 .png확장자로된 파일을 읽도록 지정합니다.
				test: /\.png$/,
				// 로더에는 file-loader를 지정합니다.
				use: [
					'file-loader'
				]
			}
		]
	}
}

 

file-loader는 캐시 갱신을 위해서 번들링을 진행할 때마다 매번 우리가 사용한 ‘bg.png’라는 파일명을 ‘유니크한 해시값. png’로 변경해버립니다.

  • 정적파일(Javascript, CSS, Font, Image)의 경우에는 성능향상을 위해서 브라우저에서 캐시하는 경우가 많이 있습니다. 그럴때 만약 이미지는 이전과 달라졌는데 파일명이 같다면 이전에 캐시로 저장되어있는 이미지 파일을 사용하기 때문에 의도와는 다른 이미지 파일을 보여주게 됩니다. 이런 상황을 예방하기 위해서 유니크한 해시값으로 파일명을 변환해서 사용합니다.

 

이렇게 file-loader를 설정하고 브라우저를 실행하니 이미지의 경로를 찾지 못하는 문제가 발생합니다.

  • 먼저 로더들이 읽은 파일들은 dist라는 디렉토리안에 생성되는데 우리는 CSS 코드에서 url() 함수에 이미지 경로를 지정할 때 ‘url(bg.png)’ 이런식으로 지정했기 때문에 경로를 찾지 못해서 이미지를 로딩하지 못하는 것입니다.

 

이 문제를 해결하기 위해서는 webpack.config.js 파일에서 file-loader에 옵션을 설정해줘야합니다.

 

webpack.config.js에서 file-loader를 수정합니다.

// node의 path모듈
const path = require('path');

// node의 모듈 시스템을 기반으로 작성합니다.
module.exports = {
	mode: 'development',
	entry: {
		main: './src/app.js'
	},
	output: {
		path: path.resolve('./dist'),
		filename: '[name].js'
	},
	module: {
		rules: [
			{
				test: /\.css$/,
				use: [
					'style-loader', // 2번으로 실행
					'css-loader'    // 1번으로 실행
				]
			},
			{
				// 보통은 이미지 파일들의 확장자가 다양하기 때문에 
				// test구문에 여러개의 확장자명을 지정해줍니다.
				test: /\.(png|jpg|gif|svg)$/,
				// 로더에 옵션을 설정하기 위해서 use구문을 삭제하고 다른 방식으로 작성합니다.
				// loader 구문을 사용해서 사용할 로더를 지정합니다.
				loader: 'file-loader',
				// option 구문을 사용해서 로더에 옵션을 설정할 수 있습니다.
				options: {
					// publicPath는 file-loader가 처리하는 파일을 모듈로 사용했을 때 
					// 해당 파일 경로 앞에 추가되는 문자열 입니다.
					// 우리는 output path를 ./dist로 지정했기 때문에 ./dist로 설정해줍니다.
					publicPath: './dist',
					// name에는 file-loader가 파일을 output에 복사할 때 사용할 파일 이름을 지정합니다.
					// [name]은 원본 파일명, [ext]는 확장자명, [hash]는 매번 달라지는 해시값을 의미합니다.
					// 캐시 무력화를 위해서 쿼리스트링(?)으로 매번 달라지는 [hash]값을 지정합니다.
					// 이렇게 지정하면 해당 이미지를 호출할 때 './dist/bg.png?해시값'으로 호출되기 때문에
					// 캐시와 관련된 문제를 해결할 수 있습니다.  
					name: '[name].[ext]?[hash]'
				}
			}
		]
	}
}

 

이렇게 해주면 이제 브라우저에서 이미지가 로딩됩니다.

 

 

url-loader(v4 사용법)

사용하는 이미지의 개수가 많다면 네트워크 리소스를 사용하는 부담이 있고 사이트 성능에 영향을 줄 수도 있습니다.

 

만약 한 페이지에서 작은 이미지를 여러개 사용한다면 Data URI Scheme를 이용하는 방법이 더 좋습니다. 이미지를 Base64로 인코딩하여 문자열 형태로 소스코드에 넣는 형식입니다. url-loader는 이러한 처리를 자동화해주는 로더입니다.

<img src="data:image/png;base64,iVBORw0KGgoAAA
ANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4
//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU
5ErkJggg==" alt="Red dot" />

 

  • CSS 예시
ul.checklist li.complete {
    padding-left: 20px;
    background: white url('data:image/png;base64,iVB\
ORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEU\
AAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8\
yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAEl\
FTkSuQmCC') no-repeat scroll left top;
}

 

  • Javascript 예시
window.open('data:text/html;charset=utf-8,' +
    encodeURIComponent( // Escape for URL formatting
        '<!DOCTYPE html>'+
        '<html lang="en">'+
        '<head><title>Embedded Window</title></head>'+
        '<body><h1>42</h1></body>'+
        '</html>'
    )
);

 

  • SVG 예시
<svg>
<image width="64" height="24" href="data:image/jpeg;base64,
/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDADIiJSwlHzIsKSw4NTI7S31RS0VFS5ltc1p9tZ++u7Kf
r6zI4f/zyNT/16yv+v/9////////wfD/////////////2wBDATU4OEtCS5NRUZP/zq/O////////
////////////////////////////////////////////////////////////wAARCAAYAEADAREA
AhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAQMAAgQF/8QAJRABAAIBBAEEAgMAAAAAAAAAAQIR
AAMSITEEEyJBgTORUWFx/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAA
AAD/2gAMAwEAAhEDEQA/AOgM52xQDrjvAV5Xv0vfKUALlTQfeBm0HThMNHXkL0Lw/swN5qgA8yT4
MCS1OEOJV8mBz9Z05yfW8iSx7p4j+jA1aD6Wj7ZMzstsfvAas4UyRHvjrAkC9KhpLMClQntlqFc2
X1gUj4viwVObKrddH9YDoHvuujAEuNV+bLwFS8XxdSr+Cq3Vf+4F5RgQl6ZR2p1eAzU/HX80YBYy
JLCuexwJCO2O1bwCRidAfWBSctswbI12GAJT3yiwFR7+MBjGK2g/WAJR3FdF84E2rK5VR0YH/9k="/>
</svg>

 

먼저 url-loader를 설치합니다.

npm install -D url-loader

 

그리고 webpack.config.js에 url-loader를 추가합니다.

// node의 path모듈
const path = require('path');

// node의 모듈 시스템을 기반으로 작성합니다.
module.exports = {
	mode: 'development',
	entry: {
		main: './src/app.js'
	},
	output: {
		path: path.resolve('./dist'),
		filename: '[name].js'
	},
	module: {
		rules: [
			{
				test: /\.css$/,
				use: [
					'style-loader', // 2번으로 실행
					'css-loader'    // 1번으로 실행
				]
			},
			{
				test: /\.(png|jpg|gif|svg)$/,
				// 기존에 사용했던 file-loader 대신에 url-loader를 지정해줍니다.
				loader: 'url-loader',
				options: {
					// 대부분의 옵션은 file-loader와 동일하게 사용합니다.
					publicPath: './dist',
					name: '[name].[ext]?[hash]',
					// url-loader에서는 추가적으로 limit키워드를 사용해서 파일의 용량을 설정해줍니다.
					// 이렇게 지정하면 url-loader가 test 구문에 있는 파일들을 처리할 때 
					// 20KB미만의 파일은 url-loader를 사용해서 Base64로 변환하게 됩니다.
					// 20KB가 넘어가는 파일들은 file-loader에게 처리하게 합니다.
					limit: 20000, // 20KB
				}
			}
		]
	}
}

 

이렇게 url-loader를 사용하면 파일의 크기에 따라 Base64로 인코딩해서 Javascript 문자열로 사용하거나, file-loader를 사용해서 파일을 복사하도록 설정할 수 있습니다.

 

 

잠깐! webpack v5 이후에 file-loader, url-loader는 webpack 기본 모듈로 채택되면서 더 이상 webpack과 호환되지 않는다고 합니다. 그러니 webpack공식사이트를 참고해서 사용방법을 확인하시기 바랍니다.

 

 

file-loader(v5 사용법)

더 이상 file-loader를 install 하지 않습니다.

 

webpack.config.js 설정

const path = require('path');

module.exports = {
  mode: 'development',
  entry: {
    main: './src/app.js'
  },
  output: {
    filename: '[name].js',
    path: path.resolve('./dist'),
    assetModuleFilename: '[hash][ext][query]' //file-loader output 설정
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      },
      { 
				//file-loader 설정
        test: /\.(jpg|png)$/,
        type: 'asset/resource'
      }
    ]
  }
}

 

 

url-loader(v5 사용법)

더 이상 url-loader를 install 하지 않습니다.

 

webpack.config.js 설정

const path = require('path');

module.exports = {
  mode: 'development',
  entry: {
    main: './src/app.js'
  },
  output: {
    filename: '[name].js',
    path: path.resolve('./dist'),
    assetModuleFilename: '[hash][ext][query]' //file-loader output 설정
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      },
      { 
				//url-loader 설정
        test: /\.(jpg|png)$/,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 8 * 1024 // 8kb
          }
        }
      }
    ]
  }
}

'Webpack' 카테고리의 다른 글

Webpack - 플러그인(Plugin)  (0) 2024.03.10
Webpack - 엔트리(Entry)/아웃풋(Output)  (0) 2024.03.10
Webpack - Webpack이란?  (0) 2024.03.10