Symbol 프로그래밍?

https://en.wikipedia.org/wiki/Symbol_(programming)

심볼. 굳이 우리말을 쓰자면 기호. 기호 개발. (뭔가 어색하다?)

오늘 페북에서 ECMAScript 6 표준에 Symbol이 정의되어 누군가가 MDN에 번역해 주었다.
Symbol – Javascript | MDN
심볼. 아마 자바, 닷넷, 자스 개발자 등에게 어찌보면 생소한 개념일 수 있다.
하지만 루비 개발자나 Object-C 개발자는 이미 있기 때문에 알고 있을 것이다.
이 심볼 개발방법은 여러 스크립트 언어 등에서 채택한 개발론이다.

간단하게 설명하자면, “가장 유일한 식별자”다.
뭐?
루비(Ruby)의 심볼이란?

정의는 한 번. 더 이상 변경할 수 없는 값이다.
여기까진 상수와 비슷한 개념이다.
그렇다면 값 비교 시 “A” 와 “A”가 같다? 심볼은 그딴거 없다.
그렇다. 심볼의 값은 일종의 사람만이 식별하기 위해 존재하는 값일 뿐.
문자열 값은 리터럴을 정의할 때마다 메모리가 늘어난다. 설령 변수를 지정하던 상수를 쓰던.
간단하게 생각해보자.
“A” “BC” “DEF”… 이들은 언제 바뀌지 못하기 때문에 불명확한 메모리를 할당받으며 서 있다.
당연히 그런 만큼 메모리를 잡아먹는다.
하지만 심볼은 딱 정해진 메모리만큼만 먹는다. 바뀔 일도 없기 때문에. 확장할 일도 없고.
그러니 메모리 애끼는 효과가 있다.

그렇다면 어따가 쓰냐고?
가장 쓰임새가 많이 쓰이는 곳이 바로 “해시 값”을 다루는 데에 쓰인다.

JS에 새로 정의된 Symbol을 통해 예제 하나 뿌리겠다.
ES6 Symbol Test 예제

var obj = {};
var a = Symbol("a")

obj[a] = "a";
obj[Symbol.for("b")] = "b";
obj["c"] = "c";
obj.d = "d";

for (var i in obj) {
   console.log(i); // logs "c" and "d"
}

console.log(obj[a]); //logs "a"

심볼 “a” 란 해시 키에 “a” 값을 넣었다.
근데… 반복문에는 나타나지 않는다.
그렇다. 주소 값이니 나오지 않을 수밖에.
대신에 명시적으로 해시 키를 정의하면 가져올 수 있다.
이런 이득이 있다는 것이다.
그렇다면 이런 특징으로 얻을 수 있는 이득이 뭐가 있을까?

간단하다. hidden 키와 값을 넣을 수 있는 그런 개발이라면 모두 가능하다.
일종의 private identifier 인 셈이다.

자. 이제. 자바와 닷넷도 이런 개념이 존재하냐고 물어보는 사람이 있을 것이다.
당연히 없다. 근데 자바와 닷넷은 문자열에 한해 인터닝(Intern) 이라는 개념이 있다.

자바의 String.intern() String.intern()은 메모리를 아낄 수 있다?

  • String pool에 있는 각종 문자열에 equals해서 같은게 있다면 그 놈을 반환하고,
  • 같은게 없다면 String pool에 String object를 추가하고, 추가한 놈을 반환한다.

즉, String 객체를 리터럴로 관리하여 메모리 절약을 꽤하는 개념이다.
그렇다 보니 아래와 같은 단점이 존재한다.

  • 우선 String 객체를 하나 만들어야 한다.
  • String의 equals 메소드를 이용해서 String pool에 있는 놈을 찾아서 비교해야 한다. ( 시간이 걸림 )
  • String pool에 들어 갔으므로, 더 이상 GC(가비지컬렉션)의 대상이 될 수 없다. ( 메모리 관리 불가 )

그렇기 때문에 적재적소에 잘 관리해야 한다는 점이 있다. 그래도 같은 문자열을 객체로 정의하여 메모리 쳐묵보단 낫다.

참고로 닷넷도 아주 동일한 개념이다. String.Intern() 메소드 쓰는 건 매한가지이며 동일하기 때문에 별도의 설명은 생략하겠다.

자. 자바와 닷넷이 문자열을 관리하는 방법이 여기서 나온다. 리터럴은 자바나 닷넷이나 내부 풀을 따로 운영하여 저장한다. GC에 걸리는 걱정 없이.
(물론 new String 처럼 객체로 만들었다면 걸릴 각오는 해야 하지만.)

뭔가 삼천포로 빠진 것 같다.
하지만 메모리를 아껴서 빠르고 유연한 개발을 꾀하는 개발자는 있다.
이럴 때 필요한 것이 뭐고, 뭘 알아야 하는지는 개발자의 사명인 것이다.
그러기 위해 이 포스트를 올렸다.
그럼 이제…

끗.

composite / 2015년 11월 17일 / Piss Development / 0 Comments

ES6 Generator 와 ES7 Generator의 차이점?

ES6 Generator

ES6의 Generator 는 Generator body가 .NET 3.5 의 Enumerable과 Java의 Iterator를 섞은 느낌.

function *foo(){
    for(var i=1;i< =10;i++){
        yield i;
    }
}

for (var v of foo()) {
    console.log(v);
}

그러나 일반적인 Iterator와는 달리 ES6의 Generator는 .next() 메소드 호출이 무제한 가능하다.
따라서 .next()를 통해 얻은 값에서 done:true 인 것으로 필터링 해야 한다.
만약 자바처럼 while(b = a.next()) 이런 식으로 반복문을 돌면 무한 반복이 된다.
왜냐면 a.next() 가 다 돌았다고 해도 계속 마지막 값을 전달하는 스펙 때문이다.
수정 : 모두 호출한 다음 next()를 호출하면 다음 yield 값을 불러오는데 없으면 value가 undefined 나온다. 물론 done:true 는 여전하다.

var a = foo(), b;
while(!(b = a.next()).done)
    console.log(b);

이런 측면에서 ES6 Generator는 기존 개발자들에게 Iterator 와는 다른 혼란이 올 수도 있다.
게다가 비동기를 쓰려면 인라인 함수도 Generator 구문을 사용해야 하며,
비동기 연동은 Promise 객체를 사용해야 하고, spawn 함수가 필요하다.
여러가지로 비동기 사용이 번거로워 계륵같은 구문이 아닐 수 없다.

spawn 함수 1
spawn 함수 2

사용 예제

//제목과 챕터별 내용을 외부 경로에서 비동기로 출력하는 예제
//getJSON 함수는 Promise 패턴을 사용한 Ajax 함수라고 가정한다.
function loadStory() {
  return spawn(function *() {
    try {
      let story = yield getJSON('story.json');
      addHtmlToPage(story.heading);
      for (let chapter of story.chapterURLs.map(getJSON)) {
        addHtmlToPage((yield chapter).html));
      }
      addTextToPage("All done");
    } catch (err) {
      addTextToPage("Argh, broken: " + err.message);
    }
    document.querySelector('.spinner').style.display = 'none';
  });
}

ES7 Generator

ES7의 Generator 는 .NET 4.5 의 await와 async 구문을 채용한 듯 한 느낌을 줄 것이다.
사실 사용법도 크게 다르진 않다. 게다가 비동기에 대한 배려가 잘 적용되어 있다.

//제목과 챕터별 내용을 외부 경로에서 비동기로 출력하는 예제
//getJSON 함수는 Promise 패턴을 사용한 Ajax 함수라고 가정한다.
async function loadStory() {
  try {
    let story = await getJSON('story.json');
    addHtmlToPage(story.heading);
    for (let chapter of story.chapterURLs.map(getJSON)) {
      addHtmlToPage((await chapter).html);
    }
    addTextToPage("All done");
  } catch (err) {
    addTextToPage("Argh, broken: " + err.message);
  }
  document.querySelector('.spinner').style.display = 'none';
}

ES6과 달리 spawn 함수가 필요없다. 하지만 비동기인 만큼 Promise 객체를 사용해야 한다는 사실은 변함없다.
하지만 번거로운 spawn 함수가 없는 데다가 코드가 상당히 간결해졌다. 그래서 ES6 스펙이 그냥 ES7로 넘어갔으면 하는 희망사항이 생기기도 한다.

사용법도 간단하다.

(async function() {
  await loadStory();
  console.log("Yey, story successfully loaded!");
}());

단지 실행시에도 Generator 구문을 써야 한다는 점만 빼면 간단하게 실행이 가능하다.

그럼 자바스크립트 개발자의 이해를 돕기 위해 .NET 4.5 에서 추가된 await/async 예제를 보도록 하자.
Async 및 Await를 사용한 비동기 프로그래밍

//msdn.microsoft.com 의 내용 길이를 가져오는 비동기 함수
async Task<int> AccessTheWebAsync()
{ 
    // HTTP 클라이언트 선언
    HttpClient client = new HttpClient();

    // GetStringAsync 함수는 mdsn.microsoft.com 의 HTML 내용을 비동기로 받아둔다.
    // Task 클래스는 JS의 Promise와 동일한 역할을 하며, (자바는 Future<t>) 일단 작업을 받아낸다.
    Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

    // 일단 작업은 받아냈기 때문에 기다릴 필요 없이 해당 구문으로 넘어간다. 기타 할 것들.
    DoIndependentWork();

    // await를 사용하여 getStringTask 의 작업이 끝날 때까지 AccessTheWebAsync 메소드는 기다린다.
    // 만약 getStringTask 의 작업이 끝나면 urlContents 에 결과값을 전달한다.
    // 이게 끝날 때까지 다음 절차로 넘어갈 수 없다.
    string urlContents = await getStringTask;

    // 이제 위의 비동기 작업이 끝났으니 작업을 마무리할 시간.
    // 비동기 작업을 통해 받아온 urlContents 의 문자열 길이를 출력함으로서 작업을 마친다.
    return urlContents.Length;
}

여기서 공통점이 있다면 Promise 특성과 Generator의 성격을 안다면 알겠지만,
await 구문에 있는 메소드나 Promise 객체가 then 을 통하여 값을 전달할 때 까지는 다음 작업을 수행할 수 없다는 것이다.
ES6의 yield 과는 다르다. yield 구문은 그냥 쌩 값을 반복하듯이 내보내기 때문에 직접 핸들링 해야 한다는 차이점이 있다.
이 차이점을 이해했다면 ES7의 Generator 가 더 진보되고 편해졌다고 느껴질 것이다.

미안하다. 나도 다쓰고 이해시키는 것에 실패했다. 조만간 부가설명이나 글 수정을 통해 개선하겠다.
게다가 일도 바빠서 여기까지.

참고자료

The Basics Of ES6 Generators
ES7 async functions

composite / 2015년 4월 6일 / Piss Development / 0 Comments

ECMAScript 6 Harmony 를 네 브라우저에 돌려봐라!

나 참.. 기분 존나 나쁘네.

뭐? CoffeeScript? TypeScript? 어쩌고 저째? 자바스크립트가 죽을 거라고?

난 동의 안한다. 제아무리 자바스크립트를 편하게 만들어도 오리지날이다. 결국 그들도 자스로 움직인다. 제아무리 편해도.

스칼라가 결국 자바에서 돌아가기 위해 자바 코드로 생성해서 컴파일 하는 것처럼 말이다.

튜닝의 끝은 순정이란 말이 괜히 나오는게 아니지.

그래서 소개한다. 니 브라우저에 직접 ECMA 6 을 적용할 기회를 내가 선사해 주겠다. 2가지 프로젝트가 있다.

1. Traceur

구글신은 미쳤다. 이번에 소개할 피로젝트는 자바 2명 타요가 아닌 외계인 2명 타요 해서 ECMA 6을 몸소 체험할 수 있는

Traceur 를 소개한다.

프로젝트 페이지는 https://github.com/google/traceur-compiler

이 스크립트 컴파일러는 ES6 을 당신 브라우저에 돌릴 수 있게 한다.

일단 IE 8은 안된다. 쳇. 최소한 8이라도 지원해주지.

크롬과 불여우는 일단 된다. 올ㅋ

Traceur 가 지원하는 ECMA 6 기능이다.

일단 여태 나오고 알려진 것들을 지원한다.

하지만 이 ES6 하모니를 쓸 때 몇가지 주의할 점이 있다.

알다시피 ES6은 확정된 표준이 아니다. 특히 클래스와 모듈이 그 대표적인데, 이들이 어떻게 바뀔지는 장담을 못하겠다.

제발 안바뀌길 바래라.

뭐.. 그래봐야 기반 스크립트 (타입스크립트 등) 또한 아직까지 안정화된 버전을 내놓지 못하고 있기 때문에

물론 커피스크립트는 상당히 안정화 되고 쓰는 사람 많은거 안다.

근데 뭐가 좋다 말을 못하겠지만, 그래도 그런 스크립트는 표준에 넣지 못할 것이다.

난 커피스크립트가 자바의 스칼라처럼 생각하지만, 그래도 스칼라도 많이 쓰지 않는가? 물론 갈라파고스 한국은 빼고.

난 표준을 만들고 따르는 주의자긴 하지. 자바스크립트는 아직까지는 강력하기 때문에. 단지 좀 귀찮고 짱나서 그렇지.

어쨌든, ES6 을 직접 체험해보고자 한다면, 이 컴파일러를 이용해 제작된 ES6Fiddle 을 사용하라.

http://www.es6fiddle.net/

ECMAScript 6 이 어떻게 돌아가는지 알 수 있을 것이다.

2. continuum 

그에 비해 이 ES6 컴파일러는 커뮤니티의 참여로 만들어졌다. 그다음 낫고, IE 8부터 지원해주기 때문에 우왕굳.

프로젝트 사이트는 https://github.com/Benvie/continuum

체험 사이트는 http://benvie.github.io/continuum/

현재 구현된 기능들이다.

  • 통합 할당 및 인자
  • 가변 처리자 및 배열 초기자
  • 가변 인자
  • 클래스와 상속
  • 화살표식 함수(람다식) (.NET 의 람다식과 동일한 형식)
  • 블록 단위 변수
  • 새로운 Math 함수
  • 새로운 Object 함수
  • 새로운 String 함수
  • concise methods in object literals
  • 열거 및 삭제 가능한 프로토타입
  • Map, Set, 그리고 WeakMap (가비지 컬렉션이 현실화되지는 않았음)
  • 열거자와 for…of 반복문
  • 템플릿
  • 모듈과 imports, exports
  • 내장 ‘@std’ 모듈 module std = '@std' 또는 import call from '@function'
  • 생성기
  • 프록시와 리플렉션
  • Symbols with syntactic @name support
  • 형식화된 배열Typed Arrays
  • Object.observe (es-next/es7)
  • 인자 기본값
  • 꼬리물기 호출 최적화
  • 배열 초기화 식 (부분 지원)

아래 기능은 아직 구현이 안되어 있으며 추후 지원될 예정이라 한다.

  • 생성기 식
  • 이진 데이터 api (structs, etc.)
  • 프로미즈 (내가 Feature request 했음.)

물론 프로미즈같은 경우 promisejs 또는 q 로 해결은 가능하기 때문에 커버는 해줄만 하다.

ECMA 6 기능에 목이 말랐다면 지금 바로 체험하라.

아참, 마지막으로 ES6의 새로운 기능을 한글로 잘 정리한 위키 페이지가 있어 이걸 소개하고 쿨하게 끝내겠다.

http://wiki.codekin.com/index.php/ECMAScript_%ED%95%98%EB%AA%A8%EB%8B%88

끝이다.

추신 업데이트 사항 : Traceur 외에 나머지 ES6 컴파일러는 몇달동안 활동이 없다. 그냥 Traceur 써라. 진자끝.

composite / 2014년 2월 25일 / 미분류 / 0 Comments