콘텐츠로 이동
Study Note웹 개발 일반

4. 런타임 — JavaScript가 도는 곳

“JavaScript를 실행한다”는 말에는 항상 어디서가 붙어야 한다

이 장에서 처음 나오는 말4개
런타임Runtime
JS 코드를 실제로 실행해 주는 환경. 엔진(연산) + API(파일·네트워크 접근)의 묶음이다. 브라우저도 런타임이고 Node.js도 런타임이다.
엔진JavaScript Engine
JS 문법을 해석·실행하는 핵심부. Chrome과 Node.js의 V8이 대표. 엔진만으로는 파일 하나 못 읽는다 — 그건 런타임이 주는 API의 몫이다.
LTSLong-Term Support
장기 지원 버전. 서버에 올리는 건 이걸 쓴다. 2026년 8월 기준 Node.js의 활성 LTS는 24다.
ESM / CJSES Modules / CommonJS
JS의 두 모듈 문법. import/export가 ESM(표준), require/module.exports가 CJS(Node의 옛 방식). 공존이 오래 이어져 고통의 근원이었다.

JavaScript는 원래 브라우저 안의 언어였다. 2009년 Node.js가 Chrome의 V8 엔진을 떼어다 파일·네트워크 API를 붙여 서버에서도 돌게 만들었다 — 이게 런타임의 구조를 그대로 보여 준다.

브라우저 런타임과 Node.js 런타임이 똑같이 V8 엔진을 쓰지만, 붙어 있는 것이 각각 DOM·fetch·localStorage와 파일 시스템·네트워크·프로세스로 다르다는 대비

같은 언어라도 런타임이 다르면 쓸 수 있는 API가 다르다. 브라우저 코드는 파일을 못 읽고, Node 코드는 document가 없다. “이 코드는 어디서 도는가”라는 질문이 웹 개발 내내 따라다니는 이유가 이것이다.

  • 한 언어로 풀스택 — 프론트와 백을 오가며 언어를 바꾸지 않는다. 타입·검증 로직을 공유한다
  • 생태계 — npm은 세계에서 가장 큰 패키지 저장소다. 웬만한 건 이미 누가 만들어 놨다
  • 비동기에 강하다 — 웹 서버 일의 대부분은 “기다리기”(DB·외부 API)다. Node의 이벤트 루프는 기다리는 동안 다른 요청을 처리한다

물론 서버를 꼭 JS로 짤 필요는 없다(3장의 백엔드 층 — Spring·Go·Django도 건재하다). 다만 현대 프론트엔드의 도구 사슬은 대개 Node 호환 런타임 위에서 돈다. 서버를 다른 언어로 짜도 Node·Bun·Deno 중 하나를 빌드 도구 실행 환경으로 만날 가능성이 높다.

여전히 기본값이고, 도전자들 덕에 좋아지는 중이다.

  • 2026년 8월 기준 활성 LTS는 24, 26은 Current다. 프로덕션은 LTS 상태를 확인해 고른다
  • 최근 몇 년 사이 내장이 두꺼워졌다 — TypeScript 직접 실행(타입만 벗겨서), 내장 테스트 러너, fetch 내장
  • 고민 없으면 이것. 자료·호환성·배포처 전부 최대

TypeScript — 이제 선택지가 아니라 기본값

섹션 제목: “TypeScript — 이제 선택지가 아니라 기본값”

TypeScript는 JavaScript에 타입 표기를 얹은 언어다. 컴파일하면 타입이 사라지고 그냥 JS가 된다 — 런타임에는 아무 흔적이 없다.

function total(items: { price: number }[]): number {
return items.reduce((sum, i) => sum + i.price, 0);
}
// 실행될 때는 타입이 전부 지워진 순수 JS다

여기서 중요한 역할 분리 하나 —

작업하는 일누가 하나속도
타입 검사타입이 맞는지 검증tsc --noEmit (에디터가 상시 수행)느리다
타입 제거타입 표기만 벗겨 JS로빌드 도구(esbuild·SWC), Node 자체매우 빠르다

빌드 도구들은 검사 없이 벗기기만 한다. 그래서 빌드는 빨리 통과했는데 타입 에러가 남아 있을 수 있다 — 검사는 에디터와 CI의 tsc가 담당한다는 분업을 알아야 “빌드는 됐는데 왜 빨간 줄이냐”에 안 놀란다.

모듈 — import가 두 종류였던 이유

섹션 제목: “모듈 — import가 두 종류였던 이유”

JS에는 모듈 문법이 없던 시절이 길었다. Node가 자체 규격(CJS, require)을 먼저 만들었고, 한참 뒤에 언어 표준(ESM, import)이 나왔다. 그 결과 생태계에 두 문법이 공존하게 됐다.

// CJS — Node의 옛 방식. 오래된 패키지와 설정 파일에 남아 있다
const express = require('express');
// ESM — 언어 표준. 새로 쓰는 코드는 전부 이쪽
import express from 'express';
  • 새 코드는 ESM을 기본 선택으로 본다. 다만 기존 Node 생태계와 일부 도구에는 CJS가 남아 있다
  • 최근 Node는 require()로 ESM을 불러오는 것까지 지원해서 경계의 고통이 많이 줄었다
  • 이 공존이 번들러가 필요한 이유 중 하나이기도 하다 — 6장에서 다시 만난다
  • 런타임 = 엔진 + API. 같은 JS라도 런타임이 다르면 쓸 수 있는 것이 다르다
  • 기본값은 Node LTS(현재 24). Bun은 속도·올인원, Deno는 보안·표준, 엣지는 초저지연
  • 서버를 JS로 안 짜도 프론트 도구 사슬에서 Node 호환 런타임을 만날 가능성이 높다
  • TypeScript는 기본값 — 검사(tsc)와 제거(빌드 도구)는 분업이다
  • 모듈은 ESM이 표준. CJS는 유산이며, 관련 에러는 코드가 아니라 판정의 문제다