자바스크립트 개발 10년차, ES2026 신기능 7가지와 제가 목마른 2가지
2026. 6. 27.
자바스크립트 개발 10년차, ES2026 신기능 7가지와 제가 목마른 2가지
두 주 전, 가볍고 쉬운 글만 쓰겠다고 약속(사실은 저 자신에게 한 약속이죠!)했던 것 기억하시나요? 음… 그 약속을 깨버렸습니다 😅 지난주에는 생각할 수 있는 가장 재미없는 주제 중 하나인 레거시 애플리케이션에 대한 엄청난 글을 발행했거든요. 그런데도 DEV 편집팀에서 좋게 봐주셨는지, 주간 Top 7 게시물에 포함시켜주셨네요! ❤️
하지만 오늘은 드디어 좀 더 가벼운 주제로 돌아왔습니다. 그리고 이 주제는 분명 더 많은 관심을 받을 가치가 있다고 생각해요.
요즘 우리는 AI 에이전트에 대해 많은 시간을 할애하며 이야기하고 있죠. 저도 그 분야가 얼마나 매력적인지 알기 때문에 충분히 이해합니다. 하지만 가끔 우리가 매일 사용하는 프로그래밍 언어 자체도 계속 진화하고 있다는 사실을 잊고 지내는 건 아닐까 하는 생각이 들어요.
저는 대부분의 시간을 자바스크립트(음… 사실은 타입스크립트죠 😄) 코드를 작성하는 데 보냅니다. 지난 몇 년간 생태계가 정말 많이 성숙해졌어요. 더 이상 끝없는 React vs Angular 전쟁을 벌이지도 않고요. 한 달은 모두가 Redux를 찬양하다가, 6개월 뒤에는 과도하게 설계된 괴물이라며 모든 애플리케이션에서 제거하던 시절도 대부분 지났죠 😅.
하지만 생태계와 ECMAScript 표준 모두 계속해서 진화하고 있습니다.
2015년 프런트엔드 개발의 판도를 완전히 바꾼 전설적인 ES6 릴리스 이후, TC39 위원회는 매년 새로운 기능을 출시하는 반복적인 접근 방식으로 전환했습니다.
그리고 좋은 점은, 우리가 최신 브라우저나 런타임을 사용하고 있다면, 이러한 기능들을 거의 즉시 사용할 수 있다는 겁니다.
그렇다면 ECMAScript 2026에서는 어떤 기능들을 만날 수 있을까요?
아, 그리고 글을 좀 더 재미있게 만들기 위해, 이 글의 모든 코드 예시는 강한 주관을 가진 가상의 ‘링크드인 구루'들의 속마음을 바탕으로 합니다. 실제 인물과의 유사점은 당연히 순전히 우연일 뿐입니다 😇.
이 시리즈의 다른 글:
- Stop Installing Libraries: 10 Browser APIs That Already Solve Your Problems
- 9 Things You're Overengineering — The Browser Already Solved Them
- 16 Modern JavaScript Features That Might Blow Your Mind
1. Map.prototype.getOrInsert() / Upsert
이 기능은 "잠깐… 왜 이게 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);
계산된 버전도 사용할 수 있습니다.
const opinions = new Map();
opinions
.getOrInsertComputed(
"Nobody writes clean code anymore",
() => []
)
.push("DDD Purist");
정말 훨씬 간결하고 읽기 쉽지 않나요? 캐싱 로직에도 완벽하게 적용할 수 있습니다. 코드가 줄어들고 API가 훨씬 멋져졌습니다. 제가 실무에서 Map을 사용할 때마다 특정 키가 없으면 기본값을 설정하고 값을 추가해야 하는 번거로움이 있었는데, 이 기능이 도입되면 코드가 훨씬 직관적이고 오류도 줄어들 것 같아요.
2. 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]);
제 생각에 이러한 이터레이터 기능들은 마땅히 받아야 할 만큼의 관심을 받지 못하고 있어요. 이터레이터 헬퍼 기능들을 접했을 때부터 이렇게 스트림을 합치는 기능이 있었으면 좋겠다고 생각했는데, 마침내 등장했네요. 대용량 데이터 처리나 이벤트 스트림을 다룰 때 유용하게 활용할 수 있을 겁니다.
3. Array.fromAsync()
이것은 우리가 사랑하는 Array.from()의 비동기 버전이라고 할 수 있습니다.
주로 비동기 이터레이터(async iterators)를 위해 설계되었지만, 저는 대부분의 자바스크립트 개발자가 매일 비동기 이터레이터를 사용하지는 않을 거라고 생각합니다. 예를 들어, 링크드인에서 댓글이 하나씩 도착하는 상황을 상상해 보세요.
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);
꽤 편리하죠. 솔직히 async iterator는 제가 매일 다루는 영역은 아니지만, 프로미스 배열을 한 번에 처리하고 싶을 때 이 기능을 써보니 코드가 훨씬 직관적이라 놀랐습니다. 기존에는 Promise.all과 map을 조합해야 했는데, 이제는 한 줄로 깔끔하게 해결되겠네요.
4. Math.sumPrecise()
이것은 인터넷 자체만큼이나 오래된 부동 소수점 문제를 해결합니다 😄 많은 분들이 아시다시피, 자바스크립트에서는:
console.log(0.1 + 0.2);
// 0.30000000000000004
왜 이런 일이 발생하는지는 오늘 깊이 파고들지 않겠습니다. 그러다가는 밤을 새야 할 테니까요 😄.
중요한 것은 ES2026에 Math.sumPrecise()가 도입되어 훨씬 더 정확한 합계를 수행하고 반올림 오류의 누적을 방지한다는 점입니다.
링크드인 구루가 생산성을 높인다고 주장하는 모든 것을 나열한다고 가정해 봅시다.
const productivityBoosts = [
0.1, // cold showers (냉수 샤워)
0.2, // AI agents (AI 에이전트)
0.3, // journaling (일기 쓰기)
0.4, // waking up at 4 AM (새벽 4시 기상)
];
console.log(
Math.sumPrecise(productivityBoosts)
);
솔직히 말해서, 대부분의 프런트엔드 개발자는 아마 이 기능이 필요하지 않을 겁니다. 일반적인 덧셈만으로도 일상적인 사용에는 완벽하게 작동하죠.
하지만 금융, 통계, 시뮬레이션, 또는 AI/ML 분야에서 수천 또는 수백만 번의 연산에 걸쳐 작은 오류가 누적될 수 있는 작업을 한다면, 이 기능은 훨씬 더 흥미로워집니다. 저도 금융 관련 데이터를 다룰 일이 있을 때마다 부동 소수점 오차 때문에 골머리를 앓았는데, 이제는 한시름 놓을 수 있겠어요. 중요한 수치 정확도가 필요한 곳에서 빛을 발할 기능입니다.
5. Error.isError()
이 기능은 아마 라이브러리 개발자와 고급 개발자를 위한 것일 겁니다. 아니면 아닐까요? 😉 지금까지는 어떤 것이 실제로 에러인지 확인하는 방법은 대개 다음과 같았습니다.
try {
throw new Error(
"Angular is dead."
);
}
catch (e) {
console.log(
e instanceof Error
);
}
대부분의 경우 이것은 완벽하게 작동했습니다. 문제가 생기기 전까지는요.
만약 에러가 다른 영역(예: 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
간단하고 아름답습니다. 저도 예전에 iframe이나 Web Worker 간 에러를 처리하다가 instanceof가 제대로 동작하지 않아 당황했던 경험이 있어요. 특히 여러 컨텍스트를 넘나드는 복잡한 아키텍처에서 라이브러리를 만들 때는 이런 동등성 검사가 필수인데, 이제 표준으로 제공되니 훨씬 신뢰할 수 있게 되었네요.
6. Base64 for Uint8Array
간단한 리마인더: 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개월 후에 다시 봐도 훨씬 이해하기 쉬워졌다는 점입니다. 예전에는 바이너리 데이터를 Base64로 인코딩할 때 저 String.fromCharCode 꼼수를 써야 했는데, 볼 때마다 뭔가 찜찜했거든요. 이제야 직관적인 API로 깔끔하게 처리할 수 있게 됐으니, 이미지나 파일 데이터를 다루는 웹 애플리케이션 개발자들에게는 분명 반가운 소식일 겁니다.
7. JSON.parse Source Text Access
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, 금융 데이터 또는 거대한 정수 값을 다루고 있다면, 이것은 지독한 버그로부터 우리를 구해줄 수 있습니다. 대규모 정수 ID나 금액 데이터를 JSON으로 주고받을 때마다 BigInt 변환 로직을 직접 구현해야 했는데, 이제 context.source 덕분에 훨씬 안전하고 편리하게 처리할 수 있게 됐습니다. 특히 백엔드 API와의 연동 시 발생할 수 있는 데이터 손실 위험을 줄여줄 거예요.
보너스: 위대한 부재자들
저는 이 기능들이 ES2026에 포함되기를 정말 바랐지만, 아쉽게도 1년을 더 기다려야 할 것 같습니다.
음, 어느 정도는요. 최신 브라우저와 런타임은 이미 이 기능들을 구현하기 시작했지만, 아직 ECMAScript 2026 공식 사양의 일부는 아닙니다.
이 두 가지는 개인적으로 제가 가장 기대하는 기능들입니다.
Temporal API
프런트엔드 개발을 충분히 오래 했다면, 날짜와 관련된 적어도 한 번의 트라우마적인 경험이 있을 겁니다. 아마도 이런 식이었겠죠?
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년이라고 명시되어 있는 것을 발견했습니다.
그러니 거의 다 왔습니다. 아주 가깝죠. 정말 기대됩니다 ❤️ 날짜와 시간 처리는 개발자라면 누구나 한 번쯤 악몽을 겪어봤을 겁니다. 저 역시 타임존 때문에 밤새 씨름했던 적이 한두 번이 아니죠. Temporal은 정말 가뭄의 단비 같은 존재가 될 거예요. 명시적인 객체 모델 덕분에 날짜 계산이나 시간대 변환 로직이 훨씬 견고해질 것이라고 믿어 의심치 않습니다.
명시적 리소스 관리 (using)
이것은 C#과 Python 개발자들이 try/finally가 완벽하게 괜찮다고 다른 개발자들이 가장하는 동안 조용히 즐겨왔던 기능 중 하나입니다 😄
때로는 나중에 정리해야 할 것(파일, 데이터베이스 연결, 스트림, 웹소켓 등)을 생성할 때가 있습니다. 오늘날 우리는 보통 다음과 같이 작성합니다.
const webinar =
createAIAgentsWebinar();
try {
webinar.start();
}
finally {
webinar.close();
}
finally 블록은 종종 유닛 테스트를 작성하는 것과 같은 열정으로 작성됩니다. 더 나쁜 것은, 만약 우리가 그것을 완전히 잊어버린다면, 리소스 누수와 신비한 버그로 이어질 수 있다는 겁니다.
using을 사용하면 스코프를 벗어날 때 정리가 자동으로 이루어집니다.
using webinar =
createAIAgentsWebinar();
webinar.start();
스코프를 벗어나면 자바스크립트는 자동으로 정리 로직을 호출합니다. 정말 멋지지 않나요?
이것은 일반적인 프런트엔드 개발자에게 매일 영향을 미치지는 않을 수 있지만, Node.js 개발자, 라이브러리 개발자, 그리고 스트림 및 파일을 다루는 사람들에게는 엄청나게 유용할 수 있는 기능입니다. C#이나 Python의 using 문법을 보면서 자바스크립트에도 이런 게 있으면 좋겠다고 늘 생각했어요. 특히 Node.js 서버 개발에서는 데이터베이스 연결이나 파일 핸들을 다룰 때 리소스 누수를 방지하는 데 큰 도움이 될 겁니다. 코드를 훨씬 안전하고 간결하게 만들 수 있을 거예요.
휴…
솔직히 말씀드리자면, 이 글이 제가 처음 예상했던 것보다 훨씬 더 많은 작업이 필요했습니다 😅 짧고 쉽게 쓸 생각이었는데, 어쩌다 보니 TC39 제안서와 MDN 페이지, 브라우저 구현 사항들을 샅샅이 뒤지고 Temporal이 정말 ES2026에 포함되었는지 아닌지 파악하느라 진땀을 뺐네요.
하지만 그럴 만한 가치가 있었고, 여러분도 즐겁게 읽으셨기를 바랍니다 ❤️
여러분은 어떠셨나요? 어떤 기능이 가장 놀라웠나요? 그리고 여러분도 Temporal이 마침내 날짜와 관련된 모든 고통에서 우리를 해방시켜 주기를 간절히 기다리고 계신가요?
(아니면 적어도 다음 주에 링크드인 구루들이 우리에게 그렇게 말할 겁니다 😇)
이 글이 즐거우셨다면, 링크드인에서 저와 연결되어 주세요 😊
원문: https://dev.to/sylwia-lask/7-new-javascript-features-and-2-im-still-waiting-for-2ck8 수집일: 2026-06-27 00:24:29