가장 먼저 "프로그램이 실행된다"는 말의 정확한 의미부터 정리해본다.
이 글은 Node.js를 활용한 웹서비스를 개발하면서
프론트엔드와 백엔드를 모두 이해하는
풀스택 개발자가 되기 위해 CS를 정리하는 첫 글이다.
JavaScript로 서버 코드를 작성하다 보면 이런 말을 자주 듣는다.
하지만 정작
"JavaScript 코드는 컴퓨터에서 어떻게 실행되나요?"
라는 질문에는 막연해지기 쉽다.
그래서 CS 공부의 첫 단계로
프로그램 실행의 전체 흐름부터 정리해보려고 한다.
컴퓨터를 사람의 몸에 비유하면 이렇다:
| 구성 요소 | 역할 | 개발자 관점 |
|---|---|---|
| CPU | 연산, 계산 | JavaScript 코드를 실제로 실행 |
| 메모리(RAM) | 임시 저장 | 변수, 객체, 함수가 올라가는 공간 |
| 저장장치(SSD) | 영구 저장 | .js 파일이 저장된 위치 |
📌 핵심: 이 세 가지는 독립적으로 동작하는 게 아니라, 프로그램이 실행되는 과정에서 서로 긴밀하게 연결되어 동작한다.
┌─────────────┐
│ 저장장치(SSD) │ ← app.js 파일 저장
└──────┬──────┘
│ ① OS가 파일 읽기
↓
┌─────────────┐
│ 메모리(RAM) │ ← 코드가 적재됨
│ + Node.js │
│ + V8 엔진 │
└──────┬──────┘
│ ② V8 엔진이 변환
↓
┌─────────────┐
│ CPU │ ← 실제 실행
└─────────────┘
이제 이 과정을 하나씩 자세히 살펴보자.
터미널에서 다음 명령어를 실행한다고 가정해보자.
node app.js
🤔 이 한 줄이 실행되면 뒤에서 무슨 일이 일어날까?
💬 운영체제(OS) 란?
Windows, macOS, Linux 같은 프로그램들을 관리하는 기본 시스템이다.
운영체제가 디스크(SSD)에서 app.js 파일을 찾아 읽어들인다.
이 시점에는 아직 텍스트 파일 그대로다.
💾 디스크(SSD)
└── app.js (텍스트 파일)
↓
[OS가 읽음]
쉽게 말하면: 책장에서 책을 꺼내는 과정이다.
OS가 읽어온 app.js 파일의 내용을 메모리(RAM)에 올린다.
이제 프로그램이 메모리 공간을 차지하게 된다.
🧠 메모리(RAM)
├── app.js 코드 내용
├── Node.js 런타임
└── V8 엔진
쉽게 말하면: 책장에서 꺼낸 책을 책상 위에 펼쳐놓는 과정이다.
💬 V8 엔진 이란?
Google에서 만든 JavaScript 실행 엔진으로, Chrome 브라우저와 Node.js가 사용한다.
메모리에 올라온 JavaScript 코드를 V8 엔진이 처리한다.
이 과정은 3단계로 나뉜다.
💬 파싱(Parsing) 이란?
코드를 읽고 문법 구조를 분석하는 과정이다.
const user = { name: "Kim" };
위 코드를 V8 엔진이 읽고
AST(추상 구문 트리) 라는 구조로 변환한다.
💬 AST(Abstract Syntax Tree) 란?
코드를 나무 구조로 표현한 것이다.
마치 문장을 주어-동사-목적어로 분석하는 것과 비슷하다.
AST (추상 구문 트리)
├── 변수 선언: const
├── 변수명: user
└── 값: 객체 { name: "Kim" }
💬 Ignition(이그니션) 이란?
V8 엔진의 인터프리터로, 코드를 빠르게 실행할 수 있는 중간 형태로 변환한다.
AST를 기반으로 V8 엔진의 Ignition이
JavaScript 코드를 바이트코드로 변환한다.
JavaScript 코드
↓
바이트코드 (중간 언어)
바이트코드는 CPU가 직접 이해할 수는 없지만,
사람이 작성한 코드보다는 기계에 가까운 형태다.
📌 마치 영어를 한국어로 번역하는 중간에 '콩글리시'를 만드는 것과 비슷하다.
💬 Hot Code(핫 코드) 란?
자주 실행되어 '뜨거워진' 코드를 의미한다.
자주 실행되는 코드는 V8의 TurboFan(JIT 컴파일러)이
머신 코드(기계어) 로 변환한다.
💬 JIT(Just-In-Time) 컴파일러 란?
코드를 실행하는 순간에 컴파일하는 방식이다.
미리 전체를 번역하는 게 아니라, 필요한 부분만 빠르게 번역한다.
바이트코드
↓
머신 코드 (CPU가 직접 실행 가능)
머신 코드는 CPU가 바로 이해하고 실행할 수 있는 0과 1로 된 언어다.
최종적으로 변환된 머신 코드를 CPU가 한 줄씩 실행한다.
⚡ CPU
├── 변수 user를 메모리 Heap에 할당
├── name 속성에 "Kim" 저장
└── 다음 명령 대기
쉽게 말하면: 책상에 펼쳐진 책을 보고 실제로 문제를 푸는 과정이다.
1. [디스크] app.js 파일 저장됨
↓
2. [OS] 파일을 읽어 메모리로 로드
↓
3. [메모리] 코드가 RAM에 적재됨
↓
4. [V8 엔진]
├─ Parsing: 코드를 AST로 변환
├─ Ignition: AST를 바이트코드로 변환
└─ TurboFan: 자주 쓰는 코드를 머신 코드로 최적화
↓
5. [CPU] 머신 코드를 실행
📌 핵심 정리
프로그램 실행이란 디스크 → 메모리 → V8 변환 → CPU 실행으로 이어지는 과정이다.
프로그래밍 언어는 실행 방식에 따라 나눌 수 있다.
| 구분 | 컴파일 언어 | 인터프리터 언어 |
|---|---|---|
| 예시 | C, Java | JavaScript (전통적) |
| 실행 방식 | 미리 전체 번역 | 한 줄씩 해석 |
| 실행 파일 | 생성됨 (.exe) | 없음 |
| 실행 시점 | 컴파일 후 | 코드 실행할 때마다 |
| 비유 | 책 전체를 번역한 후 읽기 | 동시통역하며 읽기 |
전통적으로는 인터프리터 언어였지만,
현대의 V8 엔진은 JIT(Just-In-Time) 컴파일 방식을 사용한다.
전통적 인터프리터:
코드 → 한 줄씩 실행 (느림 🐌)
V8 엔진 (JIT):
코드 → 바이트코드 → 머신 코드 → 실행 (빠름 🚀)
✨ 이 차이 덕분에 Node.js는 인터프리터 언어인 JavaScript를 서버 환경에서도 충분한 성능으로 실행할 수 있다.
V8 엔진은 JavaScript 코드를 단순히 한 줄씩 해석만 하는 것이 아니라:
1. 파싱: 코드를 AST로 변환
2. 바이트코드 생성: Ignition이 중간 코드 생성
3. 최적화: TurboFan이 자주 쓰는 코드를 머신 코드로 변환
그래서 Node.js는 인터프리터 언어임에도 불구하고 비교적 빠른 성능을 낼 수 있다.
💬 Node.js 런타임 이란?
JavaScript 코드를 실행할 수 있는 전체 환경을 의미한다.
Node.js는 단순히 V8 엔진만을 의미하지 않는다.
📦 Node.js 런타임
├── V8 엔진 (JavaScript 실행)
└── Node APIs
├── libuv (비동기 I/O 처리)
├── fs (파일 시스템)
├── http (네트워크)
└── setTimeout (타이머)
Node.js는
를 함께 포함한 하나의 실행 환경이다.
📌 정리하면
Node.js는 JavaScript를 운영체제(OS) 위에서 빠르게 실행하기 위한 런타임 환경이다.
그래서 Node.js는
다음과 같은 JavaScript 객체를 떠올려보자.
const user = {
name: "Kim",
age: 30
};
이 객체는 디스크에 저장되는 것이 아니라 메모리(RAM)에 저장된다.
프로그램이 실행되는 동안 데이터는 항상 메모리에 올라가며,
JavaScript 실행 과정에서는 메모리가 역할에 따라 나뉜다.
🧠 메모리(RAM)
├── Call Stack
│ └── 함수 호출 정보 (실행 순서)
│
└── Heap
└── 객체와 데이터 (user, 배열 등)
💬 Call Stack 이란?
함수가 어떤 순서로 실행되는지 기록하는 공간이다.
마치 책을 차곡차곡 쌓아놓는 것과 비슷하다.
function greet() {
console.log("Hello");
}
greet();
Call Stack 상태
[greet] ← 현재 실행 중
[전역]
💬 Heap 이란?
객체, 배열 같은 실제 데이터가 저장되는 공간이다.
크기가 큰 데이터를 자유롭게 보관하는 창고와 비슷하다.
const user = { name: "Kim" };
Heap 상태
{ name: "Kim" } ← 메모리 주소: 0x1234
이 메모리 구조를 이해하지 못하면 다음과 같은 개념들을 제대로 이해하기 어렵다:
| 개념 | 메모리와의 관계 |
|---|---|
| 이벤트 루프 | Call Stack과 Callback Queue의 관계 |
| 비동기 처리 | 왜 setTimeout이 Stack에서 빠져나가는가 |
| 서버 성능 저하 | Heap 메모리가 부족하면 발생 |
| 메모리 누수 | 사용하지 않는 객체가 Heap에 남아있는 경우 |
⚠️ 이러한 개념들은 모두 메모리를 어떻게 사용하느냐와 직접적으로 연결되어 있다.

💻 node app.js 실행
↓
1. OS가 app.js를 디스크에서 읽음
↓
2. 파일 내용을 메모리(RAM)에 적재
↓
3. V8 엔진이 변환 작업 수행
├─ Parsing (AST 생성)
├─ Ignition (바이트코드 변환)
└─ TurboFan (머신 코드 최적화)
↓
4. CPU가 머신 코드를 한 줄씩 실행
↓
5. 결과가 Node.js 메모리(Stack/Heap)에 저장됨
├─ Stack: 지역변수, 함수 호출 스택
└─ Heap: 객체, 배열, 문자열 데이터
👉 다음 글에서는 Stack / Heap 구조를 자세히 다룰 예정이다.