17P by GN⁺ 2일전 | ★ favorite | 댓글 2개
  • Node.js 개발환경이 최근 몇 년간 웹 표준과 높은 호환성, 내장 기능 강화 측면에서 근본적인 변화를 겪음
  • ESM(ES Modules) , node: 접두사, Top-level await 등 최신 모듈 시스템과 비동기 패턴 도입으로 더욱 직관적이고 안전한 코드 작성 가능해짐
  • Fetch API, AbortController, Web Streams 등 기존 외부 라이브러리 의존성이 줄어들고 내장 API로 많은 기능 지원함
  • 테스트 러너, Watch 모드, 환경 파일 지원 등 내장 개발 도구로 작업 편의성과 생산성이 크게 향상됨
  • 권한 제어, 진단 채널, 단일 실행파일 배포까지 가능한 보안 및 배포 인프라 강화로 현대 Node.js는 전문적이고 범용적인 플랫폼으로 진화 중

Node.js의 변화와 발전

  • Node.js는 초기의 콜백 위주, CommonJS 중심 구조에서 오늘날 더욱 표준화된 개발환경으로 거듭나고 있음
  • 이러한 변화는 단순한 외형의 변화가 아니라, 서버 사이드 JavaScript의 전체적인 개발 패러다임 변화임

1. 모듈 시스템: ES Modules의 표준화

  • CommonJS는 오랫동안 Node.js에서 사용된 방식이지만, 정적 분석, 트리 쉐이킹 등의 한계와 웹 표준과의 불일치 문제가 있음
  • ESM(ES Modules) 방식이 Node.js의 새로운 표준으로 정착됨
    • import, export 구문 사용
    • 내장 모듈을 명시적으로 구분하기 위해 node: 접두사 도입
      • 예: import { readFile } from 'node:fs/promises'
      • 내장 vs npm 패키지 구분이 명확해짐
  • Top-level await 지원으로, 모듈 최상위에서도 await 사용 가능해짐
    • 즉시 실행 비동기 함수 래퍼를 둘러쓸 필요 없음
    • 코드가 더욱 직선적이고 이해하기 쉬워짐

2. 내장 웹 API: 외부 의존성 감소

  • Fetch API가 Node.js에 내장되어, Axios나 node-fetch 같은 외부 의존성 없이 HTTP 요청 가능함
  • Fetch는 기본적으로 타임아웃과 취소 기능(AbortSignal.timeout()) 지원함
    • 별도의 타임아웃 라이브러리 없이도 일관된 에러 처리 가능
  • AbortController로 파일, 네트워크 등 다양한 비동기 작업에서 취소 패턴 구현 가능
    • 사용자 인터럽트 혹은 시간 초과에 대해 표준화된 방식 제공

3. 내장 테스트: 프로페셔널 테스트 환경

  • 기존 Jest, Mocha 등 외부 프레임워크 필요 없이, Node.js 내장 테스트 러너로 대부분의 요구사항 충족 가능
    • node:testnode:assert로 직관적인 테스트 작성
  • 테스트 Watch 모드, 커버리지 리포팅 등 개발 편의 기능이 내장됨
    • 코드를 수정할 때마다 자동으로 테스트 실행
    • Node.js 20 이상에서 실험적 커버리지 기능 제공

4. 진화된 비동기 패턴

  • async/await가 널리 쓰이지만, 현대 Node.js는 병렬 수행과 정교한 에러 핸들링 패턴 활용이 강조됨
    • Promise.all()로 병렬 작업 수행, 단일 try/catch에서 문맥 정보를 포함한 에러 처리
  • AsyncIterator 활용으로 순차적 이벤트 처리 및 흐름 제어가 용이해짐

5. 고급 스트림 기능과 웹 표준 호환

  • 스트림 API가 웹 표준(Streams API)과 호환성을 갖추게 되었음
    • Readable.fromWeb, Readable.toWeb로 Node.js와 브라우저 간 스트림 변환 가능
  • pipeline(Promise 기반) 함수로 직관적이고 안전한 스트림 파이프라인 구축 가능

6. Worker Threads: CPU 집약 작업의 병렬 처리

  • WorkerThreads를 이용해 JS 단일 스레드 한계 극복, 멀티 코어 활용 가능
  • 메인 루프 블로킹 없이 복잡한 계산 또는 대형 데이터 처리 가능

7. 개발 경험의 혁신

  • --watch 플래그로 nodemon 없이 코드 변경 감지 및 자동 재실행
  • --env-file 플래그로 dotenv 불필요, 환경 변수 즉시 사용 가능
  • 개발환경 구성이 단순해지고 빠르게 변함

8. 보안 및 성능 모니터링 내장

  • 실험적 Permission Model로 파일/네트워크 접속 등 애플리케이션 권한 제한 가능
    • 최소 권한 원칙 구현 및 보안 준수에 유리함
  • perf_hooks로 내장 성능 측정, 느린 작업 자동 분석 및 기록 가능

9. 배포와 패키징의 현대화

  • SEA(Single Executable Application) 지원으로 Node.js와 앱을 단일 바이너리로 배포 가능
    • Node.js 없는 환경에서도 손쉽게 배포/설치 가능함

10. 현대적 에러 처리 및 진단

  • 구조화된 에러 클래스로 풍부한 문맥과 진단 정보 포함, 일관된 에러 객체 전달
  • diagnostics_channel로 맞춤형 이벤트 기반 진단 데이터 전송 및 모니터링 자동화

11. 모듈 해석 및 패키지 관리의 발전

  • Import Maps로 내부 경로를 별도의 네임스페이스로 관리
    • 내부 모듈 분리 및 리팩토링 편의성 증가
  • 동적 import로 환경 또는 설정에 따라 런타임 중 코드 로드 및 코드 스플리팅 가능

핵심 정리 및 미래 전망

  • Node.js는 웹 표준 준수, 내장 도구 활용 극대화, 현대적 비동기 패턴 채택이 중요함
  • Worker Threads 등 고성능 병렬 처리와, 진단/보안 기능으로 전문가용 플랫폼으로 발전중임
  • 단일 실행파일 배포모듈 네임스페이스 등 신기능으로 운영 편의성이 크게 늘어남
  • 이러한 패턴들은 기존 코드와 호환되면서 점진적으로 도입 가능함
  • 2025년 이후에도 Node.js는 꾸준히 진화하며, 지금 소개된 이러한 모던 패턴은 미래지향적 애플리케이션의 기반이 될 예정임

오 이제 axios 안쓰고, fetch로 바로 되네

Hacker News 의견
  • 가장 큰 변화는 ESM이 아니라 Node에 fetch와 AbortController가 내장된 점임, axios나 node-fetch를 제거하고 Lambda 번들의 크기도 줄었고 콜드 스타트 지연도 약 100ms 단축됨, 습관적으로 npm i axios를 쓰는 사람이라면 2025 Node 릴리스가 그만둘 때임
    • API 호출과 유효성 검증을 모두 아우르는 ts-rest를 전체 스택에서 선호함, zod/json schema 기반 라이브러리 중에 가장 가벼우면서도 robust한 타입 안전성을 제공함, HTTP 클라이언트도 원하는 것을 꽂아쓸 수 있고(bun, node엔진에서는 fastify 선택함), 오버헤드가 있지만 타입 안전성을 컴파일 단계로 옮길 수 있다는 점에서 충분히 가치 있는 선택임, 혹시 더 좋은 대안이나 생각 있으신지 궁금함, 찾을 수 있는 한 다 찾아봤는데 ts-rest만이 경량성과 타입 안전성을 모두 챙길 수 있었음
    • fetch 문법과 await response.json 등 추가적인 예외 처리 작업이 그렇게 마음에 들진 않았음, axios 쓸 때가 훨씬 직관적임, 예시 코드에서도 axios는 단순하게 response.data를 처리할 수 있고, fetch는 직접 status 체크한 뒤 JSON을 파싱해야 해서 더 번거로움
    • 라이브러리 저자로서는 오히려 ESM 도입이 훨씬 힘들고 아팠지만 그만큼 가치있는 업그레이드였음, fetch 자체는 훌륭하지만 ESM 덕에 진짜 많은 것을 얻을 수 있었음
    • node fetch가 axios보다 훨씬 쉽고 단순해서 더 좋음, 아직 어떤 분들은 axios를 계속 쓰는지 몰랐음
    • 내장 요청 라이브러리인 Undici가 굉장히 기대됨, undici 공식 사이트 참고
  • 다음과 같이 파일 시스템이나 네트워크 접근 권한을 제한해서 실행할 수 있게 됨
    # 파일 시스템 접근 제한 예시
    node --experimental-permission \
      --allow-fs-read=./data --allow-fs-write=./logs app.js
    
    # 네트워크 제한 예시
    node --experimental-permission \
      --allow-net=api.example.com app.js
    
    Deno에서 영감을 받은 것 같음, 정말 훌륭한 기능임, Deno 퍼미션 기능 문서
  • chalk나 picocolors를 설치하지 않아도 이제 직접 텍스트 스타일링이 가능해짐
    const { styleText } = require('node:util');
    
    공식 styleText 문서 참고
  • 바로 적용 가능한 여러 가지를 알게 됨
    1. Node에 내장 테스트가 들어와서 jest를 굳이 쓸 필요가 없어짐
    2. Node에 watch 기능도 내장돼서 nodemon도 필요 없어짐
    • 아직도 jest를 선호함, jest-extended를 쓸 수 있기 때문임
    • Node의 내장 테스트 시스템은 퀄리티가 떨어진다고 생각함, 실제로 몇 주 써보면 왜 그런지 알게 되고, 이슈를 제기해도 Node 팀은 별 관심이 없음
  • Matteo Collina에 따르면 node fetch는 내부적으로 undici의 fetch를 사용함, WHATWG 웹 스트림을 만들어야 해서 본질적으로 undici의 request 방식보다 느림,
    언급한 유튜브,
    undici 작동 원리 블로그
    • 궁금한 사람을 위해 벤치마크는 이곳에서 볼 수 있음, 최근 M3 Max 맥북 프로에서 로컬과 네트워크 환경에서 테스트했는데 undici가 로컬에선 최고였지만 네트워크에선 Axios가 더 빠른 결과가 나옴, 이유는 정확히 모르겠지만 지난 1년 반 동안 undici 사용 경험은 뛰어났음, 프로덕션에서도 충분히 안정적으로 쓸 수 있지만, 뛰어난 성능을 최대로 뽑으려면 상황에 맞는 고민이 꼭 필요함
  • Node의 네이티브 typescript 트랜스파일러 덕분에 TS를 쓰는 사람들은 복잡도가 많이 줄어듦
    • 사실 타입만 제거하고 트랜스파일은 아님, TS enum 같은 건 제대로 동작하지 않음
    • 아직 실사용하기엔 부족함, Enum은 신경 안 쓰지만 확장자 없이 로컬 파일 import도 안되고, 생성자에서 class property 정의도 안 됨
    • --experimental-strip-types 플래그도 이제 필요 없음
  • 새로운 기능을 이런 식으로 우연히 알게 되는 경우가 많음, 브라우저 쓸 때처럼 “그건 최신이니까”라는 막연한 느낌이 있음, 예전 C#만 할 때는 새 언어 기능 소개를 읽고 정말 신나했는데 요즘은 다양한 언어를 병행하다 보니 한 언어만 따라가기도 쉽지 않음, 거의 블로그나 주변 영향을 통한 랜덤 학습임
    • Node(V8) 소식에 관심 많아서 2~3개월에 한 번씩 릴리즈 노트 읽고 이런 기능들을 챙김, 때로는 ECMA proposals도 읽어봄, 파이프라인 오퍼레이터가 꼭 들어갔으면 하는 바램임
  • 한동안 Node 생태계와 거리를 뒀다가 다시 보니 정말 흥미로운 신기능이 많아졌음, Deno와 Bun이 시장을 흔들면서 Node 개발진이 한층 분발한 결과라고 봄
  • 점점 Node가 Bun.js, Deno 등과의 경쟁에서 만만치 않은 존재로 변화 중임, 그런 상호 경쟁이 JS 런타임 발전에 긍정적임
    • 변화는 느리지만 확실하고 반가움, 그래도 Bun의 $ shell function이 아직 그리움, JS를 스크립트처럼 쓰는 게 정말 편리해서 서버에 두 런타임을 함께 올리고 싶진 않음
  • 브라우저처럼 Node의 신기능도 두 가지로 나눌 수 있다고 봄
    1. 전혀 새로운 기술
    2. 이미 있는 기능 위에 얹어진 "멋내기" 단계의 레이어
      사람들이 어느 쪽에 더 비중을 두는지 보는 것도 흥미로움
    • 누군가에겐 “멋내기 레이어”가 또 누군가에겐 사용성(ergonomics)이 될 수 있음