※ 알림

이 글은 원작자의 허락을 받고 번역한 번역글입니다.

번역의 부드러운 이해를 위해 생략, 의역 및 추가한 부분이 있을 수 있습니다.

 

원본 글 보러가기

 


 

우리가 매일 사용하는 이 아름다운 도구가 실제로는 어떻게 동작하고 있는지 생각해본 적이 있나요? 컴파일러는 실제로 어떻게 돌아가고 있을까요? 입맛에 맞게 컴파일 옵션을 바꾸고 싶진 않으시구요? 이 포스팅이 아마도 제법 도움이 될 거라고 생각합니다.

 

 

바벨(Babel)은 실로 굉장히 멋진 녀석입니다. 페이스북, 구글, 넷플릭스 외에도 수 백개의 다른 기업들이 이용하고 있지요. npm 월 다운로드 수는 이미 7백만을 넘어선지 오래입니다. 바벨은 ES6 뿐만 아니라 ES7, JSX, 폴리필(Polyfill), 플로우(flow) 까지 지원하고 있습니다. 만약 이 것들이 충분하지 않다고 느끼신다면 TC39(JavaScript 를 관리하고 리드하는 기술 위원회)에서 관리하고 있는 아직 정식 버젼에 포함되지도 않은 그야말로 최신 기능까지도 사용할 수 있게 해줄정도로 바벨은 굉장히 멋진 녀석입니다.

 

 

자, 이제 사설은 이쯤에서 마무리짓고, 본론으로 들어가봅시다. 

바벨은 소스 컴파일러입니다. 조금 더 자세하게 말하자면, 아래의 예를 보는게 더 나을 것 같습니다. 

ES6의 애로우 펑션(Arrow Function)입니다.

 

만약, JavaScript에서 이 함수를 사용할 일이 있다고 해보겠습니다.

(foo, bar) => foo + bar;

 

브라우저는 위 코드가 아래와 같은 형식이길 기대합니다.

"use strict"

(function(foo, bar) {
	return foo + bar;
});

 

 

위의 과정은 3 단계로 나뉘어져 있습니다.

1 단계: 파싱(Parsing)

바벨은 먼저 소스 코드를 가지고 추상적인 형태의 코드로 변환하는 과정을 수행하는데, 이를 바벨에선 파싱(Parsing)이라 표현합니다. 그리고 이 추상적인 형태의 코드는 추상구문트리(AST: Abstract Systax Tree) 라고 부릅니다.

 

 

추상구문트리(AST)는 추상화된 코드의 표현체입니다. 각각의 노드는 소스코드의 구조를 의미합니다. 이 추상구문트리는 소스코드의 좀 더 원활한 변환(transformation)을 위해 작성되는 표현체입니다.

 

 

바벨의 수 많은 플러그인 중에선 babel-parser 라고 불리는 플러그인이 있는데, 이 babel-parser 의 또 다른 이름은 바빌론(bablyon) 입니다. 추상구문트리는 바빌론에 의해 작성됩니다. 추상구문트리는 위의 애로우 펑션(Arrow Function)을 다음과 같이 표기합니다.

// AST shortened for clarity
{
    "program": {
        "body": [
            {
                "type": "ExpressionStatement",
                "expression": {
                    "type": "ArrowFunctionExpression",
                    "params": [
                        {
                            "type": "Identifier",
                            "name": "foo"
                        },
                        {
                            "type": "Identifier",
                            "name": "bar"
                        }
                    ],
                    "body": {
                        "type": "BinaryExpression",
                        "left": {
                            "type": "Identifier",
                            "name": "foo"
                        },
                        "operator": "+",
                        "right": {
                            "type": "Identifier",
                            "name": "bar"
                        }
                    }
                }
            }
        ]
    }
}

 

추상구문트리에서 볼 수 있듯이, 이 트리는 소스코드의 각 구문과 각 코드간의 관계를 모두 서술하고 있습니다. 추상구문트리는 아래의 사이트에서 좀 더 이리저리 자유롭게 바꿔가며 실험해보실 수 있습니다.

 

>[추상구문트리 변환 사이트 바로가기]

 

 


[재미로 보는 내용]

바벨(Babel)과 바빌론(Bablyon)은 모두 구약성경의 내용과 깊은 연관이 있습니다. 사실 사전에는, 바벨과 바빌론은 모두 고대 바벨로니아의 도시를 뜻하는 용어라고 표시되어 있습니다. 단지 영어로 표기하면 Bablyon, 히브리어로 표기한다면 Bável 으로 표기됩니다. 사실 우리는 바벨과 바빌론보다는 바벨탑을 조금 더 친숙하게 느끼고 있습니다. 바벨탑은 신에게 닿기 위해 높이 쌓아올린 탑이라고 알려져 있죠. 

 

창세기 11장 4절에는 이런 구문이 있습니다.

또 사람들은 논의하였다. "어서 도시를 세우고 그 가운데 꼭대기가 하늘에 닿게 탑을 쌓아 우리 이름을 날려 사방으로 흩어지지 않도록 하자."

... (중략)

주님께서 온 세상의 말을 거기에서 뒤섞어놓아 사람들을 온 땅에 흩으셨다고 해서 그 도시의 이름을 바벨이라고 불렀다.

 

JavaScript 의 브라우저들이 저마다 각각 다른 호환성을 보장하는 것이 싫어 탑을 높게 쌓아올려(BabelJS) 호환성을 하나로 묶고 싶었던 마음에서였을까요? BabelJS 의 플러그인들에서 보여지는 네이밍들은 구약성경의 바벨과 어딘가 모르게 연관되는 듯한 느낌마저 주는듯 합니다.


 

2단계: 변환(Transformation)

이 단계가 바로 마술이 펼쳐지는 곳입니다. 바벨은 1단계에서 파싱된 추상구문트리를 받아와 각 브라우저의 환경에 맞는 결과로 변환하는 작업을 수행합니다.

 

 

바벨의 플러그인 중 preset/plugin 에 의해 처리되는 곳이기도 합니다. preset 은 특별한 건 없고 plugin 들을 모아놓은 배열입니다. 단지 plugin 을 전부 하나씩 수동으로 등록할 수고를 덜어줄 뿐입니다. 바벨에서 지원하고 있는 preset/plugin 을 사용해도 좋고, 따로 입맛에 맞게 새로 만들어서 사용해도 상관없습니다.

 

 

이 플러그인들은 babel-traverse 를 이용해 원본 추상구문트리를 가로질러가는(traverse) 과정 속에서 각 부분들이 어떻게 바뀌고 어떻게 정의되어야하는지를 기록합니다.

 

 

1단계에서 생성된 애로우 펑션(Arrow Function) 에 대한 추상구문트리는 2단계에 접어들게되면서 babel-traverse 에 의해 재작성됩니다. 이 과정에서 바로 위에서 설명한 것처럼 브라우저 호환 환경에 맞게 적절한 형태로 변환될 수 있도록 안내 가이드같은 것들이 적히는 변형(transformation) 과정을 거치게되고, 이렇게 일련의 변형을 마치면 새로운 추상구문트리로 바뀌게 됩니다. (이 과정은 바벨의 플러그인 코드가 관여합니다)

 

아래는 새롭게 탄생한 추상구문트리입니다. 비슷해보이겠지만, 1단계에서 만들어진 추상구문트리와는 다른 점이 있습니다.

// AST shortened for clarity
{
    "program": {
        "type": "Program",
        "body": [
            {
                "type": "ExpressionStatement",
                "expression": {
                    "type": "Literal",
                    "value": "use strict"
                }
            },
            {
                "type": "ExpressionStatement",
                "expression": {
                    "type": "FunctionExpression",
                    "async": false,
                    "params": [
                        {
                            "type": "Identifier",
                            "name": "foo"
                        },
                        {
                            "type": "Identifier",
                            "name": "bar"
                        }
                    ],
                    "body": {
                        "type": "BlockStatement",
                        "body": [
                            {
                                "type": "ReturnStatement",
                                "argument": {
                                    "type": "BinaryExpression",
                                    "left": {
                                        "type": "Identifier",
                                        "name": "foo"
                                    },
                                    "operator": "+",
                                    "right": {
                                        "type": "Identifier",
                                        "name": "bar"
                                    }
                                }
                            }
                        ]
                    },
                    "parenthesizedExpression": true
                }
            }
        ]
    }
}

 

애로우 펑션(Arrow Function) 플러그인 코드에 대해 좀 더 살펴보실 분들은 링크를 클릭하시기 바랍니다. 그리고 바벨 플러그인 핸드북 가이드 역시 추상구문트리에 대해 좀 더 자세히 이해할 수 있도록 도와줄 것입니다.

 

 

3단계: 코드 생성(Generation)

3단계에서는 2단계에서 생성된 새로운 추상구문트리를 바탕으로 실제 브라우저 환경에 맞는 소스코드로 변환하는 과정이 이뤄집니다. 2단계에서 만들어지는 추상구문트리는 각 브라우저에서 코드 구문이 어떻게 바뀌어야하는지에 대한 내용을 기술한다고 설명했습니다. 이렇게 기술만하는 단계가 2단계라면, 실제로 이 내용을 바탕으로 코드를 생성하는 곳이 3단계입니다.

 

"use strict";
(function (foo, bar) {
  return foo + bar;
});

 

이 과정은 babel-generator 에 의해 이뤄집니다.

 

 

결국, 바벨의 동작 과정에 대한 전체 개요는 아래와 같이 정리할 수 있습니다.

 

 

 

결론

위에서 설명한 이 모든 과정이 바벨이 실제로 우리가 모르게 동작하고 있었던 과정입니다. 이 글이 모든 것을 설명하고 있지는 않습니다. 더 자세한 내용을 보고 싶다면 이 문서를 참고해보는 것도 도움이 될 겁니다.

 

 

번역 한 줄 후기

최근 바벨에 대한 기초가 너무 부족하다고 느꼈습니다. 웹팩으로 환경설정을 할 때도 뭐를 설치해야하고 버젼마다 뭐가 또 다르고.. 너무 복잡하고 끝도 없는 느낌이라 감도 안왔었습니다. 그래서 공식문서도 읽을 수 있을만큼 읽어보려고하고 동영상들도 많이 찾아보려고 했지만 전부 사용 방법에 대해서만 알려줄 뿐이었습니다.

 

 

그래서 이 글은 너무나도 저한테 한 줄기 빛과 같았습니다. 물론 전체를 자세히 다루진 않았다고 확신합니다. 그 것은 이제 각자의 몫이겠죠. 그러나 적어도 숲에 대한 개요를 요약해놓은 글이 있다는 것만으로도 충분히 번역할만한 가치가 있다고 생각했고, 시간을 들여서 기록으로 남기게 되었습니다.

 

 

좋은 글을 남겨준 원작자에게 감사를 표합니다.

 

2019/03/31 - [Web Tech/React] - React hooks (리액트 훅) 사용 방법 시리즈(1) - useState

2019/03/14 - [Web Tech/Git] - Git을 사용할 때 꼭 알아두어야 할 개념들

2019/03/31 - [Web Tech/JavaScript] - JavaScript(jQuery) - position sticky IE polyfill(폴리필) 방법

+ Recent posts