본문으로 건너뛰기

버그 제보

이상한 점을 알려 주시면 고칠게요. 지금 보고 있는 페이지와 브라우저 정보가 함께 담겨요.

페이지:

Frontend

ECMAScript 표준과 TC39 프로세스

추가
목차11개 항목
출처와 참고

팔만코딩경(42Seoul 커뮤니티 위키)의 「ECMAScript와 ECMAScript 2022의 제안 알아보기」, 「[JavaScript] JavaScript 는 어떤 언어인가?」를 바탕으로, javascript.info(모던 JavaScript 튜토리얼)의 「자바스크립트란?」, 「매뉴얼과 명세서」, 「폴리필」 글을 더해 재구성한 노트다. 초기 연표는 「A Brief History of JavaScript」를 바탕으로 더했다. ES2015~ES2021 기능 표와 내장·호스트 객체 구분은 「Must-Know-About-Frontend: ES6」 등 ES 판별 정리와 「네이티브 객체 vs 호스트 객체」를 바탕으로 보강했다. 폴리필의 정의는 Remy Sharp의 「What is a Polyfill?」(2010)으로 보강했다.

ECMAScript는 JavaScript의 핵심 문법과 내장 객체를 정의한 언어 표준이다. 규격 문서의 번호는 ECMA-262이고, ECMA International의 기술 위원회 TC39가 관리한다. JavaScript는 이 표준을 구현한 언어이고, 브라우저의 DOM이나 Node.js의 fs처럼 실행 환경이 덧붙이는 API는 ECMAScript 바깥이다.

누가 정하나

주체역할
ECMA International정보통신·전자 제품 표준을 만드는 표준화 기구. ECMAScript 외에 C#(ECMA-334), Office Open XML, NFC 같은 표준도 냈다
TC39 (Technical Committee 39)ECMA-262와 국제화 API 표준(ECMA-402)을 관리하는 위원회. 브라우저 제조사, 기업, 개인 전문가가 참여하고 합의(consensus)로 결정한다
브라우저·런타임 엔진V8, SpiderMonkey, JavaScriptCore가 제안을 구현해 실제로 쓸 수 있게 만든다

표준이 먼저 있고 JavaScript가 나온 것이 아니다. 1995년 Netscape가 브라우저를 제어하는 스크립트 언어로 JavaScript를 만들었고, Microsoft의 JScript 같은 변종이 생기자 1996년 Netscape가 ECMA에 표준화를 맡겼다. 그 결과가 1997년의 ECMAScript 1판이다.

timeline
  1995.05 : Mocha: Brendan Eich가 Netscape에서 짧은 기간에 만든 프로토타입 → LiveScript
  1995.12 : JavaScript로 개명: Java는 큰 컴포넌트, JavaScript는 작은 브라우저 스크립트라는 역할 분담
  1996 : Microsoft JScript(IE 3): 구현 차이로 오랜 브라우저 호환성 문제의 시작
  1997 : ES1
  1998 : ES2(ISO 표준과 맞춤)
  1999 : ES3(정규식, try/catch)
  2000년대 : AJAX 확산: ES4(클래스·모듈·정적 타입까지 넣은 대개편) 논쟁으로 정체
  2008 : 오슬로 회의#58; ES4 폐기, 작은 개정 ES3.1과 장기 계획 Harmony로 정리
  2009 : ES3.1이 ES5로
  2011 : ES5.1
  2015 : Harmony의 성과가 ES2015로

ES4의 실패가 지금의 운영 방식을 만들었다. 큰 판을 한 번에 합의하려다 10년을 잃은 뒤로, 제안을 하나씩 단계별로 통과시키고 준비된 것만 매년 판에 싣는다.

TC39 프로세스: 제안이 표준이 되기까지

새 문법은 제안(proposal) 으로 시작해 단계를 거친다.

단계의미
Stage 0아이디어. 아직 위원회가 검토하지 않았다
Stage 1다룰 가치가 있는 문제로 인정. 해결 방향을 탐색한다
Stage 2설계 초안 선택. 세부는 바뀔 수 있다
Stage 2.7명세 텍스트 완성·승인. 테스트(test262)와 시범 구현을 기다린다 (2024년에 추가된 단계)
Stage 3구현 권장. 엔진들이 구현하고 실사용 피드백을 받는다
Stage 4완료. 두 개 이상의 호환 구현과 테스트가 갖춰져 다음 판에 실린다

Stage 4에 오른 제안은 그해 초 명세 초안에 합쳐지고, 매년 6월 ECMA 총회에서 새 판이 승인된다. 그래서 많은 기능이 표준이 되기 전에 이미 브라우저에 들어가 있다. 진행 중인 제안 목록은 tc39/proposals에서 볼 수 있다.

JavaScript라는 이름과 실행 환경

처음 이름은 LiveScript였다. 당시 인기 있던 Java의 동생처럼 홍보하려고 JavaScript로 바꿨을 뿐, 두 언어는 관계가 없다. 상표 문제 때문에 표준의 이름은 ECMAScript가 됐다.

JavaScript는 엔진이 있는 곳이면 어디서든 돈다. 브라우저마다 엔진이 있고(Chrome·Edge·Opera의 V8, Firefox의 SpiderMonkey, Safari의 JavaScriptCore), 서버의 Node.js·Deno는 V8, Bun은 JavaScriptCore를 쓴다. 엔진의 동작은 JavaScript는 인터프리터 언어일까, 컴파일 언어일까에 있다.

언어가 할 수 있는 일은 실행 환경이 정한다. 언어 자체에는 메모리·CPU를 직접 건드리는 기능이 없고, 파일·네트워크·화면은 환경이 주는 API로만 다룬다.

같은 구분이 객체에도 있다. Object·Array·Promise·Math·JSON처럼 ECMAScript 명세가 정의해 어느 엔진에나 있는 객체를 표준 내장 객체(standard built-in object)라 하고, window·document·XMLHttpRequest나 Node.js의 process처럼 실행 환경(호스트)이 덧붙인 객체를 호스트 객체라 한다. ES5 명세는 앞의 것을 네이티브 객체(native object)라 불렀지만, ES2015부터는 이 용어를 쓰지 않고 "표준 내장 객체"와 "호스트 정의(host-defined)"로 부른다. 호스트 객체는 환경마다 있거나 없으므로, 환경을 가리지 않는 코드는 표준 내장 객체와 globalThis만 기대한다.

환경할 수 있는 일막혀 있는 일
브라우저DOM 조작, 사용자 입력에 반응, 네트워크 요청, 쿠키·로컬 저장소임의의 디스크 파일 읽기·쓰기(사용자가 고른 파일만), 허락 없는 카메라·마이크, 다른 출처(origin)의 페이지·응답 읽기(동일 출처 정책. 허용은 서버가 CORS 헤더로 한다)
Node.js 등 서버파일 시스템, 프로세스, 임의의 네트워크브라우저 API(DOM)는 없다

TypeScript, 예전의 CoffeeScript·Flow처럼 JavaScript로 변환(트랜스파일)되어 실행되는 언어도 있다. 결국 JavaScript로 돌기 때문에 무엇이 일어나는지 이해하려면 JavaScript를 알아야 한다(TypeScript: 개념과 문법 한눈에 보기).

판과 판별 주요 기능

이름 규칙: ES6와 ES2015

이름연도비고
ES31999정규식, try/catch. 오랫동안 기준선이었다
ES52009strict mode, JSON, 배열 고차 함수. ES4는 너무 큰 변경이라 폐기됐다
ES6 = ES20152015let/const, 화살표 함수, 클래스, 모듈, Promise, 구조 분해가 들어간 가장 큰 개정
ES2016 (ES7) 이후2016~매년 6월 한 판씩 작게 낸다. ES2017(ES8)의 async/await처럼 판 번호로 부르는 관용이 남아 있다

ES2015부터 공식 이름은 연도다. ES6·ES8 같은 판 번호는 관용적으로 남아 있을 뿐이고, 판 번호 = 연도 − 2009다.

ES2015~ES2021

판주요 추가자세한 노트
ES2015 (ES6)let·const, 화살표 함수, 클래스, 템플릿 리터럴, 구조 분해, 기본값·rest·spread, 이터레이터와 for...of, 제너레이터, 모듈, Map·Set·WeakMap·WeakSet, Symbol, Promise, Proxy·Reflect, Array.from·find·includes(문자열), Object.assign변수와 데이터 타입, class 문법, 구조 분해 할당, 이터레이터와 제너레이터, 모듈, Map과 Set, Symbol, 콜백과 Promise
ES2016Array.prototype.includes(indexOf와 달리 NaN도 찾는다), 거듭제곱 **배열과 객체
ES2017async/await, Object.values·Object.entries, Object.getOwnPropertyDescriptors, padStart·padEnd, 매개변수·인자 목록의 끝 쉼표async/await, 속성 설명자
ES2018비동기 이터레이션(for await...of), 객체 rest·spread({ ...rest }), Promise.prototype.finally, 정규식 이름 있는 그룹·후방 탐색·s 플래그이터레이터와 제너레이터, 정규 표현식
ES2019flat·flatMap, Object.fromEntries, trimStart·trimEnd, Symbol.prototype.description, catch 매개변수 생략(catch {}), Array.prototype.sort 안정 정렬 보장배열과 객체, 문자열
ES2020String.prototype.matchAll(캡처 그룹·index까지 담은 결과를 이터레이터로), 동적 import(), BigInt, Promise.allSettled, globalThis, 옵셔널 체이닝 ?., null 병합 ??, import.meta연산자와 제어 흐름, 숫자
ES2021replaceAll, Promise.any와 AggregateError, 논리 할당 &&=·||=·??=, 숫자 구분자 1_000_000, WeakRef·FinalizationRegistry가비지 컬렉션과 메모리 누수

Promise.all은 하나라도 거부되면 그 이유로 즉시 거부되지만, Promise.allSettled는 모두 끝날 때까지 기다려 { status: 'fulfilled', value }·{ status: 'rejected', reason } 배열을 돌려준다. match에 g 플래그를 주면 일치한 문자열만 남지만, matchAll은 일치마다 캡처 그룹과 groups를 담은 결과를 준다.

ES2022

기능예하는 일
클래스 필드class A { count = 0; #secret; static max = 10; }생성자 밖에서 필드 선언, #으로 private, static 필드·private 메서드
정적 초기화 블록class A { static { /* 초기화 */ } }클래스 정의 안에서 여러 static 필드를 try/catch 등으로 한꺼번에 초기화
private 필드 존재 확인#brand in obj접근하면 예외가 나는 private 필드를 try/catch 없이 확인
top-level await모듈 최상단의 await fetch(...)ES 모듈에서 async 함수로 감싸지 않고 바로 기다린다
.at()arr.at(-1), 'abc'.at(-1)음수 인덱스로 뒤에서부터 접근. arr[-1]은 "-1" 프로퍼티로 해석되어 안 된다
Object.hasOwn()Object.hasOwn(obj, 'key')Object.prototype.hasOwnProperty.call(obj, 'key')의 짧고 안전한 대체. 객체가 hasOwnProperty를 덮어써도 정확하다
정규식 d 플래그/a+/d.exec(s).indices일치한 부분의 시작·끝 인덱스를 함께 돌려준다
Error causenew Error('저장 실패', { cause: err })다시 던지는 오류에 원인 오류를 연결해 디버깅 정보를 잃지 않는다

클래스 문법 전반은 class 문법, 모듈은 모듈: export와 import에 있다.

ES2023 이후

판주요 추가
ES2023findLast·findLastIndex, 원본을 바꾸지 않는 toSorted·toReversed·toSpliced·with, 해시뱅(#!) 문법
ES2024Object.groupBy·Map.groupBy, Promise.withResolvers, 정규식 v 플래그, 크기 조절 가능한 ArrayBuffer
ES2025이터레이터 헬퍼(iterator.map().filter().take()), Set의 합집합·교집합 메서드, JSON 모듈과 import 속성(with { type: 'json' }), 정규식 패턴 수식자((?i:abc))와 중복 이름 캡처 그룹, Promise.try, Float16Array, ArrayBuffer의 transfer, RegExp.escape
ES2026Array.fromAsync, Error.isError, Math.sumPrecise, Uint8Array의 Base64·hex 변환, 이터레이터 이어 붙이기(Iterator.concat), Map·WeakMap의 getOrInsert(Upsert), JSON.parse 원문 접근(JSON)

날짜·시간 API Temporal과 using 선언(Explicit Resource Management), 이터레이터 chunks·join·includes와 여러 이터레이터를 나란히 도는 Iterator.zip, Atomics.pause는 Stage 4를 마쳐 ES2027에 실린다. ES2026은 새 문법 없이 내장 함수만 더한 판이다. 정규식 기능 전반은 정규 표현식: 문법·플래그·메서드에 있다.

새 기능을 실제 서비스에 쓸 때는 판 번호보다 브라우저 지원 범위가 기준이다. MDN의 호환성 표나 Baseline 표시로 대상 브라우저에서 되는지 확인한다(책임감 있는 자바스크립트).

지원하지 않는 환경에서 쓰기: 트랜스파일러와 폴리필

엔진은 표준을 한꺼번에 구현하지 않는다. 명세에 오른 기능보다 Stage 3 제안을 먼저 넣기도 하고, 어떤 기능은 늦게 넣는다. 그 틈을 메우는 도구는 무엇이 없느냐에 따라 둘로 나뉜다.

트랜스파일러폴리필(polyfill)
메우는 것새 문법(?., 클래스 필드, async)새 내장 함수·객체(Array.prototype.at, Promise.withResolvers, structuredClone)
방법빌드할 때 소스를 옛 문법으로 다시 쓴다실행할 때 그 기능이 없으면 같은 동작의 구현을 채워 넣는다
도구Babel, TypeScript, esbuild·SWC(번들러 안에서)core-js(빌드 도구가 대상 브라우저에 필요한 것만 넣는다)

문법은 실행 전에 파싱되므로 런타임에 채울 수 없다. 그래서 문법은 변환, API는 폴리필이다.

폴리필은 표준 API와 똑같은 이름·동작으로 빈 곳을 채우는 코드다. 2009년 『Introducing HTML5』를 쓰며 붙은 이름으로, 벽의 금을 메우는 퍼티 상품명(Polyfilla)에서 왔다. 넣어 두기만 하면 앱 코드는 기능 감지나 분기 없이 표준 API를 그대로 쓴다. 기능이 있는 환경에서는 아무 일도 하지 않는다. 그래서 브라우저가 표준을 구현하면 폴리필만 지우면 된다.

비슷한 말차이
심(shim)빠진 기능을 메우되 자기 API를 갖는 코드를 넓게 이른다. 폴리필은 표준 API를 흉내 내는 심의 한 종류다
포니필(ponyfill)전역을 건드리지 않고 같은 기능을 모듈로 import해 쓴다. 라이브러리가 사용자 환경의 전역을 바꾸면 안 될 때 쓴다
점진적 향상기본 경험 위에 기능을 얹는다. 폴리필은 반대로 기능이 있다고 가정하고 없는 곳을 채운다

폴리필이 내장 프로토타입을 고치는 방식과 그 예외 규칙은 프로토타입과 상속에 있다.

과거에는 요청한 브라우저에 맞는 폴리필을 골라 주는 CDN 서비스 polyfill.io를 <script>로 불러 쓰곤 했다. 2024년 도메인 소유권이 넘어간 뒤 그 주소로 악성 코드가 배포되는 공급망 공격이 일어났다. 폴리필은 빌드에 포함해 직접 배포하고, 남의 도메인에서 실행 코드를 불러오는 일은 서드파티 스크립트로 따로 관리한다.

참고 자료

자료쓰는 때
ECMA-262 최신 초안동작의 최종 근거. 읽기 어려워 일상 참고용은 아니다
tc39/proposals진행 중인 제안과 Stage
MDN JavaScript Reference메서드·문법의 설명과 예제, 호환성 표, Baseline 표시. 검색 엔진에 MDN 키워드로 찾는다
Can I use브라우저 기능의 지원 표

정리

  • ECMAScript(ECMA-262)는 JavaScript의 표준이고, ECMA International의 TC39가 관리한다.
  • 제안은 Stage 0 → 1 → 2 → 2.7 → 3 → 4를 거치고, Stage 4가 되면 매년 6월 승인되는 새 판에 실린다.
  • ES2015부터 이름은 연도다. ES6 = ES2015가 가장 큰 개정이었고 이후는 매년 작게 늘어난다.
  • 쓸 수 있느냐는 판 번호가 아니라 대상 브라우저의 지원 여부로 판단한다.

관련 노트: class 문법, JavaScript 엔진과 V8, strict mode, 정규 표현식: 문법·플래그·메서드, TypeScript: 개념과 문법 한눈에 보기, 변수와 데이터 타입

원본 기록14

댓글

GitHub 계정으로 댓글을 남길 수 있어요.