Node.js v18 의 새로운 기능을 살펴보자
Node.js v18 의 새로운 기능에 대하여
  • NodeJS

아직 LTS는 아니지만 곧 10월에 LTS로 전환되는 Node 18의 새로운 기능에 대해 살펴보고자 합니다.

Node 18은 2022년 4월에 최초 릴리스되어 오는 2022년 10월에 LTS로 전환될 예정입니다. (문서 참고) 필자는 Node 16버전을 사용하고 있는데, 곧 18버전으로 업데이트를 위해 어떤 기능들이 추가되었는지 살펴보겠습니다.

혹시 글을 읽으며 테스트해 보실 분들께서는 nvm 을 통해 node 18버전을 설치해 볼 것을 권장해 드립니다.

Node.js v18에서 변화하는 주요 기능

  • Experimental fetch API
  • Web Streams API
  • HTTP Timeouts
  • Experimental test runner
  • V8 JavaScript engine is updated to V8 10.1

Experimental fetch API

브라우저 환경에서만 사용 가능하던 fetch API 가 실험적으로 도입되었습니다.

function fetch(input: RequestInfo, init?: RequestInit): Promise<Response>

init은 선택적 매개변수로, method, headers, body, mode, credentials, cache, redirect, referrer, referrerPolicy, integrity, keepalive, signal 를 지원합니다.

const response = await fetch('https://nodejs.org/api/documentation.json');
if (response.ok) {
  const data = await response.json();
  console.log(data);
}

fetch는 네트워크 오류가 발생한 경우에 throw가 발생합니다. response.ok는 fetch가 성공했는지 여부를 응답에서 확인해야 합니다. 실패하면 추가 정보인 response.status와 response.statusText 를 제공합니다.

성공했을 때의 response의 interface는 다음과 같습니다.

interface Response extends Body {
  readonly headers: Headers;
  readonly ok: boolean;
  readonly redirected: boolean;
  readonly status: number;
  readonly statusText: string;
  readonly type: ResponseType;
  readonly url: string;
  clone(): Response;
}

본문 메세지를 가져오기 위해서는 response.json()을 통해 JSON 구문을 분석할 수 있습니다.

Node 18버전에 추가된 fetch 는 여전히 실험적 기능이기 때문에 사용에 주의해야 합니다.
http 통신을 위해서라면 axios 설치해서 사용해왔는데, 이를 대체할지 궁금해지네요.

Web Streams API

이전에는 웹을 통해 비디오나 큰 용량의 데이터를 처리하기 위해서는 전체 파일을 다운로드하고 적절한 형식으로 역직렬화될 때 까지 기다렸다가 모든 데이터가 수신되면 처리해야 했습니다. (기타 라이브러리를 통해 스트림 처리가 가능하긴 했습니다만)

Node 18의 Web Streams API 에서는 버퍼, Blob를 생성할 필요 없이 네트워크를 통해 수신된 데이터 스트림에 프로그래밍 방식으로 액세스하고 청크로 처리할 수 있습니다.
또한 스트림 시작 또는 종료 시점을 감지하고, 스트림에 연결하거나 오류 처리, 필요에 따라 스트림을 취소하거나 스트림을 읽는 속도를 조절할 수 있습니다.

Node 18에서부터 전역 범위로 제공하는 ReadableStream의 간단한 사용 예제입니다.

const getData = () => {
  fetch('https://example.com')
    .then((response) => {
      // response.body = ReadableStream { locked: false, state: 'readable', supportsBYOB: false }
      return response.body;
    })
    .then((responseBody) => {
      const reader = responseBody.getReader();
      // reader = ReadableStreamDefaultReader {
      //   stream: ReadableStream { locked: true, state: 'readable', supportsBYOB: false },
      //   readRequests: 0,
      //   close: Promise { <pending> }
      // }

      return new ReadableStream({
        start(controller) {
          // controller = ReadableStreamDefaultController {}

          /**
           * Push handles each data chunk
           */
          function push() {
            /**
             * @param {boolean} done - Whether the stream is done
             * @param {Uint8Array} done - The stream state
             */
            reader.read().then(({ done, value }) => {
              // when there is no more data to read
              if (done) {
                console.log('done =', done); 
                // done = true
                controller.close();
                return;
              }

              // get the data and put it on queue
              controller.enqueue(value);

              console.log(done, value);
              push();
            });
          }

          // start getting data
          push();
        },
      });
    })
    .then((stream) => {
      // stream = ReadableStream { locked: false, state: 'readable', supportsBYOB: false }

      // create a Response with the stream content
      return new Response(stream, {
        headers: { 'Content-Type': 'text/html' },
      }).text();
    })
    .then((result) => {
      // result = {"fact":"Tabby cats are thought to get their name from Attab, 
      // a district in Baghdad, now the capital of Iraq.","length":100}
    });
};

getData();

HTTP Timeouts

server.headersTimeout을 통해 http 통신의 시간제한을 밀리초 단위로 제한할 수 있습니다. 시간제한이 만료되었을 땐, 클라이언트로는 408 코드로 응답을 하게됩니다.

import express from 'express';
import path, { dirname } from 'path';
import { fileURLToPath } from 'url';
const app = express();
const __dirname = dirname(fileURLToPath(import.meta.url));
app.use(express.static(path.join(__dirname, '../build')));

app.get('/*', (req, res) => {
  setTimeout(() =>
  res.sendFile(path.join(__dirname, '../build', 'index.html')), 1000);
});

const server = app.listen(8080);

console.log('headersTimeout =', server.headersTimeout);
console.log('requestTimeout =', server.requestTimeout);

Node 17이하 버전에서는 headersTimeout는 60000 (60초), requestTimeout는 0 (0초) 로 기본값이 설정되어있습니다.

Node 18이상에서는 headersTimeout는 60000으로 동일하며 requestTimeout는 300000 (5분)으로 기본 설정됩니다.
Node 부터는 서버가 역방향 프록시없이 배포되는 경우 잠재적인 서비스거부 공격을 방지하기 위해서 두 시간 모두 0이 아닌값으로 세팅해야 합니다.

Experimental Test Runner

Test Runner 모듈 역시 아직은 실험 단계입니다.

모듈을 가져와서 node:test단위 테스트를 작성하고 결과를 TAP(Test Anything Protocol) 형식으로 보고할 수 있습니다. 이는 JavaScript의 테스팅 프레임워크인 Jest 와 비슷합니다.

import test from 'node:test';
import assert from 'assert';

test('synchronous passing test', (t) => {
  assert.strictEqual(1, 1);
});

test('synchronous failing test', (t) => {
  assert.strictEqual(1, 2);
});

이 역시 기존의 Jest를 대체할 수 있을지 기대해보겠습니다.

V8 JavaScript Engine Is Updated to V8 10.1

V8 JavaScript 엔진이 10.1로 업데이트 되었습니다.

클래스 필드 및 개인 클래스 메서드의 향상된 성능 외에도 아래의 새로운 기능이 있습니다.

findLast와 findLastIndex 함수

기존 find, findIndex 는 배열의 0번째 index부터 순회하는 함수입니다. 이와 반대인 findLast, findLastIndex가 추가되었습니다.

Intl.Locale API 개선

Intl.Locale API에 7개의 새로운 속성이 추가되었습니다.
calendars, collations, hourCycles, numberingSystems, timeZones, textInfo, weekInfo

const englishUsLocale = new Intl.Locale('en-us');
console.log(englishUsLocale.calendars);
// [ 'gregory' ]
console.log(englishUsLocale.collations);
// [ 'emoji', 'eor' ]
console.log(englishUsLocale.hourCycles);
// [ 'h12' ]
console.log(englishUsLocale.numberingSystems);
// [ 'latn' ]
console.log(englishUsLocale.timeZones);
// [
//   'America/Adak',
//   'America/Anchorage',
//   'America/Boise',
//   'America/Chicago',
//   'America/Denver',
//   'America/Detroit',
//   'America/Indiana/Knox',
//   'America/Indiana/Marengo',
//   'America/Indiana/Petersburg',
//   'America/Indiana/Tell_City',
//   'America/Indiana/Vevay',
//   'America/Indiana/Vincennes',
//   'America/Indiana/Winamac',
//   'America/Indianapolis',
//   'America/Juneau',
//   'America/Kentucky/Monticello',
//   'America/Los_Angeles',
//   'America/Louisville',
//   'America/Menominee',
//   'America/Metlakatla',
//   'America/New_York',
//   'America/Nome',
//   'America/North_Dakota/Beulah',
//   'America/North_Dakota/Center',
//   'America/North_Dakota/New_Salem',
//   'America/Phoenix',
//   'America/Sitka',
//   'America/Yakutat',
//   'Pacific/Honolulu'
// ]
console.log(englishUsLocale.textInfo);
// { direction: 'ltr' }
console.log(englishUsLocale.weekInfo);
// { firstDay: 7, weekend: [ 6, 7 ], minimalDays: 1 }

마지막으로

Node.js 18에는 여러 가지 새로운 기능과 개선 사항이 있습니다.
현재 릴리스단계이며 LTS는 오는 10월에 예정되어있습니다.