babel
babel 은 자바스크립트 컴파일러다
babel 을 사용하는 이유
최신 버전의 자바스크립트(ES6+)로 작업하고 배포할 때는 호환성을 위해 예전 방식의 코드로 배포하기 위해 사용한다.
IE 11 버전 조차도 지원 안되는 ES6+ 문법들이 너무 많다.
babel 을 사용하기 위해선 NodeJS를 설치해야된다.
NodeJS를 설치했다는 가정 하에 babel 사용법을 진행하도록 하겠다.
babel Tutorial
1. 초기화
프로젝트 폴더에서 초기화를 해주자.
npm init -y
위 명령어를 입력하고나면 package.json
파일이 생성된다.
npm init
만 입력해도 package.json
파일이 생성되는데, -y
옵션을 붙여준 이유?
npm init
만 입력하면 package.json
에 들어갈 내용을 계속 물어본다.
그게 싫다면 -y
옵션을 같이 입력하면 된다.
2. babel-cli 설치
npm install --save-dev babel-cli
// 또는
npm i -D babel-cli
-g 옵션으로 글로벌로 설치하거나, 각 프로젝트마다 다른 babel 버전을 사용하는 경우 –save-dev(-D) 옵션으로 설치한다.
보통 -g (글로벌) 옵션보단 로컬로 설치하는 게 좋다.
작업자가 다 다르므로.
babel-cli
란 babel을 cmd 창(터미널)에서 사용할 수 있게 해주는 거라고 보면 된다.
3. npm script로 자동화
package.json 파일을 수정한다.
"scripts" : {
"build": "babel ./public/src -d ./public/lib -w"
},
“build”: “babel ./public/src -d ./public/lib -w” 의 뜻.
명령어 이름은 build
이고 public 폴더 안에 src 폴더에 있는 스크립트를 자동으로 변환해서 public lib 폴더에 생성해줘. 라는 뜻이다.
실행은 아래와 같이한다.
npm run build
./public/src
경로에 있는 원본 코드를 변환해서 ./public/lib
안에 생성한다는 뜻이다.
이 상태에서 그냥 npm run build
를 실행하면, babel은 그냥 src 안의 파일과 같은 파일을 lib 폴더에 생성할 뿐이다.
**변환작업을 위해서는 변환 옵션을 설정해야 하는데, 그때 사용하는 파일이 아래의 .babelrc
이다.
4. .babelrc 파일로 옵션 설정
touch .babelrc
.babelrc
를 생성하고 아래의 기본 구성 내용을 입력한다.
플러그인을 사용하지 않는다면 plugins는 생략해도 무방하다.
{
"presets": [],
"plugins": []
}
우리는 ECMA 2015 preset을 사용하기 위해 해당 preset
을 설치해야된다.
npm install --save-dev babel-preset-es2015
// 또는
npm i -D babel-preset-es2015
그리고 .babelrc
파일에 우리가 사용할 프리셋(설치한 프리셋)을 추가한다.
{
"presets": ["es2015"]
}
여기까지하면 기본적으로 잘 동작한다.
5. minify
lib 에 압축된 버전의 파일을 생성하기 위해 minify
패키지를 설치하자.
npm install --save-dev babel-preset-minify
// 또는
npm i -D babel-preset-minify
.babelrc
파일에도 추가해주자.
{
"presets": ["es2015", "minify"]
}
npm run build
수행 시 “Couldn’t find intersection” 에러가 날 경우,
.babelrc
파일을 아래와 같이 바꿔 주자.
{
"presets": [
"es2015",
["minify", {builtIns: false, evaluate: false, mangle: false}]
]
}
중요
npm run build
를 통해 babel 을 실행하면, cmd(터미널)에서 ctrl + c
로 종료하지 않는 이상 계속 실시간 수정사항을 반영해준다.
이런식으로 느낌표 5개로 수정 후 브라우저 새로고침 후 아래와 같이 콘솔 창에서 Mover.sayHello()
을 실행해보면 반영된 걸 알 수 있다.
예시
<!DOCTYPE html>
<html>
<head>
<title>Babel Test</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.mover {
position: absolute;
left: 0;
top: 0;
width: 50px;
height: 50px;
border-radius: 50%;
transition: 1s;
}
</style>
</head>
<body>
<script src="src/mover.js"></script>
<script>
const m1 = new Mover('red');
const m2 = new Mover('green');
const m3 = new Mover('blue');
const f1 = new FastMover('orange');
</script>
</body>
</html>
현재 위의 html 파일 예시에선 src/mover.js
파일을 연동해 사용하고 있다.
아래 mover.js
내용을 보자.
class Mover {
constructor(color) {
this.color = color;
this.elem = document.createElement('div');
this.elem.classList.add('mover', `mover-${color}`);
this.elem.style.background = color;
this.elem.addEventListener('click', this.go);
document.body.appendChild(this.elem);
this.go();
}
go(e) {
let elem;
if (e) {
elem = e.currentTarget;
} else {
elem = this.elem;
}
const xPos = Math.random() * innerWidth * 0.9;
const yPos = Math.random() * innerHeight * 0.9;
elem.style.transform = `translate3d(${xPos}px, ${yPos}px, 0)`;
}
get getColor() {
return this.color;
}
set setColor(newColor) {
this.color = newColor;
this.elem.style.background = newColor;
}
static sayHello() {
console.log(`My name is Mover!`);
}
}
class FastMover extends Mover {
constructor(color) {
super(color);
this.elem.style.transitionDuration = '0.3s';
}
}
위의 코드를 보면 class
를 사용한 것을 볼 수 있다.
새 버전 자바스크립트를 사용한 것이다.
위의 새버전 자바스크립트 코드가 babel을 통해 자동으로 이전 버전의 자바스크립트 코드로 변환된다.
위 코드 해석
- get / set 키워드 활용
- extends 키워드로 transitionDuration 다른 값으로 변화
CLASS의 get / set 키워드
get
구문은 객체의 프로퍼티를 그 프로퍼티를 가져올 때 호출되는 함수로 바인딩합니다.
set
syntax 는 어떤 객체의 속성에 이 속성을 설정하려고 할 때 호출되는 함수를 바인드합니다.
getter / setter 의 의미
getter / setter 의 의미와 이를 사용하는 이유에 대해 알아보도록 하겠다.
getter / setter 을 언급할 때, 대부분 private 개념이 따라오게 된다.
책이나 글에서 볼 수 있는 극단적인 예는 아래와 같다.
private 변수를 지정한 후, 이 변수에 접근하기 위해 getter, setter를 이용한다.
맞는 말이다.
하지만 이해하기 힘들다.
다음 예제를 통해 확인해보자.
- name 을 저장할 때는 정확한 값일 때 저장한다.
- name 을 가져올 때는 대문자로 된 이름을 얻을 수 있다.
위 특징을 가지는 Person 클래스를 만들어보자.
class Person {
constructor(name, age) {
this._name = name;
this.age = age;
}
get name() {
return this._name.toUpperCase();
}
set name(newName){
if(newName){ this._name = newName; }
}
}
let man = new Person('John', 10);
console.log(man.name, man.age); // JOHN 10
man.name = 'John Park';
man.age = 20;
console.log(man.name, man.age); // JOHN PARK 20
이름이라는 변수는 실제 _name
이 된다.
코드를 보면 실제 데이터인 _name
에 직접 접근하지 않으면서, 정의된 오퍼레이션을 통해서만 접근하고 있다.
또한, 이름을 얻을 때 사용자는 단순히 이름을 얻을 뿐이다.
대문자로 가공하는 과정은 내부에서 일어날 뿐이다.
즉 단순히 사용자는 이름을 얻고자 할뿐, 얻는 과정에 있는 내부적인 일은 신경쓰지 않는다.
이러한 원리가 캡슐화의 이점인 정보 은닉이 된다.
메소드로 접근하는 것처럼 보이지 않기 때문에 외관상에도 좋다.
조금 더 나아가, 다른 예제를 통해 getter, setter를 통한 일관성 유지에 관한 예를 들어보겠다.
getter setter 일관성 유지
start, end, duration 3개의 변수가 있는 클래스가 존재한다.
- start : 시작하는 시간
- end : 끝나는 시간
- duration : 지속되는 시간
먼저 getter, setter를 쓰지 않고 변수에 직접 접근하는 예를 보자.
class Time {
constructor(start, end) {
this._start = start;
this._end = end;
this._duration = end - start;
}
}
const time = new Time(0, 20); // _start : 0 ; _end : 20 ; _duration : 20
time._start = 5; // _start : 5; _end: 20; _duration: 20
time._duration -= 5; // _start : 5; _end: 20; _duration: 15
console.log(time._start) // 5
start 변수의 값을 수정할 때, 사실상 duration 변수도 수정되어야하기 때문에 위와 같은 코드가 나올 것이다.
이 경우 변수의 직접 접근을 통해 보호되지 못한다.
그 결과 누구나 접근하여 변경할 수 있기에 start 에 따른 duration 값이 맞지 않는 결과가 초래될 수 있다.
즉 변수 관계에 있어서 일관성을 깨트리게 된다.
getter, setter를 사용한 예를 보자.
class Time {
constructor (start, end) {
this._start = start;
this._end = end;
this._duration = end - start;
}
setStart (newStart) {
this._start = newStart;
this._duration = this._end - this._start;
}
getStart() {
return this._start;
}
}
const time = new Time(0, 20);
time.setStart(5);
console.log(time.getStart()); // 5
getter, setter를 통해 접근함으로써 변수를 보호할 수 있다.
또한 setter를 통해 start 와 duration 을 설정함으로써, 위에서 발생한 일관성 문제를 해결할 수 있다.
자바스크립트만의 getter, setter를 사용해보자.
변수에 직접 접근하는 것처럼 보이지만 내부적으로는 그렇지 않다.
위 예시보다 일관성 유지에 맞는 코드 및 외관상으로도 보기 좋은 코드가 만들어진다.
class Time {
constructor (start, end) {
this._start = start;
this._end = end;
this._duration = end - start;
}
set start (newStart) {
this._start = newStart;
this.duration = this._end - this._start;
}
get start () {
return this._start;
}
}
const time = new Time(0, 20);
time.start = 5;
console.log(time.start); // 5
getter, setter는 위의 예들처럼 특정 목적이 있는 경우 설정한다.
무조건적으로 쓴다면, 단순히 불필요한 코드가 늘어나는 것이다.