ES2026이란?

ECMAScript 2026(ES17)은 2026년 공식 확정된 JavaScript 표준으로, 개발자들이 오랫동안 요청해온 날짜 처리, 자원 관리, 비동기 패턴 등 실용적인 기능이 대거 포함되었습니다. 이 글에서는 ES2026의 핵심 기능을 코드 예시와 함께 상세히 설명합니다.

1. Temporal API: 드디어 Date를 대체하다

수십 년간 JavaScript 개발자를 괴롭혀 온 Date 객체의 문제를 해결하는 Temporal API가 ES2026에 포함되었습니다. Temporal 객체는 불변(immutable)이며, 타임존과 비그레고리력 달력을 일급 지원합니다.

// 기존 Date의 문제점
const d = new Date(2026, 0, 1); // 월이 0부터 시작하는 혼란
console.log(d.getMonth()); // 0 (1월인데 0을 반환)

// Temporal API - 직관적이고 명확한 API
const today = Temporal.Now.plainDateISO();
console.log(today.toString()); // "2026-04-13"

// 날짜 계산 - 불변 객체라 항상 새 객체 반환
const nextWeek = today.add({ weeks: 1 });
const thirtyDaysLater = today.add({ days: 30 });
console.log(nextWeek.toString());       // "2026-04-20"
console.log(thirtyDaysLater.toString()); // "2026-05-13"

// 타임존 처리
const seoulTime = Temporal.Now.zonedDateTimeISO("Asia/Seoul");
console.log(seoulTime.hour);   // 현재 서울 시간의 시
console.log(seoulTime.offset); // "+09:00"

// 두 날짜 사이의 차이 계산
const start = Temporal.PlainDate.from("2026-01-01");
const end = Temporal.PlainDate.from("2026-12-31");
const diff = start.until(end);
console.log(diff.days); // 364

// 날짜 비교
const date1 = Temporal.PlainDate.from("2026-03-15");
const date2 = Temporal.PlainDate.from("2026-04-01");
console.log(Temporal.PlainDate.compare(date1, date2)); // -1 (date1이 이전)

2. Explicit Resource Management: using / await using

파일, DB 연결, 네트워크 스트림 등 자원을 블록 스코프 종료 시 자동으로 정리하는 키워드가 추가되었습니다. 기존의 번거로운 try...finally 패턴을 대체합니다.

// 기존 방식 - try/finally로 수동 정리
let conn;
try {
  conn = await db.connect();
  const result = await conn.query("SELECT * FROM users");
  return result;
} finally {
  if (conn) await conn.close(); // 반드시 정리해야 함
}

// ES2026 - using으로 자동 정리 (동기)
class TempFile {
  constructor(path) {
    this.handle = fs.openSync(path, "r");
  }
  [Symbol.dispose]() {
    fs.closeSync(this.handle); // 블록 종료 시 자동 호출
  }
}

{
  using file = new TempFile("/tmp/data.txt");
  const content = readFromHandle(file.handle);
  // 블록 끝에서 자동으로 file[Symbol.dispose]() 호출
}
// 여기서 이미 파일이 닫혀 있음

// ES2026 - await using으로 비동기 자동 정리
class DatabaseConnection {
  static async connect(url) {
    const conn = new DatabaseConnection();
    conn.socket = await openSocket(url);
    return conn;
  }
  async [Symbol.asyncDispose]() {
    await this.socket.close(); // 비동기 정리
  }
}

async function fetchUsers() {
  await using conn = await DatabaseConnection.connect(DB_URL);
  return await conn.query("SELECT * FROM users");
  // 블록 종료 시 자동으로 conn[Symbol.asyncDispose]() 호출
}

3. Set 메서드 표준화

ES2026에서 Set에 집합 연산 메서드들이 추가되어 수학적 집합 연산을 간편하게 처리할 수 있습니다.

const a = new Set([1, 2, 3, 4]);
const b = new Set([3, 4, 5, 6]);

// 합집합 (union)
console.log([...a.union(b)]);            // [1, 2, 3, 4, 5, 6]

// 교집합 (intersection)
console.log([...a.intersection(b)]);     // [3, 4]

// 차집합 (difference)
console.log([...a.difference(b)]);       // [1, 2]

// 대칭 차집합 (symmetricDifference)
console.log([...a.symmetricDifference(b)]); // [1, 2, 5, 6]

// 부분집합 검사
const small = new Set([3, 4]);
console.log(small.isSubsetOf(a));        // true
console.log(a.isSupersetOf(small));      // true
console.log(a.isDisjointFrom(new Set([7, 8]))); // true

4. Array.fromAsync

비동기 이터러블에서 배열을 만드는 편의 메서드입니다.

// 기존 방식 - 번거로운 for-await-of
async function collectStream(stream) {
  const chunks = [];
  for await (const chunk of stream) {
    chunks.push(chunk);
  }
  return chunks;
}

// ES2026 - Array.fromAsync
async function collectStream(stream) {
  return await Array.fromAsync(stream);
}

// 변환 함수와 함께 사용
const numbers = await Array.fromAsync(
  asyncNumberGenerator(),
  n => n * 2  // 매핑 함수
);

// 실용 예시: API 페이지네이션
async function* fetchAllPages(url) {
  let nextUrl = url;
  while (nextUrl) {
    const res = await fetch(nextUrl);
    const data = await res.json();
    yield* data.items;
    nextUrl = data.nextPage;
  }
}

const allItems = await Array.fromAsync(fetchAllPages("/api/products"));

5. Error.isError()

값이 Error 객체인지 신뢰할 수 있게 검사하는 정적 메서드입니다.

// 기존 방식의 문제점 - instanceof는 크로스-realm에서 실패
const iframe = document.createElement("iframe");
document.body.appendChild(iframe);
const IframeError = iframe.contentWindow.Error;
const err = new IframeError("test");
console.log(err instanceof Error); // false! (다른 realm)

// ES2026 - Error.isError() 사용
console.log(Error.isError(err));           // true (realm 무관)
console.log(Error.isError(new Error()));   // true
console.log(Error.isError("error str"));   // false
console.log(Error.isError({ message: "" })); // false

// 실용 예시: 에러 핸들러
function handleResult(result) {
  if (Error.isError(result)) {
    logger.error(result.message, result.stack);
    return null;
  }
  return result;
}

6. RegExp.escape()

사용자 입력 문자열을 정규식에서 안전하게 사용할 수 있도록 특수문자를 이스케이프합니다.

// 기존 방식 - 직접 이스케이프 함수 작성
function escapeRegex(str) {
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

// ES2026 - 내장 RegExp.escape()
const userInput = "(안녕하세요) 가격: $100.00";
const escaped = RegExp.escape(userInput);
// "\(안녕하세요\)\ 가격:\ \$100\.00"

const regex = new RegExp(escaped, "g");
const result = text.match(regex);

// 실용 예시: 검색어 하이라이트
function highlightSearch(text, searchTerm) {
  const escaped = RegExp.escape(searchTerm);
  const regex = new RegExp(`(${escaped})`, "gi");
  return text.replace(regex, "$1");
}

7. Promise.try()

동기/비동기 함수를 통일된 방식으로 Promise로 감쌉니다.

// 기존 방식 - 동기 에러를 Promise.reject로 변환해야 함
function loadConfig(path) {
  return new Promise((resolve, reject) => {
    try {
      const config = JSON.parse(fs.readFileSync(path)); // 동기 에러 발생 가능
      resolve(config);
    } catch (e) {
      reject(e);
    }
  });
}

// ES2026 - Promise.try()
function loadConfig(path) {
  return Promise.try(() => {
    const config = JSON.parse(fs.readFileSync(path)); // 동기 에러도 자동 처리
    return config;
  });
}

// 함수가 비동기든 동기든 동일하게 처리
const result = await Promise.try(mightBeAsyncFn);

마치며

ES2026은 JavaScript 생태계의 오랜 불편함을 해소하는 실용적인 업데이트입니다. 특히 Temporal API는 날짜 처리 라이브러리(day.js, date-fns 등)의 필요성을 크게 줄이고, using 키워드는 자원 누수를 언어 차원에서 방지합니다. 지금 바로 프로젝트에 적용해 보세요.