Skip to content

Instantly share code, notes, and snippets.

@marocchino
Last active July 19, 2022 14:25
Star You must be signed in to star a gist
Save marocchino/841e2ff62f59f420f9d9 to your computer and use it in GitHub Desktop.
ES6시대의 JavaScript

ES6시대의 JavaScript

안녕하세요. 사원사업부의 마루야마@h13i32maru입니다. 최근의 Web 프론트엔드의 변화는 매우 격렬해서, 조금 눈을 땐 사이에 점점 새로운 것이 나오고 있더라구요. 그런 격렬한 변화중 하나가 ES6이라는 차세대 JavaScript의 사양입니다. 이 ES6는 현재 재정중으로 집필시점에서는 Draft Rev31이 공개되어있습니다.

JavaScript는 ECMAScript(ECMA262)라는 사양을 기반으로 구현되어있습니다. 현재 모던한 Web 브라우저는 ECMAScript 5.1th Edition을 기반으로 한 JavaScript실행 엔진을 탑재하고 있습니다. 그리고 다음 버전인 ECMAScript 6th Edition이 현재 재정중으로, 약칭으로 ES6이라는 명칭이 사용되고 있습니다.

이 번엔, 다른 언어에 있고 JavaScript에도 있으면 하는 기능과, JavaScript에서 잘 나오는 패턴을 통합적으로 적을 수 있는 기능을 중심으로 소개하겠습니다.

Class

JavaScript는 “프로토타입 기반의 OOP”라 불리고 있는 것 처럼, Java나 Ruby등의 “클래스 기반의 OOP”와는 조금 다릅니다. 하지만 프로토타입 베이스의 기능을 효과적으로 사용한다는 것은 여태까지 별로 없었다고 생각합니다. 오히려 가짜 클래스 기능을 구현하던가, 클래스를 실현하기 위해 라이브러리를 사용해 프로그래밍을 적는 것이 많을 것입니다. 이것은 npm에서 class로 검색하면 많은 패키지가 나오는 것만 생각해도 알수 있다고 생각합니다. 여기서 ES6에서는 클래스 기능을 도입해서, 클래스를 간단히 취급할 수 있게 되었습니다.

// ES5
'use strict';

function User(name){
  this._name = name;
}

User.prototype = Object.create(null, {
  constructor: {
	value: User
  },

  say: {
	value: function() {
	  return 'My name is ' + this._name;
	}
  }
});

function Admin(name) {
  User.apply(this, arguments);
}

Admin.prototype = Object.create(User.prototype, {
  constructor: {
	value: Admin
  },

  say: {
	value: function() {
	  var superClassPrototype =  Object.getPrototypeOf(this.constructor.prototype);
	  return '[Administrator] ' + superClassPrototype.say.call(this);
	}
  }
});

var user = new User('Alice');
console.log(user.say()); // My name is Alice

var admin = new Admin('Bob');
console.log(admin.say()); // [Administrator] My name is Bob
// ES6
'use strict';

class User {
  constructor(name) {
	this._name = name;
  }

  say() {
	return 'My name is ' + this._name;
  }
}

class Admin extends User {
  say() {
	return '[Administrator] ' + super.say();
  }
}

var user = new User('Alice');
console.log(user.say()); // My name is Alice

var admin = new Admin('Bob');
console.log(admin.say()); // [Administrator] My name is Bob

Function Arguments

JavaScript에서 함수의 디폴트 인수나 가변길이 인수를 사용하고 싶다고 생각해도 언어에서 직접적인 방법이 없었기 때문에, ||를 사용한 마법같은 방법이나 arguments을 사용한 메타프로그래밍같은 방법을 쓰고 있었습니다. 여기서 ES6는 함수의 가 인수의 선언 방법이 강화되어, 자연스럽게 적을 수 있게 되었습니다. 이것은 나중에 프로그램을 읽을 때에, 시그니쳐만으로 보면 그 함수가 기대하는 인수를 어느정도 알 수 있게 되었다는 효과가 있습니다.

// ES5
'use strict';

function loop(func, count) {
  count = count || 3;
  for (var i = 0; i < count; i++) {
	func();
  }
}

function sum() {
  var result = 0;
  for (var i = 0; i < arguments.length; i++) {
	result += arguments[i];
  }
  return result;
}

loop(function(){ console.log('hello')}); // hello hello hello
console.log(sum(1, 2, 3, 4)); // 10
// ES6
'use strict';

function loop(func, count = 3) {
  for (var i = 0; i < count; i++) {
	func();
  }
}

function sum(...numbers) {
  return numbers.reduce(function(a, b) { return a + b; });
}

loop(function(){ console.log('hello')}); // hello hello hello
console.log(sum(1, 2, 3, 4)); // 10

실제로 이 디폴트 인수나 가변 길이 인수는 함수의 반인수부분만으로 사용하지는 않고, 변수의 대입처리전반이 강화된 것의 일부분이 됩니다. ES6에서 변수의 대입처리에 관해서는 Destructuring and parameter handling in ECMAScript 6에서 샘플을 포함한 다양한 패턴이 소개되어 있습니다.

Arrow Function

JavaScript에서는 이벤트 구동의 처리를 자주 적습니다. 예를들어 DOM이 클릭되면 뭔가 처리하거나, XHR 리퀘스트가 완료되면 뭔가 처리하는 경우입니다. 이런 처리를 JavaScript에서 구현하려면, 콜백 함수나 이벤트리스너라고 불리는 것을 대상 객체(DOM이나 XHR)에 설정합니다. 이 콜백함수를 등록하는 시점에서 this에 콜백 함수안의 억세스하고 싶은 경우도 자주 있습니다만, 여태까지는 클로져를 사용해 this를 보존하던가, Function.prototype.bind를 사용해 this를 속박하거나 했습니다. ES6에서는 Arrow Function라고 불리는 새로운 함수 정의 식이 도입되어, 이 this에 관한 번거로움을 해소하고 있습니다.

// ES5
'use strict';

var ClickCounter = {
  _count: 0,

  start: function(selector) {
	var node = document.querySelector(selector);
	node.addEventListener('click', function(evt){
	  this._count++;
	}.bind(this));
  }
};

ClickCounter.start('body');
// ES6
'use strict';

var ClickCounter = {
  _count: 0,

  start: function(selector) {
	var node = document.querySelector(selector);
	node.addEventListener('click', (evt)=>{
	  this._count++;
	});
  }
};

ClickCounter.start('body');

Promise

여태까지 XHR등의 비동기처리는 시작전에 콜백함수를 정의해서, 비동기처리가 끝나면 그 콜백함수가 호출되는 것이 일반적이었지만, 여러 콜백함수의 설정방법이 있었습니다. 예를들어, 비동기처리의 함수에 콜백 함수를 인수로 넘기거나(setTimeout이나 setInterval), 비동기처리를 행하는 객체에 콜백 함수를 등록하거나(XHRWebWorker), 비동기처리의 반환값에 콜백 함수를 등록하거나(IndexedDB)등이 있습니다. 이런 여러 방법이 있기 때문에, 사용하는 측에서는 각각의 방법을 나눠서 사용할 필요가 있습니다. 여기서 ES6에서는 Promise라는 비동기처리를 종합적으로 처리하는 방법이 언어레벨에서 재공되게 되었습니다. 사용법은, 비동기처리를 행하는 함수는 Promise를 반환값으로 반환해, 부른 쪽에서는 Promise에 콜백 함수를 등록한다는 것입니다.

// ES5
'use strict';

function sleep(callback, msec) {
  setTimeout(callback, msec);
}

sleep(function(){
  console.log('wake!')
}, 1000);
// ES6
'use strict';

function sleep(msec) {
  return new Promise(function(resolve, reject){
	setTimeout(resolve, msec);
  });
}

sleep(1000).then(function(){
  console.log('wake!');
});

또 비동기 처리에서는 예외처리가 문제가 됩니다. 단순히 try-catch로 감싸도 비동기에서 예외가 일어나면 보충할 수 없습니다. 여기서 Promise에서는 비동기 처리의 예외처리도 종합적으로 처리하는 방법이 제공되고 있습니다. 이 Promise에 관해서는 Web에서 무료로 읽을 수 있는 JavaScript Promiseの本(번역)이 무척 참고가 됩니다.

Generator

마지막으로 Generator에 관해 소개하겠습니다. 여기까지는 JavaScript에서 있지만 사용하기 힘들거나, 통일되지 않았던 것을 개선한 기능이지만, 이 Generator라는 것은 완전 새로운 개념으로 ES6에 도입되었습니다.1 Generator는 함수 처리안의 임의의 장소에서 처리를 중단/재개할 수 있는 구조를 제공하는 것입니다. 이 구조는 일반적으로 코루틴(co-rutine)이라 불립니다. 코루틴을 사용하면 무한 리스너나 이터레이터등의 구현을 할 수 있습니다. 이 Generator와 Promise를 조합해서 비동기 처리를 동기처리처럼 직렬로 적을 수 있게 되었습니다. 기본적인 생각 법은 "비동기 처리가 개시되면 처리를 중단해, 비동기처리가 완료되면 처리를 재개한 뒤 연결 처리를 싱행해 나감"입니다. 아까, Promise 절에서 소개한 샘플 코드를 Generator를 사용해 직렬로 적어 보겠습니다. 밑의 샘플코드에서는 Generator와 Promise를 사용해 비동기 처리를 직렬로 적을수 있게 되는 co라는 라이브러리를 사용하고 있습니다.

// ES6
'use strict';

co(function*(){
  console.log('sleep...');
  yield sleep(1000);
  console.log('wake!');
});

이번에는 co를 사용해 해설했지만, co를 사용하지 않고 비동기처리를 직렬로 적는 구조로 async/await라는것이 ES7에서는 제공되고 있습니다.2 Generator에 관해서는 저의 블로그에서 ES6 Generatorを使ってasync/awaitを実装するメモ로 해설 하고 있습니다. 흥미가 있으시면 봐주세요.

결론

이번에는 JavaScript에서 안타까웠던 곳이 ES6에서 어떻게 변하는가를 중심으로 설명했습니다. 여기서 소개하는 내용은 ES6의 일부로, 이외에도 Modules, Symbol, Data Structures, Proxy, Template String등 여러 기능이 추가되어 있습니다. 현시점에서는 ES6로 적은 코드를 그대로 브라우져나 node에서 실행하는 것은 어려운 상황입니다만, ES6를 ES5로 트랜스컴파일하는 툴로 traceur-compiler6to5가 있으므로 가볍게 시험해 보실 수 있습니다. 또 각 브라우져나 툴이 ES6의 어느 기능에 대응하고 있는지는 ECMAScript compatibility table이 참고가 됩니다. ES6시대의 JavaScript를 준비해서 지금부터 조금씩 건드려 보시면 어떨까요?

  1. Firefox에서는2006년 쯤에 이미 구현되어 있습니다.
  2. C#의 async/await와 같은 것
@creamchoco3080
Copy link

좋은글 잘 읽고 갑니다 감사합니다!

@milooy
Copy link

milooy commented Oct 12, 2016

핵심 콕콕 정리해주셔서 고맙습니다 ㅎㅎ

@sungjk
Copy link

sungjk commented Oct 14, 2016

정리 잘 해주셨네요!! 감사합니다 👍

@jahona
Copy link

jahona commented Oct 15, 2016

ES5에서 변경되거나 추가된 것을 보기 좋게 설명해줘서 감사합니다!!

@yooncobra
Copy link

차이점을 명확한 예시로 보여 주셔서 잘 이해하고 갑니다^^

@limjungmok
Copy link

차이점을 확실히 알겠네요 감사합니다!

@acious
Copy link

acious commented Oct 16, 2016

코드단위로 어떤 기능들이 유용하게 추가되었는지 알수있었습니다.

@KimBoWoon
Copy link

좋은글 감사합니다!!

@pjhjohn
Copy link

pjhjohn commented Oct 16, 2016

잘 읽었습니다. 정리가 잘 되어있어 이해하기에 용이했습니다.
Generator의 경우에는 완전히 새로운 개념이라 따로 더 찾아서 이해해봐야 할 것 같네요. 감사합니다.

@gnujoow
Copy link

gnujoow commented Oct 17, 2016

잘 읽었습니다 :)

@hoilzz
Copy link

hoilzz commented Oct 18, 2016

좋은글 감사합니다!

Copy link

ghost commented Nov 13, 2016

잘 읽었습니다!

@octave08
Copy link

쉬운 예제들 적분에 이해하기 좋았습니다

@yung6699
Copy link

잘 읽었습니다.

@ttlqudan
Copy link

ttlqudan commented Dec 30, 2016

원문 해석 감사합니다. 잘읽었습니다.

@javapark
Copy link

잘 읽었습니다~~

@dotorify
Copy link

번역해주셔서 감사합니다~

@codertimo
Copy link

감사합니다 :)

Copy link

ghost commented Apr 3, 2017

잘 읽고 갑니다!!

@Failureformula
Copy link

Failureformula commented Jul 14, 2017

ES6 Class 관련 문서 찾아보다가 유용한 글을 찾았네요!. 번역 감사합니다!!.

@kkotkkio
Copy link

술술 읽히네요 정말 감사합니다

@qnfzks55
Copy link

qnfzks55 commented Jan 5, 2018

좋은글잘읽었습니다.

@shinyongmyung
Copy link

정리하신 내용 잘 봤습니다. ^^

@qkreltms
Copy link

qkreltms commented Jul 1, 2018

좋은글 감사합니다!

@ncia
Copy link

ncia commented Apr 1, 2022

너무나 좋은글 잘익었습니다.
혹시 출러를 밝히고 제 블로그에 퍼가도 될까요?
문제의 소지가 있다면 바로 삭제하겠습니다.

@marocchino
Copy link
Author

@ncia 제가 아니라 원저자분께 물어보셔야 할 것 같습니다. 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment