지난 글에서는 JavaScript 실행 시 Call Stack과 Heap이 어떤 역할을 하는지 정리했다.
이번 글에서는 그 실행 구조가 어떤 단위로 운영체제에 의해 관리되는지, 프로세스(Process)를 살펴보겠다.
Node.js 서버를 만들다 보면 이런 순간을 만나게 된다.
이 질문들의 공통점은 모두 프로세스 단위에서 시작된다는 점이다.
📌 핵심: JavaScript 코드는 혼자서 실행되지 않는다. 항상 운영체제 위에서, 프로세스라는 실행 단위로 관리된다.
💬 프로세스(Process) 란?
실행 중인 프로그램을 의미한다.
디스크에 저장된 파일이 메모리에 올라와 실제로 동작하고 있는 상태를 프로세스라고 부른다.
프로세스는 간단히 말해 실행 중인 프로그램 하나이다.
디스크에 있던 프로그램 파일이 실행되면,
이 상태가 바로 프로세스다.
💬 CPU 스케줄링(CPU Scheduling) 이란?
운영체제가 여러 프로그램에게 CPU 사용 시간을 번갈아 배분하는 것이다.
마치 선생님이 여러 학생의 질문을 순서대로 빠르게 처리하는 것과 비슷하다.
여러 프로그램이 동시에 실행되는 것처럼 보이지만, 사실 CPU는 한 번에 하나씩만 처리한다.
운영체제가 각 프로세스에게 CPU를 아주 짧은 시간 단위로 번갈아 배분한다.
💻 CPU 스케줄링 예시
시간: 0ms~10ms → 프로세스 A 실행
시간: 10ms~20ms → 프로세스 B 실행
시간: 20ms~30ms → 프로세스 C 실행
시간: 30ms~40ms → 프로세스 A 실행 (다시)
...
⚡ 이렇게 빠르게 번갈아 실행되어서
사용자는 "동시에" 실행되는 것처럼 느낀다
| 구분 | 프로그램 | 프로세스 |
|---|---|---|
| 상태 | 정적(Static) | 동적(Dynamic) |
| 위치 | 디스크에 저장 | 메모리에 적재 |
| 비유 | 요리 레시피 책 | 실제로 요리하는 과정 |
| 예시 | app.js 파일 | 실행 중인 node app.js |
프로그램은 디스크에 저장된 정적인 파일이고
프로세스는 메모리에서 실행 중인 상태다
여기서 중요한 점은 프로세스가 새로운 개념이 아니라, 지금까지 살펴본 Call Stack과 Heap, 그리고 Code를 하나로 묶어 관리하는 단위라는 것이다.
Code, Call Stack, Heap, 이 모든 것을 운영체제가 "하나의 실행 덩어리"로 관리할 때 그 단위를 프로세스라고 부른다.
프로세스가 실행될 때 메모리에는 세 가지 주요 영역이 구성된다.
💬 Code 영역(Text Segment) 이란?
실행할 프로그램의 코드(명령어)가 저장되는 메모리 공간이다.
JavaScript 파일이 메모리에 올라올 때, 실제 실행 가능한 형태로 변환된 명령어들이 이곳에 저장된다.
Code 영역은 프로그램의 실행 명령어가 저장되는 공간이다.
| 특징 | 설명 |
|---|---|
| 읽기 전용 | 프로그램 실행 중 변경되지 않음 |
| 공유 가능 | 같은 프로그램을 여러 번 실행해도 Code는 하나 |
| 고정 크기 | 프로그램 시작 시 크기가 결정됨 |
📂 디스크의 app.js
const user = { name: "Kim" };
console.log(user);
↓ 로드 & 변환
🧠 메모리의 Code 영역
[기계어 명령어들]
0x1000: LOAD "Kim"
0x1004: CREATE_OBJECT
0x1008: CALL console.log
...
왜 읽기 전용일까?
코드는 실행 중에 변경될 필요가 없다. 프로그램의 로직 자체를 바꾸는 게 아니라, 데이터만 변경하면 되기 때문이다.
📦 프로세스 메모리 구조
┌─────────────────────────┐
│ 📜 Code (실행 명령어) │ ← 읽기 전용, 변경 안 됨
│ "무엇을 실행할지" │ 예: function add() {...}
├─────────────────────────┤
│ 📚 Call Stack │ ← 함수 호출 순서
│ "지금 어디를 실행 중" │ 예: add() → multiply()
├─────────────────────────┤
│ 📦 Heap │ ← 동적 데이터
│ "실행 중 생성된 데이터" │ 예: { name: "Kim" }
└─────────────────────────┘
쉽게 말하면:
// 1️⃣ Code 영역에 저장된 코드
function greet(name) {
const message = `Hello, ${name}`;
console.log(message);
}
greet("Kim");
// 2️⃣ 실행 시 각 영역의 상태
📜 Code 영역:
- greet 함수의 명령어들
- console.log의 명령어들
📚 Stack 영역:
- greet("Kim") 실행 프레임
- 매개변수: name = "Kim"
- 지역변수: message
📦 Heap 영역:
- 문자열 "Hello, Kim" 저장
📌 정리하면
Code 영역은 "실행할 명령어"를 저장하고,
Stack은 "실행 순서"를 관리하고,
Heap은 "실행 중 생성된 데이터"를 보관한다.
이제까지의 흐름을 한 번 다시 정리해보자.
1️⃣ JavaScript 파일은 디스크에 저장되어 있다
↓
2️⃣ 실행 명령을 내리면 (node app.js)
↓
3️⃣ 운영체제가 해당 프로그램을 메모리에 올린다
↓
4️⃣ 실행에 필요한 정보들을 구성한다
- Code 영역에 실행 명령어 적재
- Call Stack 생성
- Heap 영역 할당
- 프로세스 ID 부여
↓
5️⃣ CPU가 이 실행 단위를 번갈아 처리한다
↓
✅ 프로세스가 실행 중인 상태!
이때 운영체제가 관리하는 실행 단위가 바로 프로세스다.
💬 운영체제의 프로세스 관리 란?
프로세스가 실행되는 동안 필요한 모든 자원을 할당하고, 상태를 추적하고, 문제가 생기면 종료시키는 일련의 과정이다.
운영체제가 프로세스를 관리한다는 말은 다음과 같은 일을 대신해준다는 뜻이다:
| 관리 항목 | 설명 | 예시 |
|---|---|---|
| 실행 추적 | 지금 어떤 프로그램이 실행 중인지 기록 | Chrome, VSCode, Node.js 서버 |
| CPU 할당 | CPU를 언제, 얼마나 쓸 수 있는지 결정 | 10ms 동안 실행 → 다른 프로세스에게 양보 |
| 메모리 제한 | 메모리를 어디까지 쓸 수 있는지 제한 | 프로세스 A는 0x1000~0x5000 영역 사용 |
| 오류 처리 | 문제가 생기면 프로세스를 종료 | 메모리 부족 시 강제 종료 |
즉, 프로세스는 운영체제의 관리 아래에서만 실행될 수 있다.

각 프로세스는 실행을 위해 자기만의 자원을 가진다.
| 자원 | 설명 | 비유 |
|---|---|---|
| 독립적인 메모리 공간 | Code / Stack / Heap 영역 | 개인 책상 공간 |
| 프로세스 ID (PID) | 운영체제가 부여하는 고유 번호 | 학생 번호 |
| CPU 레지스터 상태 | 현재 실행 위치와 계산 값 | 지금 읽고 있는 책의 페이지 번호 |
| 열린 파일 정보 | 사용 중인 파일 목록 | 펼쳐둔 노트와 교과서 |
💬 PID (Process ID) 란?
운영체제가 각 프로세스를 구분하기 위해 부여하는 고유한 숫자다.
마치 학생마다 고유한 학생 번호가 있는 것처럼, 프로세스마다 고유한 번호가 있다.
# 실행 중인 Node.js 프로세스 확인
$ ps aux | grep node
USER PID CPU MEM COMMAND
kim 1234 2.5 1.2 node server.js
kim 5678 1.0 0.8 node worker.js
위 예시에서 1234와 5678이 바로 PID다.
💬 CPU 레지스터(CPU Register) 란?
CPU 내부의 아주 빠른 임시 저장 공간이다.
"지금 어느 줄을 실행하고 있는지", "계산 중인 값은 무엇인지" 같은 정보를 저장한다.
프로세스가 실행 중일 때:
같은 것들이 CPU 레지스터에 저장된다.
왜 중요한가?
프로세스가 잠시 멈추고 다른 프로세스가 실행될 때, 이 정보를 저장해두었다가 나중에 다시 불러와서 이어서 실행할 수 있다.
🔄 CPU 레지스터 상태 저장/복원 과정
프로세스 A 실행 중:
[레지스터] 현재 줄: 42, 계산값: 100
↓
10ms 경과 → CPU 스케줄링
↓
[레지스터 내용 저장] → 메모리에 백업
프로세스 B 실행 시작
↓
10ms 경과 → CPU 스케줄링
↓
[레지스터 내용 복원] ← 메모리에서 불러옴
프로세스 A 다시 실행: 42번째 줄부터 이어서 실행
중요한 점은 프로세스끼리는 메모리를 직접 공유하지 않는다는 것이다.
즉,
한 프로세스가 사용하는 Heap은
다른 프로세스에서 접근할 수 없다
이 독립성이 프로세스의 가장 큰 특징이다.
장점: 한 프로세스가 문제를 일으켜도 다른 프로세스에 영향을 주지 않는다
단점: 프로세스끼리 데이터를 주고받으려면 특별한 방법(IPC)이 필요하다
💬 IPC (Inter-Process Communication) 란?
프로세스 간 통신을 의미한다. 독립적인 프로세스들이 데이터를 주고받기 위한 특별한 방법이다. 파이프(Pipe), 소켓(Socket), 공유 메모리(Shared Memory) 같은 기술들이 있다.
Node.js 서버를 실행하면 운영체제는
즉,
Node.js 서버는 기본적으로 하나의 프로세스로 실행된다
여기서 말하는 "하나"는 요청 하나가 아니라 실행 주체 하나를 의미한다.
요청은 여러 개가 들어와도
그것을 처리하는 실행 주체는 하나의 프로세스다
그래서 Node.js 서버는 "요청 하나당 프로세스 하나"가 아니라, 프로세스 하나가 요청을 계속 처리하는 구조를 가진다.
이 프로세스가 살아 있는 동안,
🕐 프로세스 생명주기
시작: node server.js 실행
↓
[프로세스 생성]
↓
┌──────────────────────┐
│ 서버 실행 중... │ ← 계속 살아있음
│ 요청 처리 중... │
│ 메모리 사용 중... │
│ (몇 시간, 며칠...) │
└──────────────────────┘
↓
프로세스 종료 (서버 중지)
그래서 Node.js 서버는 "프로그램"이 아니라 "오래 살아 있는 프로세스"라고 보는 게 더 정확하다.
프로세스를 이해했다면 자연스럽게 이런 질문이 나온다.
"그럼 실행 단위를 여러 개로 나누면 더 빠르지 않을까?"
이 질문에 대한 한 가지 답이 멀티 프로세스다.
💬 멀티 프로세스(Multi-Process) 란?
여러 개의 프로세스를 동시에 실행하는 구조다.
각 프로세스는 완전히 독립적으로 동작한다.
멀티 프로세스는 여러 개의 프로세스를 동시에 실행하는 구조이다.
각 프로세스는
| 장점 ✅ | 단점 ❌ |
|---|---|
| 안정성: 한 프로세스 오류가 다른 프로세스에 영향 없음 | 자원 소모: 각 프로세스마다 메모리 공간 필요 |
| 독립성: 메모리 충돌 걱정 없음 | 속도: 프로세스 생성/제거 비용이 큼 |
| 확장성: CPU 코어 수만큼 병렬 처리 가능 | 통신 복잡: 프로세스 간 데이터 공유가 어려움 |
💰 자원 사용 비교
단일 프로세스:
[프로세스 1] → 메모리 100MB
멀티 프로세스:
[프로세스 1] → 메모리 100MB
[프로세스 2] → 메모리 100MB
[프로세스 3] → 메모리 100MB
[프로세스 4] → 메모리 100MB
────────────────────────────
총 400MB 사용 (4배)
안정성은 높지만 그만큼 자원을 많이 사용한다.
다른 선택지는 멀티 스레드다.
💬 스레드(Thread) 란?
프로세스 내부의 실행 흐름이다.
하나의 프로세스 안에서 여러 작업을 동시에 처리할 수 있는 단위다.
마치 한 사람(프로세스)이 여러 손(스레드)으로 동시에 일하는 것과 비슷하다.
스레드는 프로세스 내부의 실행 흐름이다. 하나의 프로세스 안에서 여러 작업 흐름을 동시에 실행할 수 있다.
💬 멀티 스레드(Multi-Thread) 란?
하나의 프로세스 내에서 여러 개의 스레드가 동시에 작업하는 구조다.
메모리는 공유하면서 실행 흐름만 여러 개로 나뉜다.

이 경우,
메모리는 대부분 공유되고
실행 흐름만 여러 갈래로 나뉜다
| 장점 ✅ | 단점 ❌ |
|---|---|
| 속도: 메모리 공유로 빠른 통신 | 복잡성: 동기화 문제 발생 |
| 효율: 메모리 사용량 적음 | 안정성: 한 스레드 오류가 전체에 영향 |
| 응답성: 여러 작업 동시 처리 | 디버깅: 오류 추적이 어려움 |
속도는 빠르지만, 복잡한 문제가 생긴다.
1️⃣ 동기화 문제
💬 동기화(Synchronization) 문제 란?
여러 스레드가 같은 데이터를 동시에 수정하려 할 때 발생하는 문제다.
마치 두 사람이 동시에 같은 문서를 편집하려고 하는 상황과 비슷하다.
❌ 동기화 문제 예시
공유 변수: count = 0
스레드 1 스레드 2
count 읽기 (0) count 읽기 (0)
count + 1 계산 (1) count + 1 계산 (1)
count에 1 저장 count에 1 저장
예상 결과: count = 2
실제 결과: count = 1 💥
2️⃣ 경쟁 조건 (Race Condition)
💬 경쟁 조건(Race Condition) 이란?
두 개 이상의 스레드가 공유 자원에 접근할 때, 실행 순서에 따라 결과가 달라지는 상황이다.
마치 두 사람이 마지막 남은 물건을 동시에 집으려는 상황과 비슷하다.
⚠️ 경쟁 조건 예시
잔액: 10000원
스레드 A (출금 5000원) 스레드 B (출금 7000원)
잔액 읽기 (10000원) 잔액 읽기 (10000원)
10000 - 5000 계산 10000 - 7000 계산
잔액 = 5000원 저장 잔액 = 3000원 저장
💥 마지막에 저장된 값에 따라 결과가 달라짐!
3️⃣ 데드락 (Deadlock)
💬 데드락(Deadlock) 이란?
두 개 이상의 스레드가 서로가 가진 자원을 기다리며 영원히 멈춰버리는 상황이다.
마치 좁은 길에서 두 차가 서로 비켜주기를 기다리다 둘 다 못 가는 것과 비슷하다.
💀 데드락 예시
자원 A (파일) 자원 B (DB)
스레드 1: 스레드 2:
A 잠금 성공 ✅ B 잠금 성공 ✅
B 잠금 대기... ⏳ A 잠금 대기... ⏳
서로가 상대방이 가진 자원을 기다리며
영원히 멈춤 💀
여기서 중요한 질문이 나온다.
왜 Node.js는 멀티 스레드 모델을 기본으로 쓰지 않았을까?
이 질문에 답하려면 웹 서버가 주로 무엇을 하는지를 봐야 한다.
웹 서버의 대부분 작업은
즉, CPU를 계속 쓰는 작업이 아니다.
💻 웹 서버의 작업 분석
CPU 집약적 작업 (적음):
- 데이터 암호화
- 이미지 처리
- 복잡한 계산
I/O 대기 작업 (많음): ⭐
- HTTP 요청 대기
- 파일 읽기/쓰기
- DB 쿼리 응답 대기
- API 호출 대기
💬 I/O(Input/Output) 란?
입력과 출력을 의미한다.
파일을 읽고 쓰거나, 네트워크로 데이터를 주고받거나, 데이터베이스에 접근하는 것처럼 프로그램 외부와 데이터를 주고받는 작업을 말한다.
이런 작업들은 CPU가 계산하는 시간보다 외부의 응답을 기다리는 시간이 훨씬 길다.
⏱️ I/O 작업의 시간 비율
파일 읽기 작업:
- CPU 처리 시간: 0.1ms
- 디스크 응답 대기: 10ms
────────────────────────
대기 시간이 100배 더 김! ⏳
DB 쿼리 작업:
- CPU 처리 시간: 0.5ms
- DB 응답 대기: 50ms
────────────────────────
대기 시간이 100배 더 김! ⏳
Node.js는 이 특성에 맞게
실행 흐름은 단순하게 유지하고
운영체제의 I/O 처리 능력을 적극 활용하는
방향을 선택했다.
그래서 Node.js는
라는 구조를 기본으로 가져간다.
🎯 Node.js의 선택
❌ "최대한 많은 일을 동시에 처리하자"
(멀티 스레드 방식)
✅ "하나의 실행 흐름을 막히지 않게 유지하자"
(싱글 스레드 + 비동기 I/O)
즉, Node.js의 선택은:
| 전통적인 방식 | Node.js 방식 |
|---|---|
| 여러 스레드로 동시 처리 | 하나의 스레드 + 비동기 |
| 복잡하지만 CPU 활용 좋음 | 단순하지만 I/O 효율 좋음 |
| 계산 집약적 작업에 유리 | 네트워크/파일 작업에 유리 |
"최대한 많은 일을 동시에 처리하자"가 아니라,
"하나의 실행 흐름을 막히지 않게 유지하자"에 가깝다.

이 글을 읽고 나서 다음 질문에 답할 수 있다면 프로세스는 제대로 이해한 것이다:
Q1. 프로그램과 프로세스의 차이는?A. 프로그램은 디스크에 저장된 정적인 파일이고,
프로세스는 메모리에서 실행 중인 동적인 상태다.
A. Code 영역 (실행 명령어), Stack (함수 호출 관리), Heap (동적 데이터)으로 구성된다.
Q3. Node.js 서버는 몇 개의 프로세스로 실행될까?A. 기본적으로 하나의 프로세스로 실행된다.
여러 요청이 들어와도 하나의 프로세스가 모두 처리한다.
A. 아니다. 각 프로세스는 독립적인 메모리 공간을 가진다.
데이터를 주고받으려면 IPC 같은 특별한 방법이 필요하다.
A. 웹 서버는 I/O 대기 작업이 많다.
Node.js는 복잡한 멀티 스레드 대신,
단순한 싱글 스레드 + 비동기 방식으로 I/O를 효율적으로 처리한다.
이 프로세스 구조를 알아야:
특히 서버가 왜 멈추는지, 왜 메모리가 계속 늘어나는지를 구조적으로 생각할 수 있게 된다.
👉 다음 글 예고
다음 글에서는 하나의 프로세스 안에서 실행 흐름이 어떻게 나뉘는지, 그리고 프로세스와 스레드의 차이를 더 자세히 살펴볼 예정이다.