이 글은 How to Read the ECMAScript Specification(Timothy Gu)을 번역한 것입니다. 원문은 다음 링크에서 찾아보실 수 있습니다. 번역이 완벽하지 않을 수 있습니다. 혹시 잘못된 부분이 있다면 댓글이나 이메일로 알려주세요!
분량이 많아 부분별로 나눠서 올릴 예정입니다 :) 이번 글은 1장 Prelude에 해당하는 부분입니다.
ECMAScript Language specification(aka. JavaScript 스펙 또는 ECMA-262)는 JavaScript가 어떻게 동작하는지 공부할 수 있는 좋은 자료입니다. 하지만 양이 어마어마하기 때문에 처음에는 혼란스럽거나 겁먹을 수 있죠. 이 문서는 최고의 JavaScript 레퍼런스를 읽기 시작하는 것을 쉽게 만드는 것을 목표로 하고 있습니다.
ECMAScript 스펙을 매일 조금씩 읽는 것은 여러분들의 건강에 좋습니다. 새해의 목표 때문이던, 의사의 처방 때문이던 탁월한 선택입니다!
이 문서에서는 스펙 자체를 부르기 위해서는 오로지 "ECMAScript”라는 용어만 사용하고 다른 모든 곳에서는 "JavaScript”를 사용합니다. 하지만 두 단어 모두 같은 것을 지칭합니다. (역사적인 이유로 ECMAScript와 JavaScript에는 차이가 있지만 이건 문서의 범위를 벗어나므로 Google that distinction에서 찾아보세요)
ECMAScript는 모든 JavaScript 구현의 동작에 대한 권위적인 자료입니다. 여러분들의 브라우저[WHATISMYBROWSER], Node.js를 이용한 서버[NODEJS], 혹은 IoT 디바이스 등[JOHNNY-FIVE]에 상관 없이 말이죠. 모든 JavaScript 엔진 개발자들은 멋지고 새로운 기능을 개발하기 위해 이 스펙에 의존할 수밖에 없습니다.
하지만 이 스펙의 유용성이 “JavaScript 엔진 개발자”로 알려진 신화 속의 존재에게만 유용하다고 생각하지 않습니다. 그걸 넘어서 여러분들과 같은 보통의 JavaScript 코더들에게도 유용하죠. 단지 여러분들은 그걸 깨닫지 못했을 뿐입니다.
어느 날 일을 하다가 다음과 같이 이상한 코드를 발견했다 해봅시다:
> Array.prototype.push(42)
1
> Array.prototype
[ 42 ]
> Array.isArray(Array.prototype)
true
> Set.prototype.add(42)
TypeError: Method Set.prototype.add called on incompatible receiver #<Set> at Set.add (<anonymous>)
> Set.prototype
Set {}
왜 어떤 메소드는 프로토타입에 작동하고 다른 메소드들은 이 프로토타입에 작동하지 않는지 헷갈리게 되죠. 불행하게도 구글은 가장 필요할 때 항상 실패하고 없이는 못사는 스택오버플로우도 마찬가지입니다.
이 때 스펙을 읽는 것은 도움이 될 수 있습니다.
또는 도대체 왜 악명높은 loose equality operator(==
)가 제 기능을 하는지 궁금할 수도 있습니다.(여기서 “기능”은 대충 쓰여진 단어입니다 [WAT]). 공부하기 좋아하는 개발자들은 MDN에서 몇 문단에 달하는 설명을 볼 수 있지만 눈만 아플 뿐입니다 [MDN].
이 때 스펙을 읽는 것은 도움이 될 수 있습니다.
하지만 저는 JavaScript를 막 사용하기 시작한 개발자들에게는 ECMAScript 스펙 읽기를 추천하지 않습니다. 입문자라면 웹에서 마음껏 뛰어노세요! 웹 앱을 만들어보거나 다른 사람을 놀래킬 수 있는, 혹은 그 무엇이던 만들어보세요! 그리고 JavaScript 사용에 충분히 숙련되었을 때나 JavaScript에 대해 고민하지 않아도 될 때 이 문서로 다시 돌아오는 것을 생각해보세요.
이제 스펙이 복잡한 언어나 플랫폼을 이해하는 데 도움이 된다는 것을 충분히 이해했을 것 같네요. 그런데 정확히 어떤 것들이 ECMAScript에 포함되어 있을까요?
교과서적인 정답은 “오로지 언어와 관련된 기능만 ECMAScript 스펙에 포함된다”입니다. 하지만 이런 건 그다지 도움이 안되죠. “JavaScript의 기능들은 JavaScript다”라고 말하는 것과 별반 다를 게 없으니까요. 그리고 저는 동어 반복을 그리 좋아하지도 않습니다.
대신 JavaScript 앱을 만들 때 흔히 보이는 것들 중 몇 개를 나열하고 각각이 언어 기능에 포함됐는지 아닌지 말해드리겠습니다.
for
...in
루프가 어떻게 써져야 하는지) ⇒ Otypeof null
이나 { a: b }
가 무엇을 리턴하는지) ⇒ Oimport a from 'a';
⇒ △ [1]Object
, Array
, Function
, Number
, Math
, RegExp
, Proxy
, Map
, Promise
, ArrayBuffer
, Uint8Array
, globalThis
⇒ Oconsole
, setTimeout()
, setInterval()
, clearTimeout()
, clearInterval()
⇒ X [2]Buffer
, process
, global
* ⇒ X [3]module
, exports
, require()
, __dirname
, __filename
⇒ X [4]window
, alert()
, confirm()
, the DOM (document
, HTMLElement
, addEventListener()
, Worker
, ...) ⇒ X [5][1] ECMAScript 스펙은 이러한 선언 문법과 무엇을 의미하는지 명시하고 있지만 모듈이 어떻게 로드되는지에 대해서는 명시하지 않고 있습니다.
[2] 이들은 브라우저나 Node.js 모두에서 사용 가능하지만 표준은 아닙니다. Node.js의 경우 자체 문서에 문서화 및 명세화되어있습니다. 브라우저의 경우console
은 Console 표준에 명세되어있고 나머지는 HTML 표준에 명세되어 있습니다.
[3] 이들은 모두 Node.js에서만 사용 가능한 global이고 자체 문서에 문서화 및 표준화되어 있습니다.
*global
과globalThis
는 다름에 주의하세요.globalThis
는 ECMAScript의 일부이고 브라우저에도 구현되어 있습니다.
[4] 이들은 Node.js에서만 사용 가능한 모듈 “전역” 객체입니다. 자체 문서에 문서화 및 표준화되어 있습니다.
[5] 이들은 브라우저에서만 사용 가능합니다.
구글에 “ECMAScript Specification”을 검색하면 모두 자신들이 표준이라 외치는 스펙들을 볼 수 있습니다. 어떤 걸 읽어야 할까요?
한 줄 요약: tc39.es/ecma262의 배포본이 여러분들이 찾는 스펙입니다
좀 더 긴 설명:
ECMAScript 언어 스펙은 다양한 배경을 가진 사람들이 모인 Ecma International Technical Committee 39(혹은 TC39라는 더 친숙한 이름도 있죠[TC39])라는 그룹의 사람들에 의해 개발되고 있습니다[TC39]. TC39는 tc39.es에서 최신 버전의 ECMAScript를 관리합니다[ECMA-262].
문제를 복잡하게 만드는 것은 매년 TC39는 특정 시점에 스냅샷으로 에디션 번호와 함께 해당 연도의 ECMAScript 언어 표준을 정한다는 것입니다. 예를 들어 ES10이나 ES2019로 널리 알려진 ECMAScript® 2019 Language Specification (ECMA-262, 10th edition) [ECMA-262-2019]의 경우 2019년 6월 PDF로 변환되어 영구 보존을 위해 포름알데히드에 담궈진 것입니다(역주-해부학 샘플들은 포름알데히드 용액에 보존됩니다).
그래서 2019년 6월에 포름알데히드에 담궈진 브라우저에서만 동작하는 웹 어플리케이션을 만드려는 의도가 아닌 이상 tc39.es에서 항상 최신 버전의 스펙을 참고하는 것이 좋습니다. 하지만 옛 버전의 브라우저나 Node.js를 지원하려면 옛 버전의 스펙을 찾아보는 것이 도움이 될 것입니다.
참고: ISO/IEC 또한 ECMAScript 언어 스펙을 ISO/IEC 22275로 재발행합니다. 하지만 걱정 마세요. 여전히 표준은 다음 링크에 있습니다. [ECMA-262]
ECMAScript는 정말 방대한 양을 다루고 있습니다. 저자들이 논리적으로 구분하기 위해 최선을 다하고 있기는 하지만 여전히 긴 텍스트입니다.
개인적으로는 스펙을 5 부분으로 나누려 합니다.
for
- in
루프는 어떻게 작성할까?)var
statement 내에서 어떻게 결정되는가?)for
- in
루프는 어떻게 실행되는가?)String.prototype.substring()
은 무슨 역할을 하는가?)하지만 이 분류는 실제 스펙이 구분된 방식이 아닙니다. 첫 번째 부분은 §5 Notational Conventions부터 §9 Ordinary and Exotic Objects Behaviours까지 산재해있고 다음 세 부분은 §10 ECMAScript Language: Source Code 부터 §15 ECMAScript Language: Scripts and Modules까지 퍼져있습니다. 다음과 같이 말이죠:
if
Statement 언어의 문법 규칙do``while
Statementwhile
Statement그리고 마지막인 API들은 §18 The Global Object 부터 §26 Reflection까지 나눠져 있습니다.
여기서 스펙을 처음부터 끝까지 읽는 사람은 아무도 없다는 사실을 말하고 싶습니다. 그보다는 대부분은 필요한 장, 그 중에서도 필요한 부분만을 읽을 것입니다. 이 때 여러분이 찾고 싶은 것이 위에서 말한 다섯 가지 분류 중 어디에 속하는지 생각해보세요. 만약 나누는 데 어려움이 있다면 스스로 질문을 해보세요. “이게(이것이 무엇이던지 간에) 계산되는 시점이 어디지?”라는 질무에 답하는 것은 도움이 될 수도 있습니다. 걱정하지 마세요. 연습 후에는 스펙을 살펴보는 것이 어렵지 않을 겁니다.