프론트엔드 개발 환경 공부 #7 플러그인

Jake Seo·2021년 9월 18일
0

플러그인 읽어보기

플러그인의 진짜 역할

로더가 파일 단위로 처리한다면, 플러그인은 번들된 결과물을 처리한다. 번들된 자바스크립트 난독화나 특정 테스트 추출 용도로 사용한다.

플러그인 공식문서 설명

플러그인은 웹팩의 척추(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을 참고하여 알아보자.

emit을 이용한 번들링 소스 추출해보기

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;

위와 같이 코드를 작성하게 되면, 번들링 결과에 내가 작성한 문자열 주석이 추가된다.

  • Compiler module의 훅을 통해 특정 hook 시점에 개입할 수 있다.
    • 이 중 emit은 라이프사이클 중 결과 파일이 나오기 직전의 상태이다.
  • Compilation Object를 통해 컴파일 시점에 다양한 내부 정보에 접근하고 수정도 할 수 있다.

간략한 그림 설명

소스코드 결과

직접 타자로 친 것이 아니다.

profile
풀스택 웹개발자로 일하고 있는 Jake Seo입니다. 주로 Jake Seo라는 닉네임을 많이 씁니다. 프론트엔드: Javascript, React 백엔드: Spring Framework에 관심이 있습니다.

0개의 댓글