ES2026이란?

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월 기준)

기능ChromeFirefoxSafariNode.js
using / await using134+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+ 또는 최신 브라우저에서 실험해보세요.