← 목록으로

자바스크립트 개발 10년차의 시선: ES2026 신기능 7가지와 제가 간절히 기다리는 2가지

2026. 6. 26.

자바스크립트 개발 10년차의 시선: ES2026 신기능 7가지와 제가 간절히 기다리는 2가지

두 주 전, 가볍고 쉬운 글만 쓰겠다고 약속했던(사실 저 자신에게 한 약속이었죠) 기억이 어렴풋이 납니다. 음… 그 약속을 깨고야 말았네요 😅 지난주에는 상상할 수 있는 가장 재미없는 주제 중 하나인 레거시 애플리케이션에 대한 방대한 글을 발행했었죠. 놀랍게도 DEV 편집팀에서 이 글을 마음에 들어 했고, 주간 인기 게시물 Top 7에 포함시켜 주셨습니다 ❤️

하지만 오늘은 드디어 조금 더 가벼운 주제를 다룰 시간입니다. 그리고 제가 생각하기에 이 주제는 훨씬 더 많은 관심을 받아야 마땅합니다.

요즘 우리는 AI 에이전트에 대해 많은 이야기를 나누고 있습니다. 저 역시 그 이유를 충분히 이해합니다. 정말 매력적인 분야이니까요. 하지만 때로는 우리가 매일 사용하는 프로그래밍 언어 또한 끊임없이 진화하고 있다는 사실을 잊고 있는 건 아닌가 하는 생각이 들어요.

저는 대부분의 시간을 자바스크립트(음… 물론 타입스크립트입니다 😄) 코드를 작성하며 보냅니다. 맞아요, 지난 몇 년 동안 생태계는 정말 많이 성숙했습니다. 더 이상 끝없는 React 대 Angular 전쟁을 벌이지도 않고요. 한 달은 Redux를 열렬히 사랑하다가 6개월 뒤에는 "너무 과하게 설계된 괴물이야!"라며 모든 애플리케이션에서 제거하던 시절도 이제 거의 지났습니다 😅.

하지만 생태계와 ECMAScript 표준 모두 계속해서 발전하고 있습니다.

프런트엔드 개발을 영원히 바꿔놓았던 2015년의 전설적인 ES6 릴리스 이후, TC39 위원회는 반복적인 접근 방식을 채택하여 매년 새로운 기능을 출시하기 시작했습니다.

그리고 좋은 점은, 최신 브라우저나 런타임을 사용한다면 이러한 기능들에 거의 즉시 접근할 수 있다는 겁니다.

그렇다면 ECMAScript 2026에는 어떤 것들이 포함될까요?

아, 그리고 재미를 위해 이 글의 모든 코드 예시는 매우 강한 의견을 가진 가상의 링크드인 전문가들의 내면의 독백을 기반으로 합니다. 실제 인물과의 유사성은 물론, 순전히 우연일 뿐입니다 😇

이 시리즈의 다른 글들:


1. Map.prototype.getOrInsert() / Upsert

MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/getOrInsert

이 기능은 "잠깐… 왜 이게 10년 전에는 없었지?"라는 생각을 들게 하는 것들 중 하나입니다.

링크드인 전문가들의 의견을 수집한다고 가정해봅시다. 지금까지는 다음과 같은 코드를 작성해야 했습니다.

const opinions = new Map();

function addOpinion(topic, author) {
  if (!opinions.has(topic)) {
    opinions.set(topic, []);
  }

  opinions.get(topic).push(author);
}

addOpinion("React is dead", "10x Engineer");
addOpinion("React is dead", "Principal AI Evangelist");

잘 작동하지만, 반복되는 코드가 꽤 많죠.

ES2026이 되면 훨씬 더 깔끔해집니다:

const opinions = new Map();

opinions
  .getOrInsert("React is dead", [])
  .push("10x Engineer");

opinions
  .getOrInsert("React is dead", [])
  .push("Principal AI Evangelist");

console.log(opinions);

계산된 값(computed version)을 위한 버전도 제공됩니다:

const opinions = new Map();

opinions
  .getOrInsertComputed(
    "Nobody writes clean code anymore",
    () => []
  )
  .push("DDD Purist");

솔직히 훨씬 더 깔끔하고 읽기 쉽습니다. 캐싱에도 완벽하죠! 코드는 줄어들고, API는 훨씬 더 좋아졌습니다. 제가 실무에서 Map을 많이 다루면서 특정 키가 없을 때 기본값을 설정하고 바로 조작하는 이런 유틸리티 함수가 늘 아쉬웠는데, 이제야 표준에 들어오니 정말 반갑네요.


2. Iterator.concat()

MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator/concat

concat()에 대해 이야기하기 전에, 한 가지 질문에 답해봅시다:

이터레이터(Iterator)란 무엇인가요?

이터레이터는 기본적으로 데이터를 한 번에 한 요소씩 소비할 수 있도록 해주는 커서입니다. 모든 데이터를 메모리에 한꺼번에 저장할 필요가 없죠.

예를 들어:

const posts = [
  "Angular is dead",
  "JavaScript was a mistake"
];

const iterator = posts.values();

console.log(iterator.next());
// { value: "Angular is dead", done: false }

console.log(iterator.next());
// { value: "JavaScript was a mistake", done: false }

console.log(iterator.next());
// { value: undefined, done: true }

그리고 이터레이터는 지연(lazy) 평가되기 때문에, 메모리를 폭발시키지 않고도 무한 시퀀스를 생성할 수 있습니다:

function* endlessHotTakes() {
  while (true) {
    yield "Everything should be rewritten in Rust.";
  }
}

const iterator = endlessHotTakes();

console.log(iterator.next().value);
console.log(iterator.next().value);
console.log(iterator.next().value);

작년에는 map(), filter() 등과 같은 기능들을 도입한 Iterator Helpers가 등장했습니다:

function* linkedinFeed() {
  yield "Use Rust for everything";
  yield "JavaScript was a mistake";
  yield "Clean code is dead";
  yield "Angular is dead";
}

const controversialTakes =
  linkedinFeed()
    .filter(post => post.includes("dead"))
    .map(post => `🔥 ${post}`);

console.log([...controversialTakes]);

그리고 이제 또 하나의 멋진 추가 기능인 Iterator.concat()이 생겼습니다. 프런트엔드 전문가와 백엔드 전문가가 각자의 지혜 스트림을 가지고 있다고 가정해봅시다:

function* frontendExperts() {
  yield "React is dead";
  yield "Nobody needs Redux";
}

function* backendExperts() {
  yield "Microservices solve everything";
  yield "Frontend developers don't understand architecture";
}

이제 이들을 우아하게 결합할 수 있습니다:

const feed = Iterator.concat(
  frontendExperts(),
  backendExperts()
);

console.log([...feed]);

제 생각에 이러한 이터레이터 기능들은 마땅히 받아야 할 만큼의 관심을 받지 못하고 있습니다. 데이터를 효율적으로 다루거나, 무한 스트림을 처리할 때 이터레이터의 진가가 발휘되는데, 특히 큰 데이터를 처리하는 Node.js 백엔드나 복잡한 UI 상태를 관리하는 프런트엔드에서 그 활용도가 엄청나죠.


3. Array.fromAsync()

MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fromAsync

이것은 우리가 사랑하는 Array.from()의 비동기 버전이라고 할 수 있습니다.

주로 비동기 이터레이터를 위해 설계되었지만, 저는 대부분의 자바스크립트 개발자가 매일 비동기 이터레이터를 사용하지는 않을 거라고 생각합니다. 예를 들어, 링크드인에서 댓글이 하나씩 도착하는 상황을 상상해봅시다:

async function* angryComments() {
  yield "This could have been a plain HTML form.";
  yield "Angular is dead.";
  yield "React is dead.";
  yield "JavaScript was a mistake.";
}

const comments = await Array.fromAsync(
  angryComments()
);

console.log(comments); 
// This could have been a plain HTML form.
// Angular is dead.
// etc

하지만 많은 사람들이 깨닫지 못하는 사실이 있습니다. 바로 이것이 프로미스(Promise)에서도 작동한다는 겁니다.

const opinions = [
  Promise.resolve("Nobody should use classes."),
  Promise.resolve("Signals change everything."),
  Promise.resolve("Microservices ruined software.")
];

const takes = await Array.fromAsync(opinions);

console.log(takes); 

심지어 값을 즉시 매핑할 수도 있습니다:

const comments = await Array.fromAsync(
  angryComments(),
  opinion => opinion.toUpperCase()
);

console.log(comments);

정말 편리하죠. 실제 프로젝트에서 여러 비동기 소스에서 데이터를 받아와 배열로 변환해야 할 때, 이전에 Promise.allmap을 조합해서 쓰던 코드를 훨씬 더 직관적으로 줄일 수 있을 것 같아 기대됩니다.


4. Math.sumPrecise()

MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sumPrecise

이것은 인터넷 자체만큼이나 오래된 부동 소수점 문제를 해결해줍니다 😄 많은 분들이 아시다시피, 자바스크립트에서는:

console.log(0.1 + 0.2);
// 0.30000000000000004

이것이 왜 발생하는지에 대해서는 오늘 깊이 다루지 않겠습니다. 밤을 새워도 모자랄 테니까요 😄

중요한 것은 ES2026이 훨씬 더 정확한 합계를 수행하고 반올림 오류의 누적을 피하는 Math.sumPrecise()를 도입한다는 것입니다.

링크드인 전문가가 생산성을 높이는 모든 것들을 나열하고 있다고 가정해봅시다:

const productivityBoosts = [
  0.1, // cold showers
  0.2, // AI agents
  0.3, // journaling
  0.4, // waking up at 4 AM
];

console.log(
  Math.sumPrecise(productivityBoosts)
);

솔직히 말해서, 대부분의 프런트엔드 개발자는 이 기능을 필요로 하지 않을 것입니다. 일상적인 사용에는 기존의 덧셈도 완벽하게 잘 작동하죠.

하지만 금융, 통계, 시뮬레이션, 또는 AI/ML과 같이 수천 또는 수백만 번의 연산에서 작은 오류가 누적될 수 있는 분야에서 일한다면, 이 기능은 훨씬 더 흥미로워집니다. 저 또한 일반적인 웹 개발에서는 겪어본 적 없지만, 과거에 한 번 수치 계산 로직을 다루는 작업을 할 때 decimal.js 같은 라이브러리를 사용하며 부동 소수점 문제와 씨름했던 기억이 나네요. 이제 표준에서 이런 해결책을 제공한다니 감격스럽습니다.


5. Error.isError()

MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/isError

이 기능은 아마 라이브러리 개발자나 고급 개발자를 위한 것일 겁니다. 아니면 아닐까요? 😉 지금까지는 어떤 것이 실제로 에러인지 확인하는 방법은 대개 다음과 같았습니다:

try {
  throw new Error(
    "Angular is dead."
  );
}
catch (e) {

  console.log(
    e instanceof Error
  );
}

그리고 대부분의 경우, 완벽하게 잘 작동했습니다. 그러다 작동하지 않는 순간이 오기 전까지는요.

만약 에러가 다른 영역(realm)에서 발생했다면, 예를 들어 iframe, Web Worker, 또는 VM 컨텍스트에서 발생했다면—instanceof Error는 갑자기 당신을 배신할 수 있습니다:

const strangeThing =
  window.frames[0].eval(
    "new Error('JavaScript was a mistake.')"
  );

console.log(
  strangeThing instanceof Error
);

// false 

이 문제는 수년 동안 라이브러리 개발자들을 괴롭혀왔고, 그래서 많은 프레임워크와 라이브러리가 에러 감지를 위한 자체 헬퍼 함수를 가지고 있었습니다. 이제 마침내 내장 솔루션이 제공됩니다:

console.log(
  Error.isError(strangeThing)
);

// true

간단하고 아름답습니다. 솔직히 instanceof의 이런 맹점을 처음 알았을 땐 꽤 충격적이었는데, 드디어 표준에서 해결책을 제시해주다니 정말 반갑네요. 이로써 라이브러리 개발자들이 겪었던 골칫거리가 하나 줄어들겠습니다.


6. Base64 for Uint8Array

MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/toBase64

간단히 상기시켜드리자면, Base64는 단순히 이진 데이터를 일반 텍스트로 변환하는 방법입니다. 왜 그렇게 할까요? 많은 프로토콜과 형식이 원시 바이트보다 텍스트를 다루는 것을 훨씬 더 선호하기 때문입니다. 예를 들어, 이미지를 Base64 문자열로 전송할 수 있죠.

어떤 사람은 이렇게 말할 수도 있습니다: "잠깐, Base64는 이미 자바스크립트에 있었잖아!" 네, 맞습니다. 하지만 주로 문자열과 함께 작동했습니다:

btoa("JavaScript was a mistake.");

이진 데이터를 다룰 때는 상황이 빠르게 복잡해졌습니다:

const bytes = new Uint8Array(buffer);

const base64 = btoa(
  String.fromCharCode(...bytes)
);

아주 나쁘지는 않지만, 우아하다고는 할 수 없는 코드였습니다.

이제 우리는 단순히 다음과 같이 작성할 수 있습니다:

const screenshot =
  await fetch(
    "/proof/react-is-dead.png"
  );

const bytes =
  new Uint8Array(
    await screenshot.arrayBuffer()
  );

const base64 =
  bytes.toBase64();

console.log(base64);

훨씬 깔끔해졌습니다. 그리고 아마 더 중요하게는, 6개월 뒤에 다시 봐도 훨씬 더 이해하기 쉬워졌다는 점입니다. 특히 이미지나 파일 데이터를 클라이언트에서 직접 처리해야 할 때 ArrayBufferUint8Array를 사용해야 하는 경우가 많은데, 이 toBase64 메서드는 불필요한 변환 과정을 대폭 줄여줄 겁니다.


7. JSON.parse Source Text Access

MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse

JSON.parse()는 훌륭합니다. 누군가가 엄청나게 큰 숫자를 보내기 전까지는요. 링크드인 전문가가 999조 명의 팔로워를 가지고 있다고 주장하는 상황을 가정해봅시다:

const json = `
{
  "followers":
  999999999999999999999999999999
}
`;

const data = JSON.parse(json);

console.log(data.followers);

이런. 자바스크립트 숫자는 한계가 있어서, 매우 큰 정수는 정밀도를 잃을 수 있습니다.

ES2026이 되면, reviver 콜백이 원본 소스 텍스트에 접근할 수 있게 되어, 큰 값을 안전하게 복구할 수 있습니다:

const data = JSON.parse(
  json,
  (key, value, context) => {

    if (key === "followers") {
      return BigInt(context.source);
    }

    return value;
  }
);

console.log(data.followers);

더 이상 거대한 숫자가 실수로 손상될 일이 없습니다.

우리 대부분은 이 기능을 매일 필요로 하지는 않을 것입니다. 하지만 ID, 금융 데이터 또는 매우 큰 정수 값을 다루고 있다면, 이것은 지독한 버그로부터 여러분을 구할 수 있습니다. 제가 실무에서 결제 관련 데이터를 다룰 때 BigInt로 처리해야 하는 경우가 있었는데, JSON.parse 단계에서부터 원본 텍스트에 접근할 수 있다면 데이터 손실 위험을 미연에 방지할 수 있어 매우 유용할 것 같습니다.


보너스: 위대한 부재자들

저는 이 기능들이 ES2026에 포함되기를 정말 바랐지만, 아쉽게도 1년을 더 기다려야 할 것 같습니다.

음, 어느 정도는요. 최신 브라우저와 런타임은 이미 이 기능들을 구현하기 시작했지만, 아직 ECMAScript 2026 사양의 공식적인 일부는 아닙니다.

이 두 가지는 개인적으로 제가 가장 기대하는 기능들입니다.


Temporal API

TC39 제안: https://github.com/tc39/proposal-temporal

프런트엔드 개발을 충분히 오래 해보셨다면, 날짜와 관련된 최소한 한 번의 트라우마적 경험을 해보셨을 겁니다. 아마 이런 것들이겠죠:

const date = new Date("2026-03-30");

console.log(date);

당신의 시간대에 따라, 축하합니다! 이제 "왜 어제지?"라는 흥미로운 세상에 발을 들여놓으셨습니다.

아니면 모두가 좋아하는 다음 경험을 해보셨을 수도 있습니다:

const today = new Date();

today.setMonth(today.getMonth() + 1);

그리고 갑자기 3월에 한 달을 더했더니 어떻게 5월이 되었는지 디버깅하고 있는 자신을 발견하게 되죠.

아니면 운 좋게도 시간대(time zone)를 다뤄본 경험이 있으실 수도 있습니다.

그때서야 시간 자체가 환상이며, 일광 절약 시간제(daylight saving time)는 프로그래머의 삶을 망치기 위해 특별히 발명되었다는 사실을 깨닫게 될 겁니다. 제가 이 부분 때문에 몇 날 며칠을 고생하면서 눈물을 흘렸던 기억이 생생합니다.

이것이 바로 우리 중 많은 사람들이 Moment.js와 같은 라이브러리를 사용하게 된 이유입니다. 아이러니하게도, Moment.js 관리자들조차 이제는 다른 솔루션을 사용할 것을 권장하고 있죠. 그 후 date-fns, Luxon 등 제정신을 되찾기 위한 다양한 시도들이 등장했습니다.

확실히 도움이 됩니다. 누군가가 "참고로, 호주 사용자들에게 이상한 버그가 보고되고 있어요"라고 말하기 전까지는요. 그리고 대개 거기서부터 진짜 재미가 시작됩니다.

Temporal은 적절한 날짜 및 시간 유형을 도입하여 이 모든 것을 해결하는 것을 목표로 합니다.

다음과 같은 코드 대신:

const now = new Date();

이런 것들을 얻게 됩니다:

const now = Temporal.Now.instant();

const birthday =
  Temporal.PlainDate.from(
    "1987-07-23"
  );

const meeting =
  Temporal.ZonedDateTime.from(
    "2026-11-03T10:00:00[Europe/Warsaw]"
  );

그리고 갑자기 모든 것이 훨씬 더 명시적이 됩니다.

참고로, 저는 Temporal이 실제로 ES2026에 포함되었는지 알아내기 위해 창피할 정도로 많은 시간을 보냈습니다. 결국 TC39 저장소에서 제안이 이미 Stage 4에 도달했지만, 예상 발행 연도는 2027년이라는 것을 명확히 확인했습니다.

그러니 아주 가깝습니다. 정말 가깝습니다. 그리고 저는 정말 기대하고 있습니다 ❤️


명시적 리소스 관리 (using)

TC39 제안: https://github.com/tc39/proposal-explicit-resource-management

이것은 C# 및 Python 개발자들이 조용히 즐기고 있었던 기능 중 하나입니다. 우리 나머지는 try/finally가 완벽하게 괜찮다고 시치미 떼고 있었지만요 😄

때로는 나중에 정리해야 할 것을 만들기도 합니다. 파일, 데이터베이스 연결, 스트림, WebSocket 등이 그렇죠. 오늘날 우리는 대개 다음과 같이 작성합니다:

const webinar =
  createAIAgentsWebinar();

try {

  webinar.start();

}
finally {

  webinar.close();

}

finally 블록은 종종 유닛 테스트를 작성하는 것과 비슷한 열정으로 쓰여집니다. 더 나쁜 것은, 만약 우리가 이것을 완전히 잊어버리면 리소스 누수와 알 수 없는 버그로 이어질 수 있다는 겁니다.

using을 사용하면 스코프를 벗어날 때 자동으로 정리 작업이 이루어집니다:

using webinar =
  createAIAgentsWebinar();

webinar.start();

스코프를 벗어나면 자바스크립트는 자동으로 정리 로직을 호출합니다. 정말 멋지지 않나요?

이것은 평균적인 프런트엔드 개발자에게는 매일 영향을 미치지 않을 수도 있는 기능이지만, Node.js 개발자, 라이브러리 개발자, 그리고 스트림 및 파일 작업을 하는 사람들에게는 엄청나게 유용할 수 있습니다. Node.js 프로젝트에서 파일 스트림이나 DB 연결 관리할 때 try/finally가 불편했던 경험이 한두 번이 아닌데, using 키워드 하나로 얼마나 코드가 간결해질지 벌써부터 기대됩니다. 특히 에러 발생 시 리소스가 제대로 해제되지 않아 문제가 생기던 골치 아픈 상황들을 이제 훨씬 우아하게 해결할 수 있게 될 겁니다.


휴…

솔직히 이 글이 처음 예상했던 것보다 훨씬 더 많은 작업이 필요했습니다 😅 짧고 쉬운 글이 될 줄 알았는데, 어쩌다 보니 TC39 제안, MDN 페이지, 브라우저 구현을 파고들고, Temporal이 실제로 ES2026에 포함되었는지 아닌지를 알아내려고 애쓰고 있더라고요.

하지만 그만한 가치가 있었고, 즐거우셨기를 바랍니다 ❤️

여러분은 어떠세요? 어떤 기능이 가장 놀라웠나요? 그리고 여러분도 Temporal이 마침내 날짜 관련 모든 고통으로부터 우리를 해방시켜 주기를 간절히 기다리고 계신가요?

(아니면 다음 주에 링크드인 전문가들이 아마 그렇게 말할 겁니다 😇)


이 글이 유익했다면, LinkedIn에서 저와 연결되어 주세요 😊


원문: https://dev.to/sylwia-lask/7-new-javascript-features-and-2-im-still-waiting-for-2ck8 수집일: 2026-06-26 00:26:14