
요약
2016년부터 매년 이어 오던 "JavaScript의 동향" 연재는 2020년을 마지막으로 한동안 쉬었습니다. 그사이 6년이 지났고, 그 6년 동안 ECMAScript 명세는 여섯 번 개정되었습니다. 2020년 글을 쓸 때 Stage 2였던 Temporal은 9년에 걸친 논의 끝에 Stage 4에 올랐고, 이제 Chrome과 Firefox, Node.js에서 플래그 없이 쓸 수 있습니다.
달라진 것은 명세만이 아닙니다. 2020년에는 상상하기 어려웠던 방식으로 코드를 쓰는 사람이 늘었습니다. AI 코딩 도구가 코드의 상당 부분을 작성하고, 사람이 방향을 정하면 AI 에이전트가 브라우저 엔진의 JavaScript 엔진 코드를 다른 언어로 옮기는 일까지 생겼습니다. 2025년 GitHub Octoverse에서 TypeScript가 처음으로 가장 많이 쓰인 언어 1위에 올랐고, GitHub은 그 배경으로 AI를 꼽았습니다.
이번 연재는 다음 네 편으로 구성됩니다.
- 1편: JavaScript(ECMAScript)
- 2편: 브라우저 밖의 JavaScript
- 3편: WebAssembly
- 4편: 라이브러리와 프레임워크
직전 회차인 2020년과 이후 JavaScript의 동향 - JavaScript(ECMAScript)에서는 ECMAScript 2020, 2021과 당시 진행 중이던 제안들을 살펴보았습니다. 해마다 발표되는 명세는 'ECMAScript 2026'처럼 연도를 붙여 부르고, 줄여서 'ES2026'이라고도 씁니다.
이 글에서는 먼저 2020년 이후 6년의 공백을 정리하고, 2020년 글에서 소개한 제안들이 어떤 결말을 맞았는지 확인합니다. 이어서 TC39 프로세스의 변화, 2026년 6월 승인된 ECMAScript 2026과 2027년 명세에 포함될 기능, 브라우저와 임베디드 엔진의 현황, JavaScript 상표권 분쟁, TypeScript 7을 살펴봅니다. 마지막으로 AI 코딩 도구가 JavaScript와 TypeScript를 쓰는 방식, 그리고 표준을 만드는 방식에 어떤 영향을 주고 있는지 정리합니다.
이 글의 내용은 2026년 10월을 기준으로 작성되었습니다. 글을 읽는 시점에 따라 글의 내용과 사실이 다를 수 있습니다.
2020년 이후 6년
2020년 글은 swyx의 The Third Age of JavaScript를 인용하며 2020년 이후를 JavaScript의 세 번째 주기로 보았습니다. 이 주기에서는 레거시를 정리하고 도구가 만든 레이어를 걷어 내는 일이 일어날 것이라고 했습니다. 구체적으로는 세 가지를 예상했습니다.
- CommonJS, AMD 같은 비표준 모듈이 ESM으로 전환될 것입니다.
- JavaScript 도구가 반드시 JavaScript로 작성될 필요는 없다는 흐름이 강해질 것입니다.
- TypeScript와 JavaScript가 서로 영향을 주며 함께 발전할 것입니다.
6년이 지난 지금 세 가지 모두 예상한 방향으로 진행되었습니다. 다만 속도와 모습은 예상과 달랐습니다.
ESM 전환은 생각보다 오래 걸렸습니다. 전환을 가로막던 가장 큰 장벽은 CommonJS 코드에서 ESM 패키지를 require()로 불러올 수 없다는 점이었고, 이 문제는 2024년 말이 되어서야 Node.js의 require(esm) 지원으로 풀렸습니다(참고: Node.js 22.12.0 (LTS)). 인기 npm 패키지의 모듈 형식을 정기적으로 집계하는 wooorm/npm-esm-vs-cjs 데이터에 따르면, ESM을 제공하는(ESM 전용이거나 ESM과 CommonJS를 함께 제공하는) 패키지 비율은 2021년 8월 7.8%에서 2024년 11월 25.9%, 2026년 6월 38.0%로 늘었습니다. 다만 2025년 12월부터 집계 대상 목록의 출처가 바뀌어 그 이전 수치와 바로 비교하기는 어렵습니다. Anthony Fu(Vue, Vite, Nuxt 코어 팀원이자 Vitest, Slidev, UnoCSS 제작자)는 2024년 말 수치를 근거로 새 패키지는 ESM 전용으로 내자고 제안하기도 했습니다(참고: Move on to ESM-only). 늘어난 것은 분명하지만 여전히 인기 패키지의 60% 이상은 ESM을 제공하지 않습니다.
그림 1 2021~2026년 인기 npm 패키지의 모듈 형식 비율 변화(esm과 dual의 합이 ESM 제공 비율, 출처: wooorm/npm-esm-vs-cjs)
도구의 언어 전환은 예상보다 빨랐습니다. 2020년 글에서 Babel과 비교했던 SWC 외에도 esbuild(Go), Rolldown과 Oxc(Rust), Biome(Rust) 같은 도구가 주류가 되었습니다. 2025년 3월에는 TypeScript 컴파일러 자체를 Go로 옮기겠다는 발표가 나왔고, 2026년 7월 Go 기반의 TypeScript 7.0이 정식 출시되었습니다(참고: A 10x Faster TypeScript, Announcing TypeScript 7.0). 이 글에서는 언어와 런타임에 관련된 부분만 짧게 살펴봅니다.
TypeScript는 '보편화'를 넘어 사실상 기본값이 되었습니다. Next.js, Astro, SvelteKit 같은 프레임워크가 프로젝트를 TypeScript로 생성하고, Node.js와 Deno, Bun 모두 .ts 파일을 별도 변환 없이 실행합니다.
예상하지 못한 변화도 있었습니다. 2020년 글은 AI에 대해 한 줄도 쓰지 않았습니다. 그러나 2026년 현재 JavaScript 생태계의 변화를 설명할 때 AI 코딩 도구를 빼놓기 어렵습니다. 이 내용은 "AI 도구로 인한 변화" 절에서 따로 정리합니다.
2020년 글에서 소개한 제안들의 결말
2020년 글의 "새로운 또는 완료되지 않은 제안들" 절에서 다룬 제안들은 다음과 같은 결말을 맞았습니다.
표 1 2020년 글에서 소개한 제안들의 현재 상태
| 제안 | 2020년 7월 | 2026년 10월 | 비고 |
|---|---|---|---|
Top-level await | Stage 3 | ES2022 | |
Class fields, private methods, static class features | Stage 3 | ES2022 | |
KV Storage 모듈 | 중단 | 비활성 | 브라우저 벤더의 투자 없음 |
Pipeline Operator | Stage 1 | Stage 2 | 2021년 8월 Hack 스타일로 Stage 2 |
Records & Tuples | Stage 1 | 철회 | 2025년 4월 철회, Composites로 이어짐 |
Temporal | Stage 2 | Stage 4 | 2026년 3월 Stage 4, ES2027 포함 예정 |
2020년 글에서 Stage 3에 머물러 있다고 했던 클래스 필드 관련 제안 세 개는 2021년 Stage 4에 올라 ES2022에 포함되었습니다(참고: 2021년 4월 회의록). Top-level await도 같은 명세에 포함되었습니다(참고: 2021년 5월 회의록).
KV Storage는 2020년 이후 다시 논의되지 않았습니다. WICG 저장소에는 "브라우저 벤더 중 누구도 투자하지 않아 비활성 상태"라는 안내만 남아 있습니다(참고: KV Storage).
Pipeline Operator는 2021년 8월 Hack 스타일 문법으로 Stage 2에 올랐습니다(참고: 2021년 8월 회의록). 2020년 글의 예시 코드처럼 함수 이름만 나열하는 F# 스타일 대신, 이전 단계의 값을 가리키는 토픽 토큰을 쓰는 방식입니다. 토큰으로 무엇을 쓸지는 아직 정해지지 않았고, 제안 문서는 임시로 %를 씁니다.
// 2020년 글의 예시 코드를 Hack 스타일로 다시 쓰면 let result = "hello" |> doubleSay(%) |> capitalize(%) |> exclaim(%);
이후로는 진척이 거의 없습니다. 2026년 9월 TC39 회의 안건에는 Stage 2.7 리뷰어가 정해지지 않은 Stage 2 제안 목록에 pipeline-operator가 올라 있었습니다(참고: TC39 116차 회의 안건). 이 회의 뒤인 10월에야 Ross Kirsling과 Christian Ulbrich가 리뷰어로 지정되었습니다(참고: tc39/proposals).
Records & Tuples는 2025년 4월 철회되었습니다. #{ }, #[ ] 형태의 새로운 원시 타입을 추가하는 방식이 위원회의 합의를 얻지 못했기 때문입니다. 같은 회의에서 그 문제의식을 이어받은 Composites 제안이 Stage 1에 올랐습니다(참고: 2025년 4월 회의록). Composites는 새 원시 타입 대신 같은 값을 가진 객체를 하나로 합치는(interning) 특수한 객체를 만듭니다.
const pos1 = Composite({ x: 1, y: 4 });
const pos2 = Composite({ x: 1, y: 4 });
pos1 === pos2; // true
const positions = new Set();
positions.add(pos1);
positions.has(pos2); // true
Composites는 2026년 9월 회의에서 Stage 2를 요청해 승인받았고(참고: TC39 116차 회의 안건, tc39/proposals 커밋), 현재 tc39/proposals 저장소의 Stage 2 목록에 올라 있습니다.
Temporal은 2026년 3월 Stage 4에 올랐습니다. 자세한 내용은 "ECMAScript 2027" 절에서 다룹니다.
ECMAScript 2022~2025 돌아보기
2020년 글 이후 ECMAScript 2022부터 2025까지 네 개의 명세가 발표되었습니다. 각 기능은 이미 여러 곳에서 소개되었으므로 여기서는 목록만 정리하고, 2026년 이후의 변화와 이어지는 부분만 짚습니다. 전체 목록은 Finished Proposals에서 확인할 수 있습니다.
표 2 ECMAScript 2022~2025의 주요 기능
| 명세 | 주요 기능 |
|---|---|
ES2022 | Class fields, private methods, |
ES2023 |
|
ES2024 |
|
ES2025 | Iterator helpers, Set 메서드, Import Attributes와 JSON 모듈, RegExp modifiers, |
네 해의 명세를 묶어 보면 큰 흐름은 세 가지입니다.
- 클래스 문법의 완성: 2019년 글부터 소개해 온 클래스 필드와 private 메서드가 ES2022에서 표준이 되었습니다. 클래스 문법에 더해질 다음 기능인 Decorators는 아래 "TC39의 변화"에서 다룹니다.
- 유틸리티 기능의 표준화: ES2023의 Change Array by Copy(
toSorted(),toReversed(),with())와 ES2024의Object.groupBy()는 Lodash 같은 유틸리티 라이브러리가 맡던 일을 언어로 가져왔습니다. ES2026의 Upsert(getOrInsert())도 같은 방향입니다. 다만 Upsert는 키가 없으면 원래 Map에 값을 삽입하므로, 원본을 유지하는 API는 아닙니다. - 이터레이터와 모듈의 확장: ECMAScript 2025의 Iterator helpers는 이터레이터에
map,filter,take같은 지연 평가 메서드를 붙였습니다. ES2026의 Iterator sequencing과 ES2027의 이터레이터 확장은 모두 이 위에 만들어졌습니다. 같은 해 표준이 된 Import Attributes(with { type: "json" })는 2020년 글에서 플래그가 필요하다고 소개한 JSON import를 표준 문법으로 만들었고, 진행 중인 Import Text 제안의 바탕이 되었습니다.
// ES2025 Iterator helpers: ES2026, ES2027 이터레이터 기능의 바탕
function* naturals() {
let i = 1;
while (true) yield i++;
}
naturals().filter(n => n % 2 === 0).map(n => n * n).take(3).toArray(); // [4, 16, 36]
// ES2025 Import Attributes
import config from "./config.json" with { type: "json" };
TC39의 변화
2020년 글에서는 TC39가 외부 전문가를 초빙하고 Discourse 포럼을 열어 논의 과정을 투명하게 만들고 있다고 소개했습니다. 그 뒤 6년 동안 가장 눈에 띄는 변화는 제안의 진행 단계 자체가 바뀌었다는 점입니다.
Stage 2.7의 신설
TC39는 2023년 11월 회의에서 Stage 2와 Stage 3 사이에 새로운 단계를 추가하기로 합의했습니다. 이름을 두고는 2.5, 2.7 같은 소수 번호부터 목적을 드러내는 이름까지 여러 의견이 나왔습니다. 한 위원이 2와 3 사이에 수학 상수 e(2.718...)가 있다며 "Stage e"를 제안하는 장면도 회의록에 남아 있습니다(참고: 2023년 11월 회의록). 결국 이 단계는 Stage 2.7이라는 이름을 얻었고, 2024년 2월 회의부터 "for Stage 2.7" 안건이 올라오기 시작했습니다(참고: 2024년 2월 회의록).
회의록에 따르면 새 단계의 목적은 Stage 3으로 가기 전에 테스트를 작성하고 필요한 경험을 쌓는 구간을 따로 두는 것이었습니다. 이전에는 명세가 완성되면 바로 Stage 3에 올랐고, Test262 테스트는 Stage 4 전까지만 갖추면 되었습니다. 그래서 Stage 3의 '구현을 시작해도 된다'는 신호와 실제 테스트 준비 상태 사이에 차이가 생겼습니다. 현재 공식 프로세스는 Stage 3의 진입 조건으로 충분한 테스트와 적절한 사전 구현 경험을 요구하며, Stage 2.7은 명세가 원칙적으로 승인되었지만 테스트와 검증이 끝나지 않은 상태를 표시합니다.
표 3 현재 TC39 프로세스의 단계(출처: The TC39 Process)
| 단계 | 의미 | 주요 진입 조건 |
|---|---|---|
Stage 0 | 아이디어 | 없음 |
Stage 1 | 문제와 해결 방향을 검토 중 | 챔피언(champion, 제안을 책임지고 이끄는 위원) 지정, 문제 정의 |
Stage 2 | 해결책을 정하고 명세를 다듬는 중 | 초기 명세 문서 |
Stage 2.7 | 원칙적으로 승인, 검증 중 | 완성된 명세, 리뷰어와 에디터의 승인 |
Stage 3 | 구현 권장 | 충분한 테스트와 적절한 사전 구현 경험 |
Stage 4 | 표준에 포함 | 호환되는 구현 두 개, 실제 출시 경험, ECMA-262 PR 승인 |
Stage 2.7에서는 테스트, 구현, 사용 경험에서 나오는 피드백 외에는 위원회가 변경을 요구하지 않습니다. 이 단계에서 Test262 테스트를 작성하고, 필요하면 명세를 따르는 프로토타입을 만들어 구현 가능성을 확인합니다.
조건부 진행과 단계 후퇴
최근 회의록에서 자주 보이는 표현이 조건부 진행(conditional advancement)입니다. 남은 작업이 테스트 리뷰나 에디터 승인 정도일 때, 그 작업이 끝나는 것을 조건으로 다음 단계에 올리는 방식입니다.
Explicit Resource Management(using 선언)가 대표적입니다. 이 제안은 Chrome, Firefox, XS에 구현된 상태로 2025년 5월 조건부 Stage 4를 받았습니다(참고: 2025년 5월 회의록). 그러나 남은 Test262 리뷰가 늦어지면서 조건이 모두 충족된 것은 1년이 지난 2026년 5월이었습니다(참고: 2026년 5월 회의록). 2026년 3월 회의에서 Mozilla의 Daniel Minor는 "앞으로 조건부 진행에 더 신중해야 한다"고 말했고, 제안자인 Ron Buckton은 Test262 리뷰어가 부족한 것이 가장 큰 걸림돌이었다고 설명했습니다(참고: 2026년 3월 회의록). 이 회의의 결론은 "Test262에는 더 많은 리뷰어가 필요하다" 한 줄이었습니다.
반대 방향의 이동도 있었습니다. Decorators는 2022년 Stage 3에 오른 뒤 TypeScript 5.0이 구현을 내놓았지만, 브라우저 엔진은 아직 출시하지 않았습니다(참고: 2022년 3월 회의록, Announcing TypeScript 5.0). 2026년 5월 회의에서 엔진 쪽은 테스트가 불완전하고 명세에 문제가 있으며 구현이 복잡하다는 우려를 제기했고, Decorators와 Decorator Metadata는 Stage 2.7로 되돌아갔습니다(참고: 2026년 5월 회의록). Stage 2.7이 생기면서 구현 단계에서 문제가 드러난 제안을 Stage 2까지 내리지 않고 검증 단계로 되돌릴 수 있게 되었습니다.
ECMAScript 2026
ECMAScript 2026(ECMA-262 17번째 에디션)은 2026년 3월 TC39의 승인을 거쳐, 6월 30일 Ecma 총회에서 공식 승인되었습니다(참고: es2026 릴리스, Ecma International approves new standards). 포함된 기능은 일곱 개입니다.
그림 2 tc39.es에 공개된 ECMAScript 2026 명세 첫 화면(출처: ECMAScript 2026 Language Specification)
표 4 ECMAScript 2026 기능과 지원 현황(출처: MDN browser-compat-data 8.1.3, 2026년 9월 24일)
| 기능 | Chrome | Firefox | Safari | Node.js |
|---|---|---|---|---|
| 121 | 115 | 16.4 | 22.0 |
| 134 | 138 | 18.4(부분 지원) | 24.3 |
| 147 | 137 | 26.2 | 미지원 |
| 140 | 133 | 18.2 | 25.0 |
Iterator sequencing( | 146 | 147 | 26.4 | 26.0 |
JSON.parse source text access( | 114 | 135 | 18.4 | 21.0 |
Upsert( | 145 | 144 | 26.2 | 26.0 |
표를 보면 Stage 4에 오르기 전부터 브라우저에 출시된 기능이 많습니다. Array.fromAsync는 2024년 1월 Chrome 121에 이미 출시되었습니다(참고: Chrome Platform Status: Array.fromAsync). 2025년 5월 회의에서는 "세 명의 에디터가 명세 PR에 서명하면 Stage 4"라는 조건부 Stage 4에 합의했습니다(참고: 2025년 5월 회의록). 명세에 내장 async 함수를 기술하는 방법을 먼저 정리해야 했기 때문에, 명세 PR(ecma262#3581)은 2025년 12월에야 병합되었고, 그 결과 ES2025가 아닌 ES2026에 포함되었습니다(참고: 2026년 1월 회의록의 에디터 보고). Stage 4는 두 개 이상의 엔진이 출시한 경험을 요구하므로, 명세 연도는 기능을 쓸 수 있게 된 시점보다 늦게 따라옵니다.
Array.fromAsync
Array.from()의 비동기 버전입니다. 비동기 이터러블을 배열로 모을 때 for await 반복문을 쓰지 않아도 됩니다.
async function* fetchPages() {
for (let page = 1; page <= 3; page++) {
yield await fetch(`/api/items?page=${page}`).then(r => r.json());
}
}
const pages = await Array.fromAsync(fetchPages());
Error.isError
값이 진짜 Error 객체인지 검사합니다. instanceof Error는 iframe이나 Node.js의 vm 모듈처럼 다른 실행 환경(realm)에서 만든 오류를 판별하지 못합니다. 대안으로 쓰던 Object.prototype.toString 검사는 Symbol.toStringTag로 속일 수 있습니다. Error.isError()는 내부 슬롯을 검사하므로 이런 경우에도 정확합니다.
const iframeError = new frames[0].Error("boom");
iframeError instanceof Error; // false
Error.isError(iframeError); // true
Error.isError({ name: "Error", message: "fake" }); // false
Math.sumPrecise
중간 합산에서 생기는 정밀도 손실을 피하면서 숫자 목록의 합을 구합니다. reduce로 더하면 중간 결과에서 정밀도가 사라지지만, Math.sumPrecise()는 전체를 정확하게 계산한 뒤 마지막에 한 번만 반올림합니다.
[1e20, 0.1, -1e20].reduce((a, b) => a + b, 0); // 0 Math.sumPrecise([1e20, 0.1, -1e20]); // 0.1
입력값 자체의 부동소수점 표현 오차까지 없애는 것은 아닙니다. 예를 들어 Math.sumPrecise([0.1, 0.2])의 결과는 여전히 0.30000000000000004입니다(참고: MDN: Math.sumPrecise).
Uint8Array와 base64, hex 변환
바이너리 데이터를 base64나 16진수 문자열로 바꾸려면 그동안 btoa()와 String.fromCharCode()를 조합하거나 라이브러리를 써야 했습니다. 이제 Uint8Array에 변환 메서드가 내장되었습니다.
const bytes = new TextEncoder().encode("Hello");
bytes.toBase64(); // "SGVsbG8="
bytes.toHex(); // "48656c6c6f"
Uint8Array.fromBase64("SGVsbG8="); // Uint8Array [72, 101, 108, 108, 111]
Iterator sequencing
여러 이터레이터를 하나로 이어 붙이는 Iterator.concat()입니다. 이전에는 제너레이터 함수에서 yield*를 반복해서 써야 했습니다.
const combined = Iterator.concat( [1, 2].values(), new Set([3, 4]), [5], ); combined.toArray(); // [1, 2, 3, 4, 5]
JSON.parse source text access
JSON.parse()의 값 변환 함수(reviver)는 원시 값을 처리할 때 세 번째 인자의 context 객체에 담긴 source 속성으로 원본 텍스트를 읽을 수 있습니다. reviver가 호출될 때는 Number 변환이 이미 끝났지만, 정밀도를 잃은 value 대신 원본 텍스트로 BigInt를 만들 수 있습니다. 반대로 직렬화할 때는 JSON.rawJSON()으로 원본 텍스트를 그대로 내보냅니다.
JSON.parse("999999999999999999", (key, value, { source }) => BigInt(source));
// 999999999999999999n
JSON.stringify({ id: JSON.rawJSON("999999999999999999") });
// '{"id":999999999999999999}'
Upsert
Map과 WeakMap에 키가 없을 때만 값을 넣고, 있으면 기존 값을 돌려주는 메서드입니다. has()로 확인하고 set()한 뒤 다시 get()하던 세 단계를 한 번에 처리합니다.
const counts = new Map();
for (const word of words) {
counts.set(word, counts.getOrInsert(word, 0) + 1);
}
// 기본값 계산 비용이 클 때는 콜백을 넘긴다
const cache = new Map();
cache.getOrInsertComputed(key, k => expensiveCompute(k));
ECMAScript 2027
ECMAScript 2027 명세는 2027년 6월 승인될 예정입니다. 2026년 10월 현재 Finished Proposals에 2027년 발행 예정으로 올라 있는 기능은 Temporal, Explicit Resource Management, Atomics.pause, Joint Iteration, Iterator Chunking, Iterator Includes, Iterator Join입니다. 이 중 Iterator Chunking, Includes, Join은 2026년 9월 회의에서 Stage 4에 올라왔으며, 2026년 10월 현재 회의록은 아직 공개되지 않았습니다(참고: TC39 116차 회의 안건).
Temporal
2020년 글에서 Stage 2로 소개한 Temporal은 2026년 3월 Stage 4에 올랐습니다. 2017년 처음 제안된 지 9년 만입니다. 회의에서 Stage 4를 요청한 Igalia의 Philip Chimento는 "드디어 'Temporal for Stage Four'라는 제목의 슬라이드를 보여 줄 수 있게 되어 기쁘다"며 발표를 시작했고, 지지 발언이 이어진 끝에 Stage 4가 승인되었습니다(참고: 2026년 3월 회의록).
그림 3 2026년 3월 TC39 회의의 Temporal Stage 4 요청 발표 자료 첫 장(출처: Temporal for Stage 4)
Stage 4 요청 당시 Firefox는 약 1년 전부터, Chrome을 비롯한 V8 기반 브라우저는 몇 달 전부터 Temporal을 플래그 없이 출시한 상태였습니다. 발표 직전 2주 사이에 Ladybird의 JavaScript 엔진도 Test262 Temporal 테스트를 거의 100% 통과하게 되었다는 소식이 함께 전해졌습니다(참고: 2026년 3월 회의록). 현재 지원 현황은 다음과 같습니다.
표 5 Temporal 지원 현황(출처: MDN browser-compat-data 8.1.3)
| 환경 | 지원 버전 |
|---|---|
Firefox | 139(2025년 5월) |
Chrome, Edge | 144 |
Safari | Technology Preview |
Node.js | 26.0(기본 활성) |
Deno | 2.7 |
규모도 큽니다. Bloomberg의 Jason Williams에 따르면 Temporal은 ES2015 이후 ECMAScript에 더해진 가장 큰 기능입니다. Test262에 추가한 테스트도 글을 쓴 시점 기준 4,496개로 단일 제안 중 가장 많고, 명세 분량은 ECMA-402(국제화 API) 전체보다 큽니다(참고: Temporal: The 9-Year Journey to Fix Time in JavaScript). V8의 Temporal 구현은 C++로 새로 작성하지 않고, Rust로 작성된 JavaScript 엔진 Boa 팀이 만든 temporal_rs 라이브러리를 가져다 씁니다. 이 라이브러리는 V8 외에도 Boa, Kiesel 같은 엔진이 함께 쓰고 있습니다(참고: Temporal_rs is here!). 같은 Bloomberg 글은 Brendan Eich의 말을 인용해, Date가 Java의 Date 코드를 C로 그대로 옮긴 것이었다는 배경도 소개합니다.
// 현재 날짜와 시간(시간대 포함)
const now = Temporal.Now.zonedDateTimeISO("Asia/Seoul");
// 날짜 계산: 1월 31일에 한 달을 더하면?
const date = Temporal.PlainDate.from("2026-01-31");
date.add({ months: 1 }).toString(); // "2026-02-28"
// 두 날짜 사이의 기간
const start = Temporal.PlainDate.from("2020-07-01");
const end = Temporal.PlainDate.from("2026-09-30");
start.until(end, { largestUnit: "years" }).toString(); // "P6Y2M29D"
// 시간대 변환
const meeting = Temporal.ZonedDateTime.from("2026-10-01T10:00[Asia/Seoul]");
meeting.withTimeZone("America/New_York").toString();
// "2026-09-30T21:00:00-04:00[America/New_York]"
Temporal.PlainDate, PlainTime, PlainDateTime, ZonedDateTime, Instant, Duration처럼 용도별로 타입이 나뉘어 있고, 모든 객체는 불변입니다. 2020년 글에서 소개한 기본 원칙이 그대로 유지되었습니다.
그림 4 Temporal의 타입 구성(출처: Temporal 문서)
라이브러리 생태계도 반응했습니다. date-fns는 "You don't need date-fns"라는 글에서 Temporal로 옮겨 가는 방법을 안내하고 있고, eslint-plugin-unicorn에는 Date 대신 Temporal을 권장하는 prefer-temporal 규칙이 추가되었습니다.
Explicit Resource Management
파일 핸들, 데이터베이스 연결, 락처럼 사용 후 반드시 정리해야 하는 자원을 다루는 문법입니다. C#의 using, Python의 with, Java의 try-with-resources와 같은 역할을 합니다. using으로 선언한 변수는 블록을 벗어날 때 [Symbol.dispose]() 메서드가 자동으로 호출됩니다. 비동기 정리가 필요하면 await using과 [Symbol.asyncDispose]()를 씁니다.
class TempFile {
constructor(path) { this.handle = openSync(path); }
[Symbol.dispose]() { closeSync(this.handle); }
}
function processFile() {
using file = new TempFile("/tmp/data");
// ... file 사용
} // 블록을 벗어나면 file[Symbol.dispose]()가 호출된다
async function query() {
// pool.connect()가 Symbol.asyncDispose를 구현한 연결을 반환한다고 가정한다
await using conn = await pool.connect();
return await conn.query("SELECT 1"); // 쿼리가 끝난 뒤 연결을 정리한다
} // conn[Symbol.asyncDispose]()를 await
여러 자원을 한꺼번에 관리하는 DisposableStack, AsyncDisposableStack과, 정리 중 발생한 오류와 원래 오류를 함께 담는 SuppressedError도 추가되었습니다. Chrome 134, Firefox 141, Node.js 24에서 쓸 수 있고 Safari는 Technology Preview에서 쓸 수 있습니다(참고: MDN: using). TypeScript는 5.2부터 이 문법을 지원했습니다(참고: Announcing TypeScript 5.2).
이터레이터 확장
ES2025의 Iterator helpers 이후 이터레이터를 다루는 제안이 연이어 Stage 4에 올랐습니다. 모두 Michael Ficarra나 Kevin Gibbons가 챔피언을 맡았습니다(참고: Finished Proposals).
// Joint Iteration: 여러 이터러블을 묶어서 순회
Iterator.zip([[0, 1, 2], [3, 4, 5]]).toArray();
// [[0, 3], [1, 4], [2, 5]]
Iterator.zipKeyed({ a: [0, 1, 2], b: [3, 4, 5] }).toArray();
// [{ a: 0, b: 3 }, { a: 1, b: 4 }, { a: 2, b: 5 }]
// Iterator Chunking: 고정 크기 묶음과 슬라이딩 윈도
const digits = () => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].values();
digits().chunks(3).toArray(); // [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
digits().windows(2).take(3).toArray(); // [[0, 1], [1, 2], [2, 3]]
// Iterator Includes, Iterator Join
function* gen() { yield 1; yield 3; }
gen().includes(3); // true
gen().join(", "); // "1, 3"
Iterator.zip은 Chrome 153, Firefox 148이 지원합니다(참고: MDN: Iterator.zip()).
이 밖에 멀티스레드 코드에서 스핀 락을 돌 때 CPU에 대기 중임을 알리는 Atomics.pause()도 2027년 명세에 포함될 예정입니다(참고: MDN: Atomics.pause(), Finished Proposals).
진행 중인 제안들
Stage 3 이하의 제안 중 앞으로 명세에 들어올 가능성이 높거나 눈여겨볼 만한 것을 살펴봅니다. 단계 정보는 2026년 10월 tc39/proposals 기준입니다. 아직 완료되지 않은 제안이므로 문법과 세부 내용은 바뀔 수 있습니다.
표 6 주요 진행 중 제안
| 단계 | 제안 |
|---|---|
Stage 3 | Deferring Module Evaluation( |
Stage 2.7 | Decorators, Decorator Metadata, ShadowRealm, Immutable ArrayBuffer, Import Bytes, Deferred Re-exports, ESM Phase Imports |
Stage 2 | Pipeline Operator, AsyncContext, Async Iterator helpers, |
Stage 1 | Type Annotations, Signals 외 다수 |
import defer(Stage 3)
모듈을 미리 불러오되 실제 평가(실행)는 처음 접근할 때까지 미루는 문법입니다. 큰 애플리케이션에서 초기화 비용을 줄이려고 import()나 함수 안의 require()로 바꾸던 코드를, 동기 코드 구조를 유지한 채 최적화할 수 있습니다. 2025년 2월 Stage 3에 올랐고(참고: 2025년 2월 회의록), 2026년 7월 회의에서는 모든 주요 브라우저가 구현을 시작했으며 JavaScriptCore와 V8은 플래그 뒤에서 사용할 수 있다고 보고되었습니다(참고: 2026년 7월 회의록).
import defer * as heavy from "./heavy-module.js";
button.addEventListener("click", () => {
// heavy 모듈은 이 시점에 처음 평가된다
heavy.run();
});
모듈은 미리 다운로드되고 파싱까지 끝나므로, 평가 시점에 네트워크 지연이 생기지 않습니다. 다만 top-level await를 쓰는 비동기 의존성은 지연할 수 없어 미리 평가됩니다.
Import Text(Stage 3)
ES2025의 JSON 모듈에 이어, 텍스트 파일을 문자열로 불러오는 type: "text" 속성입니다. 2026년 3월 Stage 2.7을 거쳐 같은 회의에서 Stage 3에 올랐습니다(참고: 2026년 3월 회의록). 번들러들이 ?raw 같은 비표준 방식으로 제공하던 기능이 표준 문법으로 정리됩니다. 바이너리 파일을 Uint8Array로 불러오는 Import Bytes는 Stage 2.7입니다.
import shader from "./shader.glsl" with { type: "text" };
import template from "./email.html" with { type: "text" };
Await Dictionary(Stage 3)
Promise.all()은 배열 순서로 결과를 돌려주기 때문에, 구조 분해(destructuring)를 할 때 순서를 잘못 맞추는 실수가 생기기 쉽습니다. Await Dictionary는 객체의 키로 결과를 받는 Promise.allKeyed()와 Promise.allSettledKeyed()를 추가합니다. 2026년 7월 Stage 3에 올랐습니다(참고: 2026년 7월 회의록).
// 순서에 의존하는 Promise.all
{
const [shape, color, mass] = await Promise.all([getShape(), getColor(), getMass()]);
}
// 키로 받는 Promise.allKeyed
{
const { shape, color, mass } = await Promise.allKeyed({
shape: getShape(),
color: getColor(),
mass: getMass(),
});
}
AsyncContext(Stage 2)
비동기 호출 흐름을 따라 값을 전달하는 기능입니다. Node.js의 AsyncLocalStorage를 언어 수준으로 표준화하는 제안으로, 요청 단위의 추적 ID나 로깅 컨텍스트, 프레임워크의 상태 전파에 쓰입니다. 언어 쪽 설계는 정리되었지만, 이벤트 리스너나 타이머 같은 웹 API에서 컨텍스트를 어떻게 전파할지에 대한 논의가 이어지고 있습니다. 2026년 5월 회의에서는 전파 규칙을 렉시컬 스코프처럼 단순하게 유지해야 한다는 피드백이 나왔고, 제안자도 이를 받아들였습니다(참고: 2026년 5월 회의록).
const requestId = new AsyncContext.Variable();
requestId.run("req-42", async () => {
await db.query("...");
log(requestId.get()); // "req-42"
});
Signals(Stage 1)
Angular, Vue, Solid, Preact, Svelte 등이 각자 구현해 온 반응형 상태 관리 원시 기능(signal)을 표준화하려는 제안입니다. 2024년 4월 Stage 1에 오른 뒤 아직 단계가 바뀌지 않았습니다(참고: 2024년 4월 회의록).
엔진과 런타임
웹 엔진의 다양성, 그 이후
2020년 글에서는 Microsoft Edge의 Chromium 전환 이후 웹 엔진의 다양성이 줄어들고 있다고 우려했습니다. 6년이 지난 지금 그 흐름은 더 뚜렷해졌습니다. StatCounter의 2026년 8월 전 세계 브라우저 점유율은 Chrome 69.28%, Safari 15.92%, Edge 5.39%, Firefox 2.98%, Samsung Internet 2.02%, Opera 1.93%입니다(참고: StatCounter Browser Market Share). Chrome, Edge, Samsung Internet, Opera, Brave 같은 Chromium 계열만 합해도 80% 가까이 됩니다. Firefox의 Gecko는 3%를 밑돕니다.
그림 5 2026년 8월 전 세계 브라우저 점유율(출처: StatCounter)
이런 상황에서 눈에 띄는 프로젝트가 Ladybird입니다. SerenityOS의 일부로 시작한 Ladybird는 Chromium이나 Gecko, WebKit 코드를 쓰지 않고 렌더링 엔진과 JavaScript 엔진(LibJS)을 처음부터 새로 만들고 있는 브라우저입니다. 검색 계약이나 광고 없이 후원금으로만 운영되는 비영리 단체(501(c)(3))이며, 2026년 중 Linux와 macOS용 첫 알파 버전 출시를 목표로 하고 있습니다. 2026년 2월에는 Rust를 C++의 후속 언어로 채택하고 LibJS부터 옮기기 시작했다고 발표했는데(참고: Ladybird adopts Rust), 이 이식 작업에 AI 코딩 도구가 쓰였습니다. 이 내용은 "AI 도구로 인한 변화" 절에서 다룹니다.
그림 6 Ladybird 홈페이지(출처: Ladybird)
표 7 주요 브라우저 렌더링 엔진과 JavaScript 엔진(2026년)
| 브라우저/프로젝트 | 렌더링 엔진 | JavaScript 엔진 |
|---|---|---|
Chrome, Edge, Opera, Samsung Internet 등 | Blink | |
Safari | WebKit | |
Firefox | Gecko | |
Ladybird | LibWeb |
2020년 표와 비교하면 IE와 구 Edge가 제외되고 Ladybird가 추가되었습니다. Servo는 Mozilla를 떠나 Linux Foundation Europe으로 옮겨 독립 프로젝트로 개발이 이어지고 있습니다(참고: About Servo).
엔진 간 차이를 줄이려는 노력도 계속되고 있습니다. 2026년 6월 WWDC에서 공개된 Safari 27 베타는 ECMAScript 모듈 로더를 표준에 맞게 C++로 다시 작성해, 모듈이 완전히 평가되기 전에 export에 접근하는 실행 순서 문제를 고쳤습니다(참고: News from WWDC26: WebKit in Safari 27 beta). 브라우저마다 달랐던 모듈 동작이 맞춰지는 것은 ESM 전환 관점에서도 의미가 있습니다.
브라우저 밖의 엔진
표 7의 엔진은 모두 브라우저에 들어갑니다. 브라우저 밖에서는 크기와 시작 시간을 우선하는 엔진이 따로 자리를 잡았습니다.
대표적인 엔진이 Fabrice Bellard가 2019년 7월 공개한 QuickJS입니다. C 파일 몇 개로 이뤄져 외부 의존성이 없고, hello world 프로그램이 x86 코드 367KiB이며 런타임 인스턴스 하나를 만들고 정리하는 데 300마이크로초가 걸리지 않습니다. JIT 컴파일러 없이 인터프리터로만 동작하지만, Test262에서 ES2025 기능 테스트를 거의 100% 통과합니다. 2026년 6월 4일 릴리스는 Map의 Upsert 메서드, Math.sumPrecise(), Uint8Array의 base64와 hex 변환 같은 ES2026 기능을 추가했고, 작은 블록용 메모리 할당기를 추가해 bench-v8에서 11%, 미세 최적화로 30% 빨라졌습니다(참고: QuickJS Changelog).
QuickJS는 2021년 3월 이후 2년 넘게 새 릴리스가 없었습니다. 그사이 2023년 11월 libuv 메인테이너인 Ben Noordhuis와 Saúl Ibarra Corretgé가 개발을 이어 가려고 quickjs-ng 포크를 시작했습니다. 원본도 2023년 12월부터 릴리스를 재개해, 지금은 두 프로젝트가 함께 개발되고 있습니다. quickjs-ng는 2026년 9월 v0.17.0을 냈습니다.
작고 빨리 뜨는 엔진은 서버리스와 임베디드 환경에 맞습니다. AWS의 실험적 서버리스 런타임 LLRT(Low Latency Runtime)는 Rust로 작성하고 엔진으로 QuickJS를 써서, 함수가 처음 실행될 때의 지연을 줄이는 것을 목표로 합니다. 이 밖에 React Native의 기본 엔진인 Hermes, Rust로 작성한 임베디드 엔진 Boa(2026년 8월 v0.22)도 브라우저 밖에서 쓰이는 엔진입니다. V8과 JavaScriptCore가 웹의 대부분을 실행하는 동안, 다른 프로그램 안에 넣어 쓰는 엔진 쪽은 오히려 선택지가 늘었습니다.
JavaScript 상표권
이 연재의 1편은 매번 "JavaScript(ECMAScript)"라는 제목을 달고 있습니다. 언어의 명세 이름이 JavaScript가 아니라 ECMAScript인 이유 중 하나는 상표권입니다. 'JavaScript'는 미국에서 Oracle이 보유한 등록 상표(등록번호 2416017)이고, Oracle은 Sun Microsystems를 인수하면서 이 상표를 넘겨받았습니다(참고: javascript.tm). 명세를 만드는 Ecma와 TC39가 'JavaScript 명세'라는 이름을 쓰지 않고, 콘퍼런스나 서적이 이름에 JavaScript를 넣을 때 법적 위험을 따져야 했던 것도 이 때문입니다.
공개서한에서 취소 심판까지
2024년 9월, Node.js와 Deno를 만든 Ryan Dahl이 Oracle에 상표를 포기해 달라는 공개서한을 javascript.tm에 올렸습니다. JavaScript를 만든 Brendan Eich도 서명자 중 한 명이었고, 서명자는 2만 7천 명을 넘었습니다. Oracle이 응하지 않자 Deno Land는 2024년 11월 22일 미국 특허상표청 상표심판원(TTAB, Trademark Trial and Appeal Board)에 상표 등록 취소 심판을 청구했습니다(참고: Deno v. Oracle: Canceling the JavaScript Trademark).
그림 7 Oracle에 JavaScript 상표 포기를 요구하는 javascript.tm 첫 화면(출처: javascript.tm)
청구 사유는 세 가지였습니다.
- 보통명칭화(genericness): Deno는 'JavaScript'가 ECMA-262 명세가 정의하는 프로그래밍 언어를 가리키는 일반 명칭이 되었고, 특정 회사의 상품을 가리키지 않는다고 주장했습니다.
- 불사용(abandonment): Deno는 Oracle이 이 상표를 상거래에서 상표로 사용하지 않고 있다고 주장했습니다.
- 사기(fraud): Deno는 Oracle이 2019년 상표 갱신 때 사용 증거로 Oracle과 무관한 Node.js 웹사이트 화면을 제출했다고 주장했습니다.
2025년 6월 18일 TTAB는 Oracle의 일부 기각 신청을 받아들여 사기 주장을 기각했습니다. Deno는 사기 주장을 보완하는 대신 나머지 두 사유로 심판을 이어 가기로 했습니다(참고: JavaScript™ Trademark Update). Oracle은 2025년 8월 6일 처음으로 상표의 유효성에 대해 답변서를 냈고, 'JavaScript'가 보통명칭이라는 주장을 부인했습니다. Deno는 증거 조사(discovery) 단계에 들어가며 일반인이 'JavaScript'와 Oracle을 연결해 인식하는지 확인하는 설문 조사, 전문가 증언 비용 등을 위해 20만 달러 모금을 시작했습니다. 남는 금액은 OpenJS Foundation에 기부한다고 밝혔습니다(참고: Help Us Raise $200k to Free JavaScript from Oracle).
2026년 10월 현재
TTAB 사건 기록(취소 심판 번호 92086835)을 보면, 2025년 10월 보호 명령(protective order)에 합의한 뒤 2025년 12월부터 2026년 7월까지 Oracle이 Deno의 동의를 받아 기한 연장을 네 번 신청했고 모두 받아들여졌습니다. 그리고 2026년 8월 28일 Oracle이 "합의 협상 진행 중"을 이유로 낸 절차 중지 신청이 Deno의 동의하에 받아들여져, 현재 사건은 중지(Suspended) 상태입니다(참고: TTABVUE: Cancellation No. 92086835).
그림 8 JavaScript 상표 취소 심판의 상태와 2025년 6월 이후 진행 기록(일부 발췌, 출처: TTABVUE: Cancellation No. 92086835)
양측이 어떤 조건으로 협상하고 있는지는 공개되지 않았습니다. 합의가 상표 포기로 이어질지, 사용 허락 같은 다른 형태가 될지는 알 수 없습니다. 다만 Ryan Dahl이 밝힌 것처럼 Deno가 심판에서 이기거나 Oracle이 자발적으로 상표를 포기하면, 명세가 'JavaScript 명세'라는 이름을 쓰고 누구나 사용 허락 걱정 없이 JavaScript라는 이름을 쓸 수 있게 됩니다. 다음 회차 글의 제목에서 괄호 속 ECMAScript가 사라질 수 있을지 지켜볼 만합니다.
TypeScript
2020년 글은 "TypeScript의 사용은 더 이상 새로운 일이 아닐 정도로 보편화되었다"고 썼습니다. 2026년의 TypeScript는 JavaScript 생태계의 기본 언어에 가깝습니다. State of JS 2025의 JavaScript와 TypeScript 작성 시간 배분 문항에서 TypeScript에 쓰는 시간의 비중은 평균 77%였고, 작성 시간을 모두 TypeScript에 쓴다는 응답이 가장 많았습니다(참고: State of JS 2025: Usage). 2026년에는 TypeScript 자체가 크게 바뀌었습니다. 컴파일러가 Go로 이식되었고, 런타임이 TypeScript를 직접 실행하면서 '타입만 지우면 JavaScript가 되는' 문법이 중요해졌습니다.
그림 9 JavaScript와 TypeScript 작성 시간 중 TypeScript 비중에 대한 State of JS 2025 응답 분포(출처: State of JS 2025: Usage)
TypeScript 7: Go로 옮긴 컴파일러
2025년 3월 TypeScript 수석 설계자 Anders Hejlsberg는 TypeScript 컴파일러와 언어 서비스를 Go로 이식하고 있다고 발표했습니다(참고: A 10x Faster TypeScript). VS Code 코드베이스 기준으로 빌드 시간이 77.8초에서 7.5초로 줄었고, 에디터의 프로젝트 로딩 시간은 9.6초에서 약 1.2초로 줄었습니다. 기존 JavaScript 구현에는 Strada, 새 Go 구현에는 Corsa라는 코드명이 붙었습니다.
TypeScript 팀은 Go를 고른 이유로 기존 코드와 구조가 비슷하다는 점을 들었습니다. 새로 설계하는 재작성(rewrite)이 아니라 같은 구조를 다른 언어로 옮기는 이식(port)이므로, 두 코드베이스를 한동안 함께 유지하며 변경 사항을 쉽게 옮길 수 있는 언어가 필요했다는 설명입니다(참고: Why Go?).
이식은 두 단계로 진행되었습니다.
- TypeScript 6.0(2026년 3월 23일): 기존 JavaScript 코드베이스로 만든 마지막 버전입니다. 7.0으로 넘어가기 위한 준비 단계로, 기본값을 최신 환경에 맞게 바꾸고 오래된 옵션을 폐기 예정으로 지정했습니다(참고: Announcing TypeScript 6.0).
- TypeScript 7.0(2026년 7월 8일): Go로 이식한 컴파일러입니다. 네이티브 코드와 공유 메모리 기반 병렬 처리로 전체 빌드가 보통 8~12배 빨라졌습니다(참고: Announcing TypeScript 7.0).
그림 10 프로젝트별 TypeScript 6과 7의 컴파일 시간 비교(출처: Announcing TypeScript 7.0)
표 8 TypeScript 6.0의 주요 기본값 변경과 폐기(출처: Announcing TypeScript 6.0)
| 항목 | 이전 | 6.0 |
|---|---|---|
|
|
|
|
|
|
| 고정된 이전 버전 | 지원하는 최신 ES 버전(현재 |
| 모든 |
|
폐기 예정 | - |
|
제거 | - |
|
TypeScript 7.0에서는 6.0에서 폐기 예정으로 지정한 옵션이 오류가 됩니다. 모듈 기본값이 CommonJS에서 ESM으로 바뀌고 ES5 출력이 사라졌다는 점은, 이 글의 첫 절에서 다룬 ESM 전환과 레거시 정리가 TypeScript에서도 마무리 단계에 들어섰다는 신호로 볼 수 있습니다.
TypeScript 7.0 발표에 따르면 Slack은 CI의 타입 검사 시간이 약 7.5분에서 1.25분으로 줄었습니다. 다만 7.0은 아직 안정된 프로그래밍 API를 제공하지 않아, 컴파일러 API에 의존하는 Vue, Svelte, Astro, MDX 같은 언어 플러그인 기반 워크플로는 당분간 7.0을 온전히 쓰기 어렵습니다. 7.1에서는 기존과 다른 새 API를 제공할 예정입니다(참고: Announcing TypeScript 7.0).
erasableSyntaxOnly와 지울 수 있는 TypeScript
Node.js, Deno, Bun이 TypeScript를 직접 실행하게 되면서, TypeScript 문법은 두 종류로 나뉘었습니다. 타입 표기처럼 지우기만 하면 유효한 JavaScript가 되는 문법과, enum처럼 JavaScript 코드를 새로 만들어 내야 하는 문법입니다. Node.js의 타입 스트리핑(type stripping)은 앞의 것만 지원합니다.
TypeScript 5.8(2025년 2월)은 이 차이를 컴파일 단계에서 잡아 주는 --erasableSyntaxOnly 옵션을 추가했습니다(참고: Announcing TypeScript 5.8). 이 옵션을 켜면 다음 문법이 오류가 됩니다.
// erasableSyntaxOnly에서 오류가 되는 문법
enum Direction { Up, Down } // enum 선언
namespace Utils { // 런타임 코드가 있는 namespace
export const version = "1.0";
}
class Point {
constructor(public x: number) {} // 매개변수 속성
}
import fs = require("fs"); // import = 별칭
Node.js 문서도 타입 스트리핑을 쓸 때 TypeScript 5.8 이상과 erasableSyntaxOnly, verbatimModuleSyntax 설정을 권장합니다(참고: Node.js: Modules: TypeScript). TypeScript가 처음 등장했을 때 JavaScript에 없던 기능을 앞서 제공하던 enum, namespace 같은 문법은 이제 '되도록 쓰지 않는' 문법이 되었습니다.
Type Annotations 제안(Stage 1)
JavaScript 자체에 타입 표기 문법을 넣자는 Type Annotations 제안은 2022년 3월 Stage 1에 올랐습니다(참고: 2022년 3월 회의록). 엔진은 타입 표기를 주석처럼 무시하고, 타입 검사는 TypeScript나 Flow 같은 외부 도구가 맡는다는 구상입니다. 이 제안이 표준이 되면 런타임의 타입 스트리핑 없이도 브라우저에서 타입이 붙은 코드를 바로 실행할 수 있습니다.
그러나 2023년 9월 업데이트 이후 회의 안건에 다시 오르지 않았고, 2025~2026년 회의록에서도 논의된 기록을 찾을 수 없습니다(참고: proposal-type-annotations). 런타임 쪽에서 타입 스트리핑이 먼저 자리 잡은 만큼, 언어 차원의 표준화가 다시 속도를 낼지는 지켜볼 필요가 있습니다.
도구의 네이티브 전환
TypeScript 7 외에도 JavaScript 도구의 상당수가 Rust나 Go로 옮겨 갔습니다. 2020년 글에서 "2023년까지 대다수의 유명 JavaScript 도구들은 JavaScript로 작성되지 않을 것"이라는 전망을 소개했는데, 번들러, 린터, 포매터, 트랜스파일러에서는 그 전망이 상당 부분 현실이 되었습니다.
언어와 런타임 관점에서 보면 이 흐름은 두 가지를 바꾸었습니다. 하나는 런타임과 도구 사이의 경계입니다. Node.js의 타입 스트리핑은 Rust로 작성된 SWC의 TypeScript 파서를 WebAssembly로 컴파일한 Amaro를 쓰고, V8의 Temporal은 Rust 라이브러리로 구현되었습니다. 다른 하나는 기여의 진입 장벽입니다. JavaScript 개발자가 자신이 쓰는 도구의 코드를 읽고 고치려면 이제 Rust나 Go를 알아야 하는 경우가 많습니다.
AI 도구로 인한 변화
2020년 글의 작성 시점에는 GitHub Copilot이 아직 공개되지 않았습니다. 2026년 현재 Claude Code, GitHub Copilot, Cursor, Codex 같은 AI 코딩 도구는 JavaScript 개발 환경의 일부가 되었습니다. State of JS 2025 응답자들은 자신이 작성하는 코드 중 평균 29%가 AI가 생성한 코드라고 답했습니다. 전년도 조사의 20%에서 늘어난 수치입니다(참고: State of JS 2025: Usage). 이 절에서는 이 변화가 언어와 런타임, 그리고 표준을 만드는 과정에 어떤 영향을 주고 있는지 확인된 사실 위주로 정리합니다.
그림 11 작성하는 코드 중 AI가 생성한 코드 비중에 대한 State of JS 2025 응답 분포(출처: State of JS 2025: Usage)
TypeScript가 1위가 된 이유
GitHub Octoverse 2025에 따르면 2025년 8월 TypeScript는 월간 기여자 수 기준으로 처음 GitHub에서 가장 많이 쓰인 언어가 되었습니다. TypeScript 기여자는 2,636,006명으로 1년 사이 66.6% 늘었고, Python보다 약 4만 2천 명 많았습니다. JavaScript 기여자는 약 215만 명이었습니다(참고: Octoverse: A new developer joins GitHub every second as AI leads TypeScript to #1).
그림 12 GitHub 월간 기여자 수 기준 언어 순위 변화(출처: Octoverse 2025)
보고서 제목이 말하듯 GitHub은 그 배경으로 AI를 꼽았습니다. 보고서는 타입 시스템이 대규모 언어 모델(LLM)이 만든 코드의 오류를 파이프라인 앞단에서 잡아 준다고 설명하며, LLM이 생성한 코드의 컴파일 오류 중 94%가 타입 검사 실패였다는 2025년 연구를 인용했습니다(참고: Type-Constrained Code Generation with Language Models). 또 하나의 이유로는 Next.js 15, Astro 3, SvelteKit 2 같은 프레임워크가 기본으로 TypeScript 프로젝트를 생성한다는 점을 들었습니다(참고: Octoverse 2025).
정리하면 사람이 직접 작성하는 코드가 줄어들수록, 생성된 코드를 기계적으로 검증할 수단의 가치가 커집니다. 타입은 그 수단 중 비용이 가장 낮은 것에 속합니다. TypeScript 팀도 같은 관점을 보였습니다. Anders Hejlsberg는 Go 이식을 발표하며 새 기반이 "다음 세대의 AI 도구"를 가능하게 할 것이라고 했고, TypeScript 7.0 발표문은 tsc를 실행하는 주체로 "여러분(그리고 최근에는 아마 AI 에이전트)"을 함께 언급했습니다(참고: A 10x Faster TypeScript, Announcing TypeScript 7.0). 에이전트가 코드를 고칠 때마다 타입 검사를 다시 돌린다면, 컴파일러가 10배 빨라진 효과는 사람이 직접 쓸 때보다 더 크게 나타납니다.
학습 데이터의 시차
LLM은 학습 시점까지의 코드로 학습하므로, 그 뒤에 표준이 된 기능을 모르거나 이미 쓰지 않는 패턴을 제안하는 경우가 있습니다. 매년 명세가 개정되는 JavaScript에서 이 문제는 매년 반복됩니다. 이 글에서 소개한 Object.groupBy, Promise.withResolvers, Iterator helpers, Error.isError, Map.prototype.getOrInsert, Temporal은 모두 2023년 이후 표준이 되었거나 브라우저에 출시되었습니다. 학습 데이터에는 이 기능들보다 Lodash의 groupBy, new Promise((resolve) => ...) 패턴, instanceof Error, Moment.js와 Date 코드가 훨씬 많이 들어 있을 것입니다.
이 문제를 직접 언급한 곳은 MDN입니다. MDN은 2026년 6월 공식 MCP(Model Context Protocol) 서버를 공개하며 "AI 도구는 학습 데이터와 지식 컷오프 때문에 오래된 웹 플랫폼 정보를 내놓을 수 있다"고 이유를 밝혔습니다. MDN 팀의 비교 실험에서 MCP 서버 없이 Claude Code(Opus 4.7)에 질문했을 때 브라우저 지원 정보를 맞힌 경우는 네 번 중 한 번뿐이었습니다(참고: Introducing the MDN MCP server). 브라우저 벤더도 비슷한 도구를 내놓았습니다. Chrome은 2025년 9월 Chrome DevTools MCP를, WebKit은 2026년 여름 Safari MCP 서버를 공개해, 에이전트가 실제 브라우저에서 코드를 실행하고 결과를 확인할 수 있게 했습니다.
그림 13 MDN MCP 서버를 호출해 CSS 함수의 사용법과 브라우저 지원 정보를 답하는 화면(출처: Introducing the MDN MCP server)
프레임워크 쪽 대응은 더 직접적입니다. Next.js는 16.2부터 프로젝트의 node_modules 안에 버전에 맞는 문서를 함께 설치하고, 16.3부터는 next dev가 AI 코딩 에이전트를 감지하면 AGENTS.md에 다음 문구를 자동으로 넣습니다(참고: Next.js 16.3: AI Improvements).
# This is NOT the Next.js you know This version has breaking changes ... APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` ... before writing any code. Heed deprecation notices.
언어 차원의 직접적인 대응은 아직 없습니다. 다만 TypeScript 6.0과 7.0이 target: es5, baseUrl, moduleResolution: node 같은 오래된 설정을 오류로 바꾼 것은, 학습 데이터에 흔한 설정을 그대로 가져온 코드를 빌드 단계에서 오류로 알려 주는 효과도 있습니다. eslint-plugin-unicorn의 prefer-temporal 같은 규칙도 같은 역할을 할 수 있습니다. 오래된 패턴을 사람이 기억해서 피하던 시절에서, 도구가 기계적으로 알려 주는 시절로 바뀌고 있습니다.
엔진을 옮기는 AI
AI 코딩 도구는 JavaScript 엔진을 만드는 데에도 쓰이기 시작했습니다.
Ladybird는 2026년 2월 Rust를 C++의 후속 언어로 채택했다고 발표하며, 첫 대상으로 LibJS의 렉서, 파서, AST, 바이트코드 생성기를 옮겼습니다. 약 2만 5천 줄의 Rust 코드를 2주 만에 이식했고, 번역에는 Claude Code와 Codex를 썼습니다. 이식 결과 Test262 테스트 52,898개와 Ladybird 자체 회귀 테스트 12,461개에서 회귀가 하나도 없었고, Rust 파서가 만든 모든 AST와 Rust 컴파일러가 만든 모든 바이트코드가 C++ 구현의 결과와 동일했습니다(참고: Ladybird adopts Rust). Andreas Kling은 이 작업이 AI가 혼자 한 것이 아니라고 강조했습니다.
무엇을 어떤 순서로 옮길지, Rust 코드가 어떤 모습이어야 할지는 내가 정했습니다. 수백 개의 작은 프롬프트로 에이전트가 가야 할 방향을 잡아 주는 작업이었습니다.
I decided what to port, in what order, and what the Rust code should look like. It was hundreds of small prompts, steering the agents where things needed to go.
LibJS가 첫 대상이 된 이유도 눈여겨볼 만합니다. 단계와 출력이 명확하고, Test262라는 방대한 공식 테스트 묶음이 있어 결과를 검증하기 쉬웠기 때문입니다. 6년 전 글에서 '명세를 읽는 것'의 가치를 이야기했는데, 이제는 명세와 함께 공개된 테스트가 AI 시대에 엔진을 검증하는 기준 역할까지 합니다.
Ladybird의 이식은 기존 구현과 테스트가 이미 있는 코드를 다른 언어로 옮기는 작업이었습니다. Bun도 같은 방식으로 Zig 코드 53만 줄을 Rust로 옮겼습니다(참고: Rewriting Bun in Rust). 결과를 판정할 기준이 분명할 때 AI 도구의 효과가 크게 나타났습니다. 새 기능을 설계하거나 명세를 해석하는 일에 같은 방식이 통할지는 아직 사례가 부족합니다.
표준과 오픈소스 커뮤니티의 대응
AI 도구의 확산은 표준을 만드는 사람들의 일하는 방식에도 질문을 던졌습니다.
TC39는 2025년 7월 회의에서 "자신의 코멘트는 직접 쓴다"는 원칙을 논의했습니다. Kevin Gibbons는 Discourse 포럼 등에 LLM이 작성한 코멘트가 늘고 있다며, 남이 읽을 글을 LLM으로 만들어 올리는 일은 읽는 사람의 시간을 낭비한다고 지적했습니다. 위원회는 이를 행동 강령(Code of Conduct)에 넣지는 않되 별도의 정책 문서로 두는 데 합의했습니다(참고: 2025년 7월 회의록). 현재 AI_POLICY.md는 다음과 같이 정하고 있습니다.
- 아이디어, 코멘트, 이슈 같은 글(prose) 기여는 본인이 직접 쓴 것이어야 합니다. LLM에게 아이디어를 부풀리거나 설명하게 한 결과를 올리지 말고, 아이디어 자체를 올립니다.
- LLM을 이용한 번역과 교정은 새로운 내용을 더하지 않는 한 허용합니다.
- 코드 기여는 이 정책의 대상이 아니며, 기존 리뷰 과정으로 평가합니다.
AI는 제안의 동기로도 등장합니다. 2026년 5월 Stage 1에 오른 Comparisons 제안은 두 값이 어떻게 다른지 비교하는 기능을 언어에 추가하는 제안입니다. 논의 중 "앞으로 사람이 이런 코드를 직접 쓰는 일이 줄어든다면 표준화가 꼭 필요한가"라는 질문이 나오자, 위원들은 오히려 LLM이 이런 코드를 잘못 생성할 가능성 때문에 언어가 올바른 구현을 제공해야 한다고 답했습니다. 회의 결론에도 "거의 아무도 제대로 이해하지 못하는 문제를 AI가 생성한 해결책에 맡기지 말고 언어가 직접 풀어야 한다는 점에 많은 위원이 공감했다"고 기록되었습니다(참고: 2026년 5월 회의록). 2025년 7월 Module Import Hook 제안 논의에서는 AI가 생성한 코드를 격리해 실행하는 용도가 거론되었는데, Kevin Gibbons는 "AI는 샌드박스를 탈출하려 들 것"이라며 이 제안이 그런 격리를 제공하지 못한다고 선을 그었습니다(참고: 2025년 7월 회의록).
Ladybird는 2026년 6월 외부 PR을 받지 않기로 했습니다. 공지는 "큰 패치에는 큰 노력이 들었고, 그 노력이 선의를 가늠하는 합리적인 기준이었지만, 그 가정은 더 이상 통하지 않는다"고 이유를 밝혔습니다. 인터넷 전체의 신뢰할 수 없는 입력을 사용자 컴퓨터에서 실행하는 브라우저이기에 이 문제가 더 중요하다는 설명입니다(참고: Changing How We Develop Ladybird). 같은 프로젝트가 AI로 LibJS를 옮기면서 외부의 기여는 막았습니다. 두 결정은 모순이라기보다 '누가 검증 책임을 지는가'라는 같은 질문에 대한 답으로 읽힙니다. LibJS 이식은 메인테이너가 직접 지휘하고 검증했고, 외부 PR은 그 검증 비용을 메인테이너에게 넘깁니다.
마치며
2020년 글은 세 번째 주기의 JavaScript가 레거시를 정리하고 도구가 만든 레이어를 걷어 낼 것이라고 했습니다. 6년 동안 그 일은 실제로 일어났습니다. TypeScript는 6.0에서 ES5 출력과 AMD 모듈을 폐기 예정으로 지정하고 7.0에서 제거했고, Node.js는 require(esm)으로 CommonJS와 ESM 사이의 벽을 낮추었으며, 런타임들은 TypeScript를 별도 빌드 없이 실행합니다. 30년 넘게 Date에 의존해 온 날짜 처리도 Temporal로 바뀌기 시작했습니다. 2020년에 Stage 2였던 제안이 Stage 4에 오르기까지 6년이 걸렸다는 사실은, 표준이 얼마나 신중하게 만들어지는지 보여 줍니다.
그러나 걷어 낸 자리에는 새로운 레이어가 추가되었습니다. 코드를 작성하는 주체에 AI 에이전트가 더해졌고, 런타임과 프레임워크는 사람이 아닌 에이전트를 위한 문서와 서버를 따로 만들기 시작했습니다. TypeScript가 1위에 오른 이유도, TC39가 AI 정책을 문서로 만든 이유도 모두 이 새로운 레이어와 관련이 있습니다. 6년 전에는 이 변화를 예상하지 못했습니다.
AI가 확산될수록 오히려 표준과 검증의 가치가 커지고 있습니다. Ladybird가 2주 만에 LibJS를 Rust로 옮길 수 있었던 것은 Test262라는 공개된 테스트 묶음 덕분이었고, LLM이 만든 코드의 오류를 가장 먼저 잡아 주는 것은 타입 검사기였습니다. TC39 위원들은 AI가 잘못 생성할 만한 코드일수록 언어가 직접 올바른 구현을 제공해야 한다고 말했습니다(참고: 2026년 5월 회의록). 앞으로의 JavaScript 명세는 사람뿐 아니라 기계가 읽고 따르는 기준 역할도 함께 맡게 될 것입니다.












