ivory's Log
그게 무엇이라도 항상 쉬운 일이다.

ivory's DevLog

Babel

ivorycode 2020. 10. 18. 11:42
반응형

이 글은 Babel에 대해 적은 글입니다. 이 포스팅을 포함해 게시된 모든 포스팅의 큰 주제는 순차적인 흐름을 지키지 않습니다. 혼란에 주의해 주세요!


ES6, Babel, webpack 이미지 출처: www.google.com

 

Babel이란?

JavaScript Compiler

Babel 공식문서에 따르면, Babel은 JavaScript(이하 JS) Complier(이하 컴파일러)라고 정의되어 있다. 다시 정리하면, Babel은 JS의 컴파일러로서, 최신 JS 문법을 이전 단계의 JS문법으로 변환 가능하게 해주는 도구다. 

 

Babel, 왜 필요할까???

끊임없이 발전하는 브라우저와 ES6

몇 년 전만 해도 가장 인기가 많고 많은 개발자들이 선호했던 jQuery가 빠르게 잊혔던 것처럼 프론트엔드 개발언어(다른 개발 분야도 마찬가지다.)는 빠르게 발전한다. 그와 동시에 브라우저 역시 발전하게 되고, 최신 문법의 등장과 적용으로 다양하고 유용한 기술이 등장한다. 

 

2010-2020 전 세계 브라우저 점유율 이미지 출처: PRESSCAT

이렇게 새롭고 다양한 기술을 선보이는 에버그린 브라우저들을 전 세계 사람들이 사용한다면 개발자 입장에선 가장 편리할 것이다. 하지만, 사람들이 선호하는 부분이 다르듯이 브라우저 점유율에서도 다양한 추이를 보여준다. 또한 각 브라우저마다 사용하는 개발 언어가 달라서 코드가 일관적이지 못하는 경우가 발생한다. 이 뿐만이 아니다. 우리가 위의 점유율 그래프에서 주목해야 할 부분은 바로 구형 브라우저의 점유율이다. 대표적으로 Internet Explorer(이하 IE)는 2020년 3월 기준 약 3.47%이다. 대한민국만 한정하면 13.7%인데, IE의 가장 큰 문제는 새롭게 도입된 ES6 사양을 약 11%밖에 지원하지 못한다. 쉽게 말해, IE를 포함하여 구형 브라우저들은 기본적으로 ES6 이상의 최신 사양을 사용하지 못한다는 의미다.

 

크로스 브라우징 이슈와 Babel

크로스 브라우징이란 웹페이지를 개발할 때, 모든 브라우저에서 호환할 수 있도록 작업하는 것을 말한다. 여기서 우리가 쉽게 오해하는 부분이 있는데, 모든 브라우저에서 동일하게 보이게 한다고 이해하는 경우가 있다. 이는 굉장히 잘못 이해하고 있는 것이다. 아래에 Mozila 한국 커뮤니티에서 발췌한 'CSS Browsing 가이드'(저자 '윤석찬'님)를 첨부할 테니 반드시 참고하자!!!

 

웹 페이지의 상호 호환성(Cross Browsing) 구축에 대해 이야기하면 기본적인 오해가 있다. 그것은 바로 이것이 모든 웹 브라우저에서 100% 똑같이 보이도록 만드는 것이라는 생각이다. 작게는 1990년대 후반 Netscape사와 Microsoft사의 Browser War 기간 동안 일어난 브라우저의 비호환성을 억지로 끼워 맞추려는 기법 정도로 치부되는 것이다.

Cross Browsing
이란 적어도 표준 웹 기술을 채용하여 다른 기종 혹은 플랫폼에 따라 달리 구현되는 기술을 비슷하게 만듦과 동시에 어느 한쪽에 최적화되어 치우지지 않도록 공통 요소를 사용하여 웹 페이지를 제작하는 기법을 말하는 것이다. 또한, 지원할 수 없는 다른 웹 브라우저를 위한 장치를 구현하여 모든 웹 브라우저 사용자가 방문했을 때 정보로서의 소외감을 느끼지 않도록 하는 방법론적 가이드를 의미하는 것이다.

참고 자료
'mulder21c'님 블로그
http://mulder21c.github.io/2019/01/30/what-is-cross-browsing/
Mozila 한국 커뮤니티 웹 개발자 가이드
www.mozilla.or.kr/docs/web-developer/standard/crossbrowsing.pdf

 

반복해서 말하지만 브라우저마다 사용하는 개발언어가 다르고, IE는 ES6이상의 최신 문법을 적용하지 못한다고 했다. IE 뿐만 아니라 얼마 전까지만 해도 Safari 브라우저도 특정 메서드를 사용할 수 없었다. 이렇게 발생하는 크로스 브라우징 이슈는 코드의 일관성을 해치며 개발자를 피곤하게 하였는데 이런 혼란스러운 상황을 Babel이 바로 잡아 주었다. Babel은 TypeScript, JSX문법 등을 포함해 ES6이상의 버전으로 작성된 문법들을 모든 브라우저에서 호환할 수 있게 해 주었다.

 

🤔ECMAScript(ES)

 

ECMAScript란, Ecma International이 ECMA-262 기술 규격에 따라 정의하고 있는 표준화된 스크립트 프로그래밍 언어를 말한다. - 위키백과 -

 

즉, JS를 표준화하기 위해 만들어진 문법이라고 생각하면 된다. ES는 버전에 따라 특징이 있는데, 대표적인 버전의 특징만 살펴보고 넘어가도록 하자.

 

ES3 (1999)
- 우리가 흔히 말하는 JavaScript. 스코프, 호이스팅, 모듈화 미지원, 프로토타입, 클로져 등 기본적인 특징을 가졌으며, 대부분의 브라우저에서 지원한다(IE 8).

ES5 (2009)
- ES4는 그 내용이 너무 급변되어 거절되고, 점진적인 개선을 위해 ES5가 등장하였고 IE 9부터 본격적인 지원을 하게 되었다. ES5-shim을 사용하여 하위 버전에도 기능 지원이 가능하다. 이 버전에선 배열, 객체, strict모드, JSON을 지원하였으며 이밖에 bind() 메소드, Lexical Environment 등을 지원하였다.

ES2015 (ES6)
- ES6 Harmony라고도 불리며, 여러 가지 기능이 추가되었으며 이건 포스팅에 언급했었던 let, const 변수 선언방식이 이 버전에서 추가되었다. 아래의 목록은 ES6에서 추가된 기능들이다.
  1. let, const 키워드 추가
  2. arrow 문법 지원(화살표 함수)
  3. 비구조화 할당
  4. 템플릿 리터럴
  5. Promise 도입
ES 2015+, ES 2016 (ES7), ECMA 2017 (ES8)
- ES6 이후에 제곱 연산자, includes() 메소드, asyns - await 도입 등 여러 가지 기능이 추가되었고, 지금 이 순간에도 ES의 발전은 현재 진행 중이다.

 

기본 동작 과정

특정 버전의 ES코드를 하위 버전의 ES코드로 변환해주는 것을 트랜스파일이라고 한다. 우선 예시를 살펴보자.

 

트랜스파일의 예시. 이미지 출처: 내가 직접 작성한 코드

보다시피 ES6에서 새롭게 등장한 화살표 함수를 아래 function 키워드를 이용한 일반 함수로 트랜스파일 하여 출력해준다. 그런데 Babel은 어떤 방식으로 코드를 변환해 주는 것일까? 우선 Babel의 동작 과정은 다음과 같다.

 

🤔그전에 Babel부터 설치해보자!

 

1. 프로젝트 폴더 생성하기
mkdir
project-name

2. 프로젝트 폴더로 이동
cd
project-name

3. package.json 생성하기
npm init -y

4. Babel 설치하기
npm install --save-dev @babel/core @babel/cli

 

동작 과정

1. 파싱(Parsing)

파싱은 예전에 '브라우저 렌더링 과정'을 공부할 때 만나본 용어라 익숙할 텐데, 첫 번째 파싱 단계에서 코드를 읽고 추상 구문 트리(AST)로 변환한다.

 

2. 변환(Transform)

코드 파싱을 통해 변환한 추상 구문 트리(AST)를 설정에 맞는 코드로 변환하는 단계다.

 

3. 출력(Print)

변환된 코드를 출력하는 단계다.

 

새로운 test.js 파일을 생성하고 아래의 코드를 적어봤다. 내 시나리오대로면 화살표 함수에서 일반 함수로 트랜스파일이 되어야 한다. 코드를 적고 npx babel test.js를 터미널에 입력해 보았다.

 

코드가 변환되지 않는다... 이미지 출처: 내가 확인한 코드

출력을 단계를 마치면, Babel 작업이 완료된다. 그런데 Babel을 설치해서 작업한 후, 결과물을 확인해보니 처음 쓴 코드가 트랜스파일 되지 않고 그대로 출력된다. 이유가 무엇일까?

 

플러그인

변환을 담당하는 플러그인

우선 Babel은 세 가지 동작 과정 중, 파싱과 출력만 담당하고 플러그인이 남은 변환작업을 하게 된다. 플러그인은 추상 구문 트리(AST)를 실제로 동작하는 코드로 변환시켜준다. Babel에는 수많은 플러그인이 존재하여 하나하나 구성할 필요 없이 필요한 플러그인을 설치하여 사용하면 된다. Babel 공식문서에서 찾아볼 수 있는 예제 코드를 통해 플러그인을 좀 더 파헤쳐보자.

 

커스텀 플러그인

공식문서 예제 코드를 복사 붙여 넣기 하여, testPlugin.js라고 생성하였다.

 

위의 플러그인 코드는 visitor라는 객체를 반환하고 있다. 객체 안을 더 살펴보면 Identifier()라는 메소드가 보이는데, 바로 Babel이 visitor라는 객체를 파싱 하여 만든 추상 구문 트리(AST)를 접근할 수 있는 메소드를 제공하는 것이다. 메소드 안을 살펴보면, name이라는 변수에 path.node.name 이 바로 Babel이 만든 AST 노드이며 split(), reverse(), join()을 통해 코드 문자열을 역순으로 변환하는 것이다. 이렇게 만든 플러그인을 이제 적용시켜보자.

 

적용시켜보기

npx babel test.js --plugins ./testPlugin.js

 

생각했던 결과가 아니다. 이미지 출처: 내가 확인한 결과창

예상과는 아주 다른 결과였다. test라는 변수 이름이 반전되었다. 왜 이렇게 출력된 건지 생각해봤는데, 아까 만든 커스텀 플러그인 부분이 문제였다. 커스텀 플러그인의 Identifier() 메소드에 split(), reverse(), join()를 기억하는가? 이 메소드들을 차례로 진행하다 보니, test라는 문자열 값을 역순으로 반환한 것이다. 일단 바벨이 어떤 것인지 충분히 파악은 되었지만, 화살표 함수를 일반 함수로 출력하려면 어떻게 해야 할지 고민해봤다. '혹시 메소드를 직접 작성해야 하나?'라는 끔찍한 생각도 잠깐 해봤는데, 서두에 언급했다시피 Babel에는 수많은 플러그인이 존재한다. 화살표 함수를 일반 함수로 변환해주는 플러그인 역시 존재했다.

 

플러그인 설치하고 사용해보기

잠깐 돌아가서 테스트 코드를 살펴보니, 화살표 함수 말고 변수 선언 방식이 const도 ES6에서 새롭게 적용된 문법이기에 하위 버전 ES로 변환해야 한다.(크로스 브라우징을 간과하고 있었다. 참고로 이번 포스팅에서 var, let, const에서 다루지 않습니다.) 즉, 설치해야 할 플러그인은 최소 2가지다. 다시 돌아와서, 먼저 설치한 플러그인은 다음과 같다.

 

npm install --save-dev @babel/plugin-transform-block-scoping

 

Babel 공식문서를 찾아보니 위의 플러그인을 설치하라고 안내해 주었다. 공식문서에 따르면, const나 let처럼 block-scoping을 따르는 키워드 혹은 예약어를 var로 변경해주는 플러그인이라고 한다.(테스트 브라우저가 Chrome이었다.) 이 플러그인을 설치하게 되면 크로스 브라우징 이슈를 예방할 수 있다. 플러그인 설치가 끝나면, 다음 코드를 쳐서 화살표 함수도 처리해보자.

 

npm install --save-dev @babel/plugin-transform-arrow-functions

 

이렇게 두 플러그인 설치를 끝냈고, 설치한 내용을 적용하기 위해 아래와 같은 코드를 입력 후 결과를 확인해보자.

 

설치한 플러그인을 적용시켜보자!!
npx babel test.js --plugins @babel/plugin-transform-arrow-functions --plugins @babel/plugin-transform-block-scoping

 

정상적으로 트랜스파일되었다. 이미지 출처: 내가 확인한 결과창

플러그인 적용을 통해 Babel 실행을 성공시켰다! 이렇게 플러그인을 적용해야 Babel이 정상적으로 실행할 수 있다는 사실을 깨달았다. 그런데 여기서 한 가지 Tip이 있다면, 프로젝트 경로에 플러그인 설정 파일을 따로 분리하여 관리하는 게 좋다. 지금은 적용한 플러그인이 단 2개였지만 이것이 여러 개가 되었을 때, 작성해야 할 실행문이 엄청 복잡해지고 길어질 것이다. 이럴 땐, 설정 파일을 따로 분리하여 플러그인을 관리해준다.

 

babel.config.js

babel.config.js 이미지 출처: 예제를 통해 적용해본 코드

이렇게 babel.config.js 파일을 만들어서 플러그인 객체 안에 적용시킬 플러그인을 모아 두고, 실행문을 통해 플러그인을 적용시킨다.

 

프로젝트 파일 이름을 실행문에 입력하면 된다!!
npx
babel test.js
**이번 포스팅에서 테스트할 때 자주 사용하게 될 실행문이니 참고하자!!

 

프리셋

프리셋이란, 여러 가지 특정한 목적에 필요한 플러그인들을 모아둔 것을 말한다. 일일이 필요한 플러그인을 찾아서 Babel에 적용하는 정말 귀찮은 일이다. 그래서 Babel은 이 귀찮은 일을 개선하고자 프리셋을 제공하게 되었고, 개발자들은 이것을 통해 쉽고 빠른 Babel 환경설정을 할 수 있게 되었다.

 

프리셋 패키지 다운로드 방법
npm install
@babel/preset-env

 

설치가 완료되면 babel.config.js에 프리셋을 적용시킨다.

 

이미지 출처: 내가 확인한 코드

이렇게 하면, 우리는 프리셋까지 적용시킨 것이다. 아래의 인용문은 대표적으로 사용되는 프리셋 목록을 메모해둔 것이다. 

 

env 프리셋
현재 지원 가능한 가장 최신 버전의 프리셋이 사용 가능하며, 추가로 프로젝트의 지원 브라우저를 기반으로 폴리필과 플러그인을 관리할 수 있는 옵션들을 사용할 수 있다.
@babel/preset-env

env Flow
flow를 변환하기 위해 사용하는 프리셋
@babel/preset-flow

env React
React를 변환하기 위해 사용하는 프리셋
@babel/preset-react

env TypeScript
TypeScript를 변환하기 위해 사용하는 프리셋
@babel/preset-typescript

 

🤔env 프리셋으로 옵션을 추가해보자.

env 프리셋을 사용하면, 폴리필과 플러그인을 관리할 수 있는 옵션을 사용할 수 있다고 했다. 폴리필에 대해선 따로 정리해보고, 먼저 다른 브라우저에 적용하는 옵션을 추가해보자.

 

env 프리셋 적용하기 이미지 출처: 내가 작성한 코드

 

폴리필

프리셋까지 알아보았다. 이제 우리는 Babel에 대해서 완벽히(?) 알아봤다고 말하고 싶지만, 프리셋에서 더 알아봐야 할 개념이 남았다. 잠시 env 프리셋에 옵션을 적용한 코드를 보자. 저렇게 프리셋 옵션을 설정하면 Chrome 브라우저에선 잘 작동하지만, IE에선 잘 작동하지 못한다.(IE는 참 까다로운 친구다... 이쯤 되면 눈치 있게 최신 브라우저를 사용하자.) 위의 옵션을 통해 변환을 해줬음에도 불구하고, IE를 포함해 더 낮은 버전의 브라우저에서 변환되지 않은 구문이 존재하기 때문이다. 아래 이미지 예시를 참고하자.

 

Chrome 브라우저에선 Promise를 자동완성 해주었지만,
IE에선 Promise를 찾을 수 없었다. 이미지 출처: 내가 직접 캡쳐한 화면

 

IE에서 Promise를 인식하지 못한다는 것을 알았다. 이렇게 Babel에서 변환하지 못하는 것들을 작동시키기 위해 '폴리필'이라 부르는 코드 조각들을 추가하여 변환작업을 해결한다. 이전에 작성한 babel.config.js 파일에서 폴리필 옵션을 추가해보자. 일단 env 프리셋만 적용한 상태라고 가정하자.

 

test.js 파일. 기존에 있던 내용은 지우고, 새로운 내용을 적어주자.

 

천천히 따라해보자. 이미지 출처: 내가 작성한 코드

이 상태에서 useBuiltIns라는 옵션을 추가하자. 이 옵션은 어떤 방식으로 폴리필을 사용할지 설정하는 옵션이다. 이 옵션은 세 가지 선택 옵션을 가지고 있다.

 

false
기본값이다.

entry
지정된 환경에서 필요로 하는 폴리필 모두를 내장하는 설정.

usage
실제로 사용되는 코드들만 고려하여 적용하는 옵션. 이 옵션을 사용하면 사용되는 폴리필만 각 파일의 최상단에 포함시켜 적용된다.

 

usage를 사용하여 적용시켜보자.

 

폴리필 적용하기 이미지 출처: 내가 작성한 코드

 

이렇게 하면, 폴리필이 상단부에 내장하게 되며 IE에서도 잘 작동하게 된다. 출력 결과는 아래와 같다.

 

출력결과 이미지 출처: 내가 확인한 코드

아까 폴리필을 적용할 때 작성한 corejs 패키지를 통해 Promise import 하는 구문이 파일 상단에 추가된 것을 확인할 수 있다.

 

정리

Babel이란?

JS의 컴파일러로서, 최신 JS 문법을 이전 단계의 JS문법으로 변환 가능하게 해주는 도구다. Babel을 통해 다양한 브라우저를 호환하고 웹, 앱을 개발할 수 있다.

 

플러그인이란?

Babel의 동작 과정은 파싱, 변환, 출력 순서로 이루어지는데, Babel 코어는 파싱과 출력만 담당한다. 실질적인 변환 작업은 플러그인이 처리한다. 플러그인은 추상 구문 트리(AST)를 실제로 동작하는 코드로 변환시켜준다.

 

필요와 목적에 따라 플러그인을 추가하여 환경 설정을 한다. 사용하는 플러그인이 여러 개가 되었을 때, 작성해야 할 실행문이 엄청 복잡해지고 길어지는 것을 방지하고자, 설정 파일을 따로 분리하여 플러그인을 관리해준다.

 

프리셋이란?

플러그인을 모아둔 세트를 프리셋이라고 한다. 참고로 ES+ 환경에선 env 프리셋을 사용하며, 특정 목적에 따른 프리셋이 존재한다. 프리셋을 통해 쉽고 빠른 Babel환경을 설정할 수 있다. 

 

폴리필이란?

Babel에서 변환하지 못한 코드는 폴리필이라고 부르며, 이 폴리필 코드 조각들을 불러와 결과물에 로딩하여 해결한다.

 

useBuiltIns라는 옵션은 어떤 방식으로 폴리필을 사용할지 설정하는 옵션이며, 세 가지 선택 옵션을 가지고 있다.

반응형

'ivory's DevLog' 카테고리의 다른 글

Git 폭파범이 정리한 Git에 관한 기본 개념  (0) 2020.11.04
Webpack  (0) 2020.10.28
[JavaScript] - this  (0) 2020.10.04
CORS란?  (2) 2020.10.02
SSR(Sever Side Rendering)과 CSR(Client Side Rendering)  (1) 2020.09.30