[React 정복기] 바벨과 웹팩 자세히 들여다보기 2 (실전 리액트 프로그래밍) - 5

예흠·2020년 12월 16일
0

React 정복기!

목록 보기
5/8
post-thumbnail

React를 정복해보자 💪

실전 리액트 프로그래밍 개정판 - 이재승 지음 (참고자료)

지난번에 이어서 바벨에 대해서 더 깊이 공부해 보자.

* 바벨 플러그인 제작하기

1. AST 구조 들여다보기

바벨은 문자열로 입력되는 코드를 AST(abstract syntax tree)라는 구조체로 만들어서 처리한다. 플러그인에서는 AST를 기반으로 코드를 변경한다. => 플러그인을 제작하려면 AST의 구조를 알아야 한다.

astexplorer 사이트에서 const v1 = a + b; 코드의 AST를 확인해보자. 바벨은 babylon이라는 파서(parser)를 이용해서 AST를 만든다.
=> astexplorer 사이트의 파서 목록에서 babylon을 선택하면 다음과 같은 결과가 나온다.

  • "type": "Program" : AST의 각 노드는 type 속성이 있다.
  • "type": "VariableDeclaration" : 변수 선언은 VariableDeclaration 타입이다.
  • "declarations" : 하나의 문장에서 여러 개의 변수를 선언할 수 있기 때문에 배열로 관리된다.
  • 두 번째 "type": "VariableDeclaration" : 선언된 변수의 타입은 VariableDeclaration이다.
  • "type": "Identifier" : 개발자가 만들어낸 각종 이름은 Identifier 타입으로 만들어 진다.
  • "name": "v1 : 실제 코드에 사용된 v1이라는 이름이 보인다.
  • "type": "BinaryExpression" : 사칙연산은 BinaryExpression 타입으로 만들어진다. left, right 속성으로 연산에 사용되는 변수나 값이 들어간다.

타입의 종류는 굉장히 많아서 필요할 때마다 문서를 찾거나 하자.

2. 바벨 플러그인의 기본 구조

바벨 플러그인은 하나의 자바스크립트 파일로 만들 수 있다. 바벨 플러그인의 기본 구조는 다음과 같다.

module.exports = function({ types: t }) { // (1)
  const node = t.BinaryExpression('+', t.Identifier('a'), t.Identifier('b')); // (2)
  console.log('isBinaryExpression:', t.isBinaryExpression(node)); // (3)
  return {};
};
  • (1) : types 매개변수를 가진 함수를 내보낸다.
  • (2) : types 매개변수를 이용해서 AST 노드를 생성할 수 있다. 여기서는 두 변수의 덧셈을 AST 노드로 만들었다.
  • (3) : types 매개변수는 AST 노드의 타입을 검사하는 용도로도 사용된다.

이제 반환값의 형태를 자세히 살펴보자

module.exports = function({ types: t}) {
  return {
    visitor: { // (1)
      Identifier(path) { // (2)
        console.log('Identifier name:', path.node.name);
      },
      BinaryExpression(path) { // (3)
        console.log('BinaryExpression operator:', path.node.operator);
        // ...(모든 괄호 닫기)
  • (1) : visitor 객체 내부에서 노드의 타입 이름으로 된 함수를 정의할 수 있다. 해당하는 타입의 노드가 생성되면 같은 이름의 함수가 호출된다.
  • (2) : Identifier 타입의 노드가 생성되면 호출되는 함수다. 만약 const v1 = a + b; 코드가 입력되면 이 함수는 세 번 호출된다.
  • (3) : BinaryExpression 타입의 노드가 생성되면 호출되는 함수다. 만약 const v1 = a + b; 코드가 입력되면 이 함수는 한 번 호출된다.

3. 바벨 플러그인 제작하기: 모든 콘솔 로그 제거

프로젝트 하나를 만들어 보자.

mkdir test-babel-custom-plugin
cd test-babel-custom-plugin
npm init -y
npm install @babel/core @babel/cli

프로젝트 루트에 src 폴더를 만들고 그 밑에 code.js 파일을 만들고 콘솔 로그가 포함된 코드를 작성해 보자.

저 코드에 등장하는 두 개의 콘솔 로그를 제거하는 것을 목표로 해보자.

플러그인을 제작하기 위해서는 콘솔 로그 코드의 AST 구조를 이해해야 한다. console.log('asdf'); 코드의 AST는 다음과 같다.

  • 콘솔 로그 코드는 ExpressionStatement 노드로 시작한다.
  • 함수 또는 메서드를 호출하는 코드는 CallExpression 노드로 만들어진다.
  • 메서드 호출은 CallExpression 노드 내부에서 MemberExpression 노드로 만들어진다.

자 이제 프로젝트 루트에 Plugins 폴더를 만들고 그 밑에 remove-log.js 파일을 만들고, 다음 코드를 입력하자.

  • ExpressionStatement(path) : ExpressionStatement 노드가 생성되면 호출되도록 메서드를 등록한다.
  • 첫번째 if : ExpressionStatement 노드의 expression 속성이 CallExpression 노드인지 검사한다.
  • 두번째 if : callee 속성이 MemberExpression 노드인지 검사한다.
  • 세번째 if : console 객체의 log 메서드가 호출된 것인지 검사한다.

모든 조건을 만족하면 AST에서 ExpressionStatement 노드를 제거한다.

우리가 만든 플러그인을 바벨 설정에 추가해 보기 위해 babel.config.js 파일을 만들고 다음 코드를 입력한다.

이제 바벨을 실행해 보자
npx babel src/code.js

목표대로 콘솔 로그가 제거됐다.

4. 바벨 플러그인 제작하기: 함수 내부에 콘솔 로그 추가

이번에는 이름이 on으로 시작하는 모든 함수에 콘솔 로그를 추가해 주는 플러그인을 제작해 보자.

먼저 함수의 AST 구조를 파악해 보자. 다음은 function f1(p1) {let v1;} 코드로 만들어진 AST다.

  • 함수를 정의하는 코드는 FunctionDeclaration 노드로 만들어 진다.
  • 함수 이름은 id 속성에 들어 있다.(이 값이 on으로 시작하는지 검사하면 된다.)
  • BlockStatement 노드의 body 속성에는 함수의 모든 내부 코드에 대한 노드가 배열로 담겨 있다.(이 배열의 가장 앞쪽에 콘솔 로그 노드를 넣으면 된다.)

Plugins 폴더 밑에 insert-log.js 파일을 만들고, 다음 코드를 입력해 보자.

  • FunctionDeclaration(path) : FunctionDeclaration 노드가 생성되면 호출되는 함수를 정의한다.
  • if ... : 함수 이름이 on으로 시작하는지 검사한다.
  • .unshiftContainer : body 배열의 앞쪽에 노드를 추가하기 위해 unshiftContainer 메서드를 호출한다.
  • t.expressionStatement... : 콘솔 로그 노드를 생성한다. 이 노드는 console.log(`call ${함수 이름}`); 형태의 코드를 담고 있다.

babel.config.js 파일을 수정해서 remove-log.js 플러그인을 insert-log.js 플러그인으로 교체하고 바벨을 실행해 보자.

npx babel src/code.js

의도대로 on으로 시작하는 함수의 최상단에 콘솔 로그를 출력하는 코드가 생성된 것을 확인할 수 있다.

profile
노래하는 개발자입니다.

0개의 댓글