NodeJS v25 Release
Node.js — Node.js v25.0.0 (Current)
Node.js® is a free, open-source, cross-platform JavaScript runtime environment that lets developers create servers, web apps, command line tools and scripts.
nodejs.org
Node 25버전이 며칠 전, 25년 10월 15일에 릴리즈되었습니다. 자세한 변경 사항들은 위 공식 블로그에서, 커밋들을 확인해보시면 됩니다.
V8 14.1 적용
기존 24버전의 Node에서는 13.6버전의 V8 엔진을 사용했습니다. 메이저 버전의 업데이트에 따라 V8을 14.1로 업데이트하였습니다.
아래는 V8 버전의 업데이트에 따라 자연스레 Node에도 적용된 변경사항입니다.
1. JSON.Stringify 최적화
두 달 전인 8월에 V8 공식 블로그에 JSON.stringify를 두 배 이상 빠르게 만드는 방법이라는 제목으로 포스팅이 게시되었습니다.
핵심은, 사이드 이펙트가 없는 직렬화를 감지했을 때, Fast Path를 사용할 수 있도록 개선했다는 내용입니다.
여기에 문자열 이스케이프 경로 개선(플랫폼에 따라 SIMD 활용)과 number 처리 최적화가 얹어져 2+a배의 성능 개선이 이루어 졌다고 합니다.
반대로 getter, proxy, 순환참조, toJSON 커스터마이징, pretty print 등 직렬화 과정에서 사이드 이펙트는 Fast Path가 아닌 일반 경로로 폴백합니다. V8의 직렬화 퍼포먼스 개선의 이점을 얻기 위해서는, 개발자가 직렬화 과정에서 사이드 이펙트가 언제 발생하는지 인지하는 게 중요할 것 같습니다.
const N = 200_000;
const safe = Array.from({ length: N }, (_, i) => ({ id: i, ok: true, n: i|0, s: "x" }));
// 1) Fast path 기대 (무부작용)
console.time("safe");
JSON.stringify(safe);
console.timeEnd("safe");
// 2) replacer 사용 → 일반 경로
console.time("replacer");
JSON.stringify(safe, (k, v) => v);
console.timeEnd("replacer");
// 3) space 사용(pretty print) → 일반 경로
console.time("space");
JSON.stringify(safe, null, 2);
console.timeEnd("space");
// 4) toJSON 개입 → 일반 경로
const withToJSON = { ...safe[0], toJSON(){ return "x"; } };
console.time("toJSON");
JSON.stringify(withToJSON);
console.timeEnd("toJSON");
2. Uint8Array 내장 인코딩 지원
ECMAScript에서 최근 Uint8Array에서 직접 Base64, Hex 인코딩/디코딩을 다루는 표준 API가 구현되었습니다.
Unit8Array는 바이너리를 다루는 바이트 배열(Typed Array)로 이미지, 파일, 압축, 암호화, 스트리밍 등에 사용되는 바이너리를 다룰 때 기본 자료 구조로 활용되는 것들 중 하나입니다.
25년 9월 기준의 최신 브라우저나 JS 엔진에서 사용 가능하며 자세한 내용은 MDN을 확인해보시면 좋을 것 같습니다.
Uint8Array - JavaScript | MDN
Uint8Array is currently the only TypedArray subclass that has additional methods compared to other typed arrays. Because of its nature as a generic byte array, it is the most suitable for working with arbitrary binary data. It supports two sets of methods
developer.mozilla.org
// 1) 기본: base64 ↔ bytes
const bytes = Uint8Array.fromBase64("aGVsbG8="); // "hello"
const b64 = bytes.toBase64(); // "aGVsbG8="
// 2) 옵션: base64url + 패딩 생략(프로토콜 규약에 맞춤)
const b64url = bytes.toBase64({ alphabet: "base64url", omitPadding: true });
// 3) 부분 디코딩: 프리할당 버퍼에 직접 채우기(대용량/스트리밍 친화)
const buf = new Uint8Array(1024 * 1024);
const { read, written } = buf.setFromBase64(b64, { lastChunkHandling: "strict" });
// 4) Hex
const hex = bytes.toHex(); // "68656c6c6f"
const data = Uint8Array.fromHex("deadbeef"); // Uint8Array [222,173,190,239]
기존의 Node에서는 보통 Buffer에 의존했는데요.
Buffer.from(bytes).toString('base64')
Buffer.from(str, 'base64')
이번 업데이트로 Node와 브라우저가 동일한 코드를 사용할 수 있게 되었고, 특히 setFromBase64/Hex가 직접 버퍼를 채우는 방식이기 때문에 중간 문자열, 메모리 복사를 줄이고 큰 페이로드에서 GC Pressure을 낮추고, 메모리 사용을 절감할 수 있습니다. 또한 옵션으로 유니온 리터럴 타입을 사용하여 옵션들을 표준화했습니다. 코드 일관성과 퍼포먼스 둘 다 개선했다고 볼 수 있겠습니다.
3. JIT 파이프라인 변경
V8의 JavaScript 실행 파이프라인은 여러 단계로 구성되어있습니다.

- Ignition: 인터프리터
- SparkPlug: 베이스라인 컴파일러
- Maglev: 중간 계층 최적화 컴파일러
- TurboFan: 최적화 컴파일러
Maglev는 Chrome M117에 도입된 새로운 최적화 컴파일러로, 기존 SparkPlug와 TurboFan 사이에 위치합니다. 컴파일 속도 측면에서 Maglev는 SparkPlug보다 약 10배 느리고, TurboFan보다 약 10배 빠르다고 합니다. Maglev는 기존 두 컴파일러 사이의 간격을 좁혀 빠른 최적화와 균형 잡힌 성능, 그리고 점진적 워밍업을 제공합니다. 보다 더 자세한 내용은 공식 블로그 내용을 참조하시면 좋습니다.
Maglev - V8’s Fastest Optimizing JIT · V8
In Chrome M117 we introduced a new optimizing compiler: Maglev. Maglev sits between our existing Sparkplug and TurboFan compilers, and fills the role of a fast optimizing compiler that generates good enough code, fast enough. Background # Until 2021 V8 had
v8.dev
4. JSPI(JavaScript Promise Integration) 지원
Node 25부터는 JSPI를 지원합니다.
WASM은 기본적으로 동기적인 실행 모델을 가정합니다. 하지만 웹 환경의 많은 API들은 비동기적입니다.
기존에는 이 문제를 해결하기 위해 Binaryen의 ASYNCIFY 같은 복잡한 변환 도구를 사용해야 했습니다. 이로 인해 코드 크기가 증가하고, 런타임 오버 헤드가 자연스레 증가하며 빌드 프로세스 또한 복잡해지는 문제가 있습니다.
Node 25부터는 JSPI를 통해 WASM 애플리케이션이 동기적으로 작성되어 있더라도, JavaScript의 비동기 API를 자연스럽게 사용할 수 있게 해줍니다.
// 1) WebAssembly.Suspending: 비동기 함수를 래핑
const importObject = {
env: {
asyncFetch: new WebAssembly.Suspending(async (url) => {
const response = await fetch(url);
return await response.text();
})
}
};
// 2) WebAssembly.promising: WASM 함수를 Promise 반환 함수로 변환
const wasmModule = await WebAssembly.instantiate(wasmBytes, importObject);
const promising = new
WebAssembly.promising(wasmModule.instance.exports.main);
await promising();
Introducing the WebAssembly JavaScript Promise Integration API · V8
Posted by Francis McCabe, Thibaud Michaud, Ilya Rezvov, Brendan Dahl.
v8.dev
여기까지가, V8 업데이트로 인한 Node v25의 변경사항입니다.
아래부터는 Node의 별개 커밋들로 변경된 사항들에 대해 알아보겠습니다.
Permission Model: --allow-net 추가
Node는 기본적으로 모든 시스템 리소스에 대한 접근 권한을 갖고 있었습니다. 이는 편리하지만 보안상의 문제가 생길 수 있습니다.
이를 개선하기 위해 Node v20에 Permission Model이 도입되었고, v25에서는 네트워크 권한 제어가 추가되었습니다.
Permission Model을 활성화하면, 명시적으로 허용하지 않은 모든 작업이 차단됩니다.
# Permission Model 없이 (기존 방식)
node index.js # 모든 권한 허용
# Permission Model 활성화 (네트워크 차단됨)
node --permission index.js
# Error: connect ERR_ACCESS_DENIED Access to this API has been restricted.
# 네트워크 권한 허용
node --permission --allow-net index.js # 정상 작동
런타임에서도 권한을 확인할 수 있습니다.
if (process.permission) {
console.log(process.permission.has('net')); // true or false
}
async function fetchData(url) {
if (!process.permission || !process.permission.has('net')) {
throw new Error('Network access not permitted');
}
return fetch(url);
}
ErrorEvent의 글로벌 객체화
브라우저에서는 ErrorEvent 인터페이스가 스크립트나 파일의 에러와 관련된 정보를 제공하는 표준 WEB API입니다.
하지만 Node에서 이를 사용하려면 별도의 polyfill을 설치하고, 브라우저와 Node환경을 분기 처리하며, 플랫폼(OS)별 에러 핸들링 코드를 별도로 작성해야했습니다.
// 기존 방식: 플랫폼 분기
if (typeof ErrorEvent !== 'undefined') {
// 브라우저 환경
window.addEventListener('error', (event) => {
console.log(event.message, event.filename, event.lineno);
});
} else {
// Node.js 환경: 다른 방식 사용
process.on('uncaughtException', (error) => {
console.log(error.message, error.stack);
});
}
Node v25부터 ErrorEvent가 글로벌 객체로 사용 가능합니다. 자세한 구현사항은 아래 커밋을 확인해보시면 좋습니다.
lib: expose global ErrorEvent · nodejs/node@663554a
PR-URL: https://github.com/nodejs/node/pull/58920 Reviewed-By: Zeyu "Alex" Yang <himself65@outlook.com> Reviewed-By: Matthew Aitken <maitken033380023@gmail.com> Reviewed-By: J...
github.com
WebStorage 기본 활성화
Node v22 이전까지는 localStorage, sessionStorage 같은 WebStorage API를 사용하려면 --experimental-webstorage 플래그가 필요했는데, 이 부분을 Node v25부터는 기본적으로 활성화 상태로 애플리케이션이 실행됩니다. 자세한 변경 내용은 아래 커밋을 확인해보시면 좋습니다.
src: unflag --experimental-webstorage by default · nodejs/node@3312e4e
PR-URL: https://github.com/nodejs/node/pull/57666 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Edy Silva <edigleyssonsilva@gmail.com> Reviewed-By: Colin Ihrig <...
github.com
Portable한 Compile Cache 추가
Node v22.8.0부터 내장 컴파일 캐시가 도입되었었습니다. 컴파일은 JS 실행 전 항상 수행되어야 하기 때문에, 내장 컴파일 캐시가 도입되기 이전에는 반복적인 컴파일 비용 문제와 그에 따른 느려지는 애플리케이션 부트스트래핑 등의 문제가 있었을 것이라 생각됩니다.
이 내장 컴파일 캐시에 상대 경로를 지정하여 재사용 할 수 있게, 즉 Portable한 패치가 이루어졌습니다.
node --compile-cache --compile-cache-portable app.js
이를 통해 CI/CD 환경이나 컨테이너, 혹은 협업 과정 등 실제 컴파일이 필요한 테스트, 배포 단계에서 불필요하게 중복 컴파일을 하는 일이 사라지게 될 것으로 기대합니다.
# 1. 로컬 개발
node --compile-cache --compile-cache-portable dev-server.js
# 2. CI/CD 파이프라인 (e.g. Git Actions)
- name: Build and Test
run: |
node --compile-cache --compile-cache-portable build.js
- name: Deploy # 캐시를 아티팩트로 저장
run: |
node --compile-cache app.js # 캐시 재사용으로 빠른 배포
# 3. Docker
FROM node:25
WORKDIR /app
COPY . .
# 빌드 시 캐시 생성
RUN node --compile-cache --compile-cache-portable build.js
# 런타임에서 캐시 활용
CMD ["node", "--compile-cache", "app.js"]
마치며
Node v25의 주요 변경 사항들을, 신규 피쳐 위주로 알아봤습니다.
더 많은 변경사항이 있고, 특히 이 글에서 다루지 않은 Deprecated들을 포함하여 더 자세하게 알고 싶으신 분들은 릴리즈 노트를 활용해보시면 좋을 것 같습니다.
개인적으로 당장 하나씩 씹어먹어보고 싶지만, 11월까지 바쁜 개인 일정을 마무리하고, 나중에 깊게 공부할 수 있도록 주제별로 정리만 간단하게 했습니다. 특히 V8 관련된 공부를 가장 먼저 깊게 해 볼 생각입니다. 메인 스택을 JS, Node으로 계속 갖고 가기 위해 반드시 하나씩 깊게 독파하는 포스팅으로 찾아뵙겠습니다 하하..
References
1. NodeJS 공식 문서
NodeJS 25 릴리즈노트: https://nodejs.org/en/blog/release/v25.0.0
Github NodeJS 25 Realease Tags: https://github.com/nodejs/node/releases/tag/v25.0.0
NodeJS Docs API: https://nodejs.org/api
NodeJS Docs API - Permissions: https://nodejs.org/api/permissions.html
2. V8
JSON.Stringify 최적화: https://v8.dev/blog/json-stringify
Maglev(JIT Compiler): https://v8.dev/blog/maglev
JSPI(Javscript Promise Integration): https://v8.dev/blog/jspi
3. ECMAScript 관련
TC39 ArrayBuffer Base64 Spec: https://tc39.es/proposal-arraybuffer-base64/spec
MDN Uint8Array 관련 API들: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array
4. 기타
NodeJS Permission Model References - 1: https://www.nodejs-security.com/learn/nodejs-runtime-security/nodejs-permissions-model
NodeJS Permission Model References - 2: https://dev.to/andreasbergstrom/introducing-the-nodejs-permission-model-enhanced-security-and-granular-control-3md0
NodeJS Permssion Model References - 3: https://www.nearform.com/blog/adding-a-permission-system-to-node-js/
Node v24 Referecnes: https://blog.logrocket.com/node-js-24-new
Node v22 References: https://medium.com/@branimir.ilic93/exploring-node-js-22-maglev-top-level-await-v8-engine-update-and-more-d8e9a8d847f2
V8 JSON.stringify Optimization References: https://dev.to/figsify/the-invisible-optimization-that-sped-up-the-web-how-v8-supercharged-jsonstringify-ke9
MDN Web Storage API: https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API
MDN ErrorEvent: https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent































