Contents
see ListES2026이란?
ECMAScript 2026(ES2026)은 TC39 위원회가 2026년 확정한 자바스크립트 표준입니다. Temporal API의 Stage 4 승격과 함께, 개발자들이 오랫동안 기다려온 리소스 관리 패턴, 비동기 배열 생성, 정밀 수학 연산 등 실용적인 기능들이 다수 포함됐습니다. 이 글에서는 ES2026의 핵심 신기능 4가지를 실전 코드 예제와 함께 완전 정복합니다.
1. 명시적 리소스 관리: using / await using
파일 핸들, 데이터베이스 연결, 소켓 등을 사용한 뒤 반드시 정리해야 하는 리소스를 자동으로 해제해주는 키워드입니다. C#의 using, Python의 with와 유사하며, 리소스 누수(resource leak)를 언어 차원에서 방지합니다.
Symbol.dispose / Symbol.asyncDispose
리소스 객체에 [Symbol.dispose]() 또는 [Symbol.asyncDispose]() 메서드를 구현하면 using 키워드와 함께 사용할 수 있습니다.
// 동기 리소스 관리 예제
class DatabaseConnection {
constructor(url) {
this.url = url;
this.conn = openConnection(url); // 가상의 연결 함수
console.log(`[DB] 연결됨: ${url}`);
}
query(sql) {
return this.conn.execute(sql);
}
[Symbol.dispose]() {
this.conn.close();
console.log(`[DB] 연결 해제: ${this.url}`);
}
}
// using 키워드: 블록 종료 시 자동으로 [Symbol.dispose] 호출
function getUsers() {
using db = new DatabaseConnection("postgres://localhost:5432/mydb");
const users = db.query("SELECT * FROM users");
return users;
// 여기서 자동으로 db[Symbol.dispose]() 호출 → 연결 해제
}
// 비동기 리소스 관리 예제 (await using)
class FileHandle {
constructor(path) {
this.path = path;
}
static async open(path) {
const handle = new FileHandle(path);
handle.fd = await fs.promises.open(path, "r");
return handle;
}
async read() {
return await this.fd.readFile("utf-8");
}
async [Symbol.asyncDispose]() {
await this.fd.close();
console.log(`[File] 닫힘: ${this.path}`);
}
}
async function processFile(path) {
await using file = await FileHandle.open(path);
const content = await file.read();
return content.split("\n").length;
// 블록 종료 시 자동으로 await file[Symbol.asyncDispose]() 호출
}
DisposableStack / AsyncDisposableStack
여러 리소스를 묶어서 한 번에 관리할 때 사용합니다.
async function multiResource() {
await using stack = new AsyncDisposableStack();
const db = stack.use(new DatabaseConnection("..."));
const cache = stack.use(new RedisConnection("..."));
const logger = stack.use(new LogStream("./app.log"));
await processData(db, cache, logger);
// 블록 종료 시 역순으로 모두 dispose: logger → cache → db
}
2. Array.fromAsync()
비동기 이터러블(async iterable), Promise 배열, 또는 비동기 배열 형태 객체에서 배열을 생성합니다. 기존에는 수동으로 루프를 돌거나 Promise.all()을 써야 했던 패턴을 한 줄로 줄여줍니다.
// 비동기 제너레이터 → 배열 변환
async function* fetchPages(url) {
let page = 1;
while (true) {
const res = await fetch(`${url}?page=${page}`);
const data = await res.json();
if (!data.items.length) break;
yield* data.items;
page++;
}
}
// 기존 방식 (번거로움)
const items = [];
for await (const item of fetchPages("https://api.example.com/data")) {
items.push(item);
}
// Array.fromAsync() 사용 (간결)
const items = await Array.fromAsync(fetchPages("https://api.example.com/data"));
// map 함수와 함께 사용
const titles = await Array.fromAsync(
fetchPages("https://api.example.com/posts"),
(post) => post.title.toUpperCase()
);
// Promise 배열을 순차적으로 처리 (Promise.all과 차이점: 순차 실행)
const urls = ["https://api1.com", "https://api2.com", "https://api3.com"];
// Promise.all: 병렬 실행
const parallel = await Promise.all(urls.map(fetch));
// Array.fromAsync: 순차 실행 (순서 보장, Rate Limit 우회에 유용)
const sequential = await Array.fromAsync(urls, (url) => fetch(url).then(r => r.json()));
3. Error.isError()
값이 Error 객체인지 신뢰할 수 있게 확인하는 정적 메서드입니다. 기존 instanceof Error는 다른 iframe이나 다른 realm에서 생성된 Error를 제대로 감지하지 못하는 문제가 있었습니다.
// 기존 방식의 문제점
const err = new Error("test");
console.log(err instanceof Error); // true (같은 realm)
// iframe 또는 Node.js vm.runInNewContext에서 생성된 Error는?
const vmError = vm.runInNewContext("new Error('cross-realm')");
console.log(vmError instanceof Error); // false! (realm이 달라 instanceof 실패)
// Error.isError()는 realm 무관하게 정확히 판별
console.log(Error.isError(vmError)); // true
console.log(Error.isError(new TypeError("t"))); // true
console.log(Error.isError({ message: "err" })); // false (일반 객체)
console.log(Error.isError("error string")); // false
console.log(Error.isError(null)); // false
// 실전 활용: 에러 핸들링 유틸
function handleError(value) {
if (Error.isError(value)) {
console.error(`[Error] ${value.name}: ${value.message}`);
reportToSentry(value);
} else {
console.error(`[Unknown] 예상치 못한 throw:`, value);
}
}
// Promise rejection 처리
process.on("unhandledRejection", (reason) => {
handleError(reason); // Error.isError()로 안전하게 분기
});
4. Math.sumPrecise()
부동소수점 오차 없이 숫자 배열의 정확한 합계를 계산합니다. 금융 계산, 과학 연산, 통계 처리에서 발생하던 누산 오차 문제를 해결합니다.
// 기존 JavaScript의 부동소수점 문제
console.log(0.1 + 0.2); // 0.30000000000000004
console.log([0.1, 0.2].reduce((a, b) => a + b)); // 0.30000000000000004
// Math.sumPrecise()는 정확한 값 반환
console.log(Math.sumPrecise([0.1, 0.2])); // 0.3
// 대규모 배열에서의 누산 오차 해결
const prices = [9.99, 14.99, 4.99, 29.99, 7.49];
console.log(prices.reduce((a, b) => a + b)); // 67.44999999999999 (오차!)
console.log(Math.sumPrecise(prices)); // 67.45 (정확)
// 이터러블도 지원
function* generateValues() {
yield 1.1; yield 2.2; yield 3.3;
}
console.log(Math.sumPrecise(generateValues())); // 6.6 (정확)
브라우저 및 런타임 지원 현황 (2026년 4월 기준)
| 기능 | Chrome | Firefox | Safari | Node.js |
|---|---|---|---|---|
| using / await using | 134+ | 130+ | 18+ | 22+ |
| Array.fromAsync() | 121+ | 115+ | 16.4+ | 22+ |
| Error.isError() | 134+ | 134+ | 18.4+ | 24+ |
| Math.sumPrecise() | 136+ | 136+ | 18.4+ | 24+ |
TypeScript는 5.2+부터 using/await using을 지원하며, Babel 플러그인을 통해 구형 환경에서도 트랜스파일 가능합니다.
정리
ES2026은 자바스크립트를 더 안전하고 예측 가능한 언어로 발전시킨 중요한 업데이트입니다. using/await using으로 리소스 누수를 언어 차원에서 방지하고, Array.fromAsync()로 비동기 데이터 처리를 단순화하며, Error.isError()로 크로스 렐름 에러 감지를 해결하고, Math.sumPrecise()로 부동소수점 누산 오차를 근본적으로 해결했습니다. 지금 바로 Node.js 24+ 또는 최신 브라우저에서 실험해보세요.