로더가 파일 단위로 처리한다면, 플러그인은 번들된 결과물을 처리한다. 번들된 자바스크립트 난독화나 특정 테스트 추출 용도로 사용한다.
플러그인은 웹팩의 척추(backbone)라고 불린다. 웹팩 자체도 내가 웹팩에서 설정하는 플러그인과 같은 방식으로 만들어졌다.
플러그인은 로더가 못하는 일을 해줄 수 있다. 웹팩은 공개적으로 많은 플러그인을 제공한다.
플러그인의 정체는 자바스크립트 apply 메소드이다. apply
메소드가 웹팩 컴파일에 의해 호출되며 전체 컴파일 라이프사이클에 대한 엑세스를 제공한다.
const pluginName = 'ConsoleLogOnBuildWebpackPlugin';
class ConsoleLogOnBuildWebpackPlugin {
apply(compiler) {
compiler.hooks.run.tap(pluginName, (compilation) => {
console.log('The webpack build process is starting!');
});
}
}
module.exports = ConsoleLogOnBuildWebpackPlugin;
컴파일러 훅의 탭 메소드의 첫번째 파라미터가 케멀 케이스로된 플러그인 이름이다. 모든 훅에서 재활용될 수 있도록 상수를 사용하는 것이 권장된다.
개발자에게 웹팩의 진짜 잠재력을 보여주는 것이 플러그인이다. 단계적인 빌드 콜백을 사용하여, 개발자는 웹팩 빌드 프로세스에 직접 정의한 동작을 넣어줄 수 있다. 플러그인을 작성하는 게 로더를 작성하는 것보다 더 고급 개념이다. 웹팩의 저수준 내부 구현을 이해할 필요가 있기 때문이다.
작성 방법은 다음과 같다.
apply
메소드를 정의하라.플러그인은 프토토타입에 apply
메소드를 가진 오브젝트에 의해 인스턴스화 된다. apply
메소드는 플러그인을 설치할 때 웹팩 컴파일러에 의해 딱 한번 호출된다. apply
메소드는 존재하는 웹팩 컴파일러에 레퍼런스로 주어져 컴파일러 콜백에 접근할 권한을 받는다. 플러그인 구조는 다음과 같다.
class HelloWorldPlugin {
apply(compiler) {
compiler.hooks.done.tap(
'Hello World Plugin',
// stats는 done 훅이 탭 되었을 때, 인자로 전달된다.
(stats) => {
console.log("HelloWorldPlugin worked");
console.log("stats", stats);
}
)
}
}
module.exports = HelloWorldPlugin;
플러그인을 사용하고 싶다면, 웹팩 설정파일에서 다음과 같이 plugin
배열에 인스턴스를 추가하면 된다.
const HelloWorldPlugin = require('hello-world');
module.exports = {
// ... other configuration settings ...
plugins: [new HelloWorldPlugin({ options: true })]
};
이전에 실행했던 로더는 모든 파일에 대해서 검증을 거쳐 파일단위로 각각 실행됐다면, 플러그인은 모든 번들링 작업이 끝나고 난 뒤에 한 번만 실행이 된다.
그렇다면 플러그인은 어떻게 번들된 결과에 접근이 가능한걸까? 웹팩 내장 플러그인인
BannerPlugin
을 참고하여 알아보자.
class HelloWorldPlugin {
apply(compiler) {
compiler.hooks.done.tap("Hello World Plugin", (stats) => {
console.log("HelloWorldPlugin worked");
});
compiler.hooks.emit.tapAsync(
"Hello World Plugin",
(compilation, callback) => {
console.log(
"compilation.assets['main.js']",
compilation.assets["main.js"].source()
);
callback();
}
);
}
}
module.exports = HelloWorldPlugin;
위와 같이 소스코드를 작성하고, 실행해보면
이렇게 번들링 이후의 소스코드를 볼 수 있다. 그 말인 즉슨, 우리가 웹팩의 플러그인을 이용해 웹팩의 특정 컴파일 시점에 우리 소스코드를 삽입할 수 있다는 것이다.
class HelloWorldPlugin {
apply(compiler) {
compiler.hooks.done.tap("Hello World Plugin", (stats) => {
console.log("HelloWorldPlugin worked");
});
compiler.hooks.emit.tapAsync(
"Hello World Plugin",
(compilation, callback) => {
const originalBundlingResult =
compilation.assets["main.js"].source();
compilation.updateAsset("main.js", {
source() {
const comment = [
"/**",
" * 안녕하세요. 사용자 정의 코멘트입니다.",
" */",
].join("\n");
return comment + "\n\n" + originalBundlingResult;
},
});
callback();
}
);
}
}
module.exports = HelloWorldPlugin;
위와 같이 코드를 작성하게 되면, 번들링 결과에 내가 작성한 문자열 주석이 추가된다.
emit
은 라이프사이클 중 결과 파일이 나오기 직전의 상태이다.직접 타자로 친 것이 아니다.