
3-1. REPL 사용하기
- 자바 스크리브는 스크립트 언어이므로 미리 컴파일을 하지 않아도 즉석 코드 실행이 가능하다.
입력한 코드를 읽고(Read), 해석하고(Eval), 결과물을 반환하고(Print), 종료할 때까지 반복한다(Loop)라고 해서 REPL(Read Eval Print Loop)라고 불린다.

3.3 모듈로 만들기
모듈: 특정한 기능을 하는 함수나 변수들의 집합
모듈을 만들어놓으면 여러 프로그램에 해당 모듈을 재사용할 수 있다.
이는, 자바스크립트에서 코드를 재사용하기 위해 함수로 만드는 것과 비슷하다.
예시: 한 명의 사람에 대한 정보)
한 사람의 이름과 나이를 관리한다고 가정한다.
person.js 파일은 사람의 이름과 나이를 정의하고 내보내는 모듈이다. 마치 "이 파일에는 이런 정보가 들어있어요!"라고 말해주는 것과 같다.
// person.js
// 'name'이라는 변수를 다른 파일에서 사용할 수 있게 내보냄
export const name = '김민준';
// 'age'라는 변수를 다른 파일에서 사용할 수 있게 내보냄
export const age = 30;
main.js 파일은 person.js 모듈에서 필요한 정보를 가져와서 사용한다. 마치 "아까 그 파일에 있던 '이름'과 '나이'를 여기서 쓸게요!"라고 말하는 것과 같다.
// main.js
// 'person.js' 모듈에서 'name'과 'age'를 가져옴
import { name, age } from './person.js';
// 가져온 변수들을 사용함
console.log(`이름: ${name}`); // 출력: 이름: 김민준
console.log(`나이: ${age}`); // 출력: 나이: 30
여러 파일에 걸쳐 재사용 되는 함수나 변수를 모듈로 만들어두면 편리하다.
그러나 모듈이 많아지고 모듈 간의 관계가 얽히게 되면 구조를 파악하기 어렵다는 단점이 있다.
3-4. 노드 내장 객체 알아보기
3.4.1 global
브라우저의 window와 같은 전역 객체이다. 자바스크립트의 모든 요소가 연결되는 '가장 큰 공간' 이다.
*전역 객체이므로 모든 파일에서 접근 가능한 최상위 객체
예시: 아래의 코드는 같은 의미이다.)
// window 객체의 예
// 전역 변수 'message'를 선언했음
var message = '안녕하세요!';
// 이 변수는 사실 window 객체에 속하게 됨
// 따라서 window.message 로 접근해도 같은 값을 얻음
console.log(window.message); // 출력: 안녕하세요!
// Node.js에서 전역 변수 'user'를 선언함
var user = '관리자';
// 이 변수는 global 객체에 속하게 됨
// 따라서 global.user 로 접근해도 같은 값을 얻음
console.log(global.user); // 출력: 관리자
global은 이렇게 객체의 속성에 값을 대입해 파일 간의 데이터를 공유할 수 있다.
하지만 이를 남용할시 프로그램의 규모가 커져 어떤 파일에서 global 객체에 값을 대입했는지 찾기 어려워져 유지 보수에 어려움을 겪을 수 있다.
*추천: var 대신 const와 let을 사용하고, 모듈(Module)을 활용하여 전역 공간을 깨끗하게 유지하는 것을 권장
3.4.2 console
주로 디버깅을 위해 사용한다. 개발을 하면서 변수에 값이 제대로 들어 있는지 확인하기 위해 사용하고, 에러 발생 시 에러 내용을 콘솔에 표현하기 위해 사용하며, 코드 실행 시간을 알아보려고 할 때도 사용한다. 대표적으로 console.log 메소드가 있다.
const string = 'abc';
const number = 1;
const boolean = true;
const obj = {
outside: {
inside: {
key: 'value'
},
},
};
console.time('전체 시간');
console.log('쉼표로 구분해 여러 값을 찍을 수 있습니다.');
console.log(string, number, boolean);
console.error('에러 메시지');
console.table([{
name: '제로',
birth: 1994
}, {
name: 'hero',
birth: 1998
}]);
console.dir(obj, {
colors: false,
depth: 2,
});
console.dir(obj, {
colors: true,
depth: 1,
});
console.time('시간 측정');
for (let i = 0; i < 100000; i++) { }
console.timeEnd('시간 측정');
function b() {
console.trace('에러 위치 추적');
}
function a() {
b();
}
a();
console.timeEnd('전체 시간');
console.dir(user, { colors: true });
console.dir(user, { depth: 2 });
3.4.3 타이머
타이머 기능을 제공하는 함수인 setTimeout, setInterval, setImmediate는 노드에서 window 대신 global 객체 안에 들어있다.
밑의 세 함수는 웹 브라우저에서도 자주 사용되는익숙한 타이머 함수들이다.
이 타이머 함수들은 모두 아이디를 반환한다, 아이디를 사용하여 타이머를 취소할 수 있다.
* 아이디(ID)는 타이머 함수(setTimeout, setInterval, setImmediate)가 반환하는 고유한 식별자이다.
const timeout = setTimeout(() => {
console.log('1.5초 후 실행');
}, 1500);
const interval = setInterval(() => {
console.log('1초마다 실행');
}, 1000);
const timeout2 = setTimeout(() => {
console.log('실행되지 않습니다.');
}, 3000);
setTimeout(() => {
clearTimeout(timeout2);
clearInterval(interval);
}, 2500);
const immediate = setImmediate(() => {
console.log('즉시 실행');
});
const immediate2 = setImmediate(() => {
console.log('실행되지 않습니다.');
});
clearImmediate(immediate2);
# 실행 결과
즉시 실행
1초마다 실행
1.5초 후 실행
1초마다 실행
3.4.4 __filename, __dirname
노드에서는 파일 사이에 모둘 관계가 있는 경우가 많으므로 때로는 현재 파일의 경로나 파일명을 알아야 한다. 노드는 __filename, __dirname이라는 키워드로 경로에 대한 정보를 제공한다. 파일에 __filename과 __dirname을 넣어두면 실행 시 현재 파일명과 현재 파일 경로로 바뀐다.
# filename.js
console.log(__filename);
console.log(__dirname);
# 콘솔
c:\Users\사용자이름\filename.js
c:\Users\사용자이름
3.4.5 module, exports, require
this 특징
*최상위 스코프 ex: global 스코프
console.log(this);
console.log(this === module.exports);
console.log(this === exports);
function whatIsThis() {
console.log('function', this === exports, this === global);
}
whatIsThis();
# 콘솔
# $ node idnex
{}
true
true
function false true
console.log("require가 가장 위에 올 필요가 없습니다.");
module.expports = "저를 찾아보세요.";
require('./var');
console.log('require.cache입니다.');
console.log(require.cache); // 파일명에 대한 정보 출력
console.log('require.main입니다.');
console.log(require.main === module); // 현재 파일이 첫 모듈인지 확인
console.log(require.main.filename); // 첫 모듈 파일명 확인
3.4.6 process
Process: 현재 실행되고 있는 노드 프로세스에 대한 정보를 담고있다.
3.5.1 os
웹 브라우저에 사용되는 자바스크립트는 운영체제의 정보를 가져올 수 없지만, 노드는 os 모듈에 정보가 담겨 있어 정보를 가져올 수 있다.
경로
3.5.2 path
path 모듈은 Node.js에서 파일과 폴더의 경로를 다루는 데 사용된다. 운영체제에 상관없이 경로를 안전하게 조작할 수 있도록 도와주는 역할을 한다.
경로 정보 확인
경로 조작 및 변환
경로 합치기
3-6. 파일 시스템 접근하기
fs 모듈은 Node.js에서 파일을 읽고, 쓰고, 관리하는 데 사용되는 모듈이다. 이 모듈의 함수들은 대부분 비동기적으로 동작하기 때문에, 결과를 처리하기 위해 콜백이나 프로미스를 사용해야 한다.
* 콜백(callback): 다른 코드의 실행이 끝난 후 다시 호출될 함수
1. 파일 읽기: fs.readFile()
파일의 내용을 읽어오는 함수다. 파일을 읽고 나면 결과는 Buffer라는 형식으로 반환된다. toString()을 사용하면 우리가 읽을 수 있는 문자열로 변환할 수 있다.
콜백 형식 (기본)
const fs = require('fs');
// readFile 함수: 콜백 함수로 결과를 처리함
fs.readFile('./readme.txt', (err, data) => {
if (err) {
// 에러 발생 시 여기서 처리하는 것임.
throw err;
}
console.log(data); // 버퍼(Buffer) 형태로 출력되는 것임.
console.log(data.toString()); // 버퍼를 문자열로 변환해 출력하는 것임.
});
프로미스 형식 (최신)
const fs = require('fs').promises;
// readFile 함수: 프로미스를 반환하여 then()과 catch()로 결과를 처리함
fs.readFile('./readme.txt')
.then((data) => {
console.log(data.toString());
})
.catch((err) => {
// 에러가 발생하면 여기서 처리함.
console.error(err);
});
2. 파일 쓰기: fs.writeFile()
지정한 경로에 파일 내용을 쓰는 함수다. 파일이 이미 존재하면 덮어쓰고, 없으면 새로 생성한다.
프로미스 형식
const fs = require('fs').promises;
// 1. writeme.txt 파일에 글을 작성함.
fs.writeFile('./writeme.txt', "파일에 글을 작성했음.")
.then(() => {
// 2. 파일 쓰기가 완료되면, 바로 이어서 파일을 읽는 것임.
console.log('파일 쓰기 완료!');
return fs.readFile('./writeme.txt');
})
.then((data) => {
// 3. 읽은 파일 내용을 출력하는 것임.
console.log(data.toString());
})
.catch((err) => {
// 에러가 발생하면 여기서 처리함.
console.error(err);
});
3.6.1 동기 메서드와 비동기 메서드
동기 vs. 비동기: 파일 시스템 접근
파일 시스템에 접근하는 fs 모듈은 작업을 처리하는 방식에 따라 동기와 비동기 메서드로 나뉜다. 이 둘은 순서를 기다리는 방식에서 큰 차이가 있다.
1. 비동기 (논블로킹 방식)
비동기 메서드는 작업이 끝날 때까지 기다리지 않고, 다음 코드를 바로 실행한다. 파일을 읽는 동안에도 다른 작업을 처리할 수 있어 효율적이다. 마치 여러 주문을 동시에 받고, 주문이 완성되는 대로 손님을 부르는 식당과 같다. 하지만 이 때문에 순서가 보장되지 않을 수 있다.
// 비동기 방식: 실행 순서가 뒤죽박죽
fs.readFile('./readme.txt', (err, data) => {
// 이 코드가 가장 먼저 실행될 수 있음.
console.log('1번', data.toString());
});
fs.readFile('./readme.txt', (err, data) => {
// 이 코드가 1번보다 먼저 실행될 수도 있음.
console.log('2번', data.toString());
});
2. 동기 (블로킹 방식)
동기 메서드는 작업이 완료될 때까지 다음 코드를 실행하지 않고 기다린다. 마치 한 손님이 주문을 마치고 음식을 받을 때까지 다른 손님의 주문을 받지 않는 식당과 같다. 이 방식은 코드가 순서대로 실행되므로 결과를 예측하기 쉽지만, 동시에 많은 요청이 들어오면 모든 작업이 멈추는 블로킹(Blocking) 현상이 발생해 성능에 문제가 생긴다.
// 동기 방식: 순서가 보장됨
let data1 = fs.readFileSync('./readme.txt');
console.log('1번', data1.toString()); // 이 코드가 실행되기 전까지 다음 줄로 넘어가지 않음
let data2 = fs.readFileSync('./readme.txt');
console.log('2번', data2.toString());
let data3 = fs.readFileSync('./readme.txt');
console.log('3번', data3.toString());
3.6.2 버퍼와 스트림 이해하기

버퍼(Buffer)와 스트림(Stream)은 Node.js에서 대용량 데이터를 처리할 때 메모리와 시간을 효율적으로 사용하게 해주는 중요한 개념이다. 이 둘은 데이터를 다루는 방식에서 차이가 있다.
버퍼(Buffer)
버퍼는 파일이나 네트워크로부터 데이터를 메모리에 일시적으로 저장하는 공간이다. 마치 택배를 트럭에 모두 실어 한 번에 보내는 것처럼, 데이터를 한 덩어리로 모아 전송한다.
readFile과 같은 함수는 내부적으로 버퍼를 사용한다. 파일 전체를 메모리에 올리기 때문에, 파일 크기가 클수록 메모리 사용량이 급증하여 비효율적일 수 있다.
스트림(Stream)
스트림은 데이터를 작은 조각으로 나누어 조금씩 전송하는 방식이다. 마치 물이 수도관을 통해 끊임없이 흘러가는 것처럼, 데이터가 버퍼를 통해 연속적으로 흐른다. 이 방식은 대용량 파일을 다룰 때 메모리를 훨씬 효율적으로 사용한다.
pipe를 사용하면 파일 압축(zlib)과 같은 작업을 readStream과 writeStream 사이에 연결하여 데이터를 효율적으로 처리할 수 있다.
3-7. 이벤트 이해하기
Node.js에서 이벤트는 특정 상황이 발생했을 때 미리 정해둔 동작(콜백 함수)을 실행하는 메커니즘이다. events 모듈의 EventEmitter 클래스를 사용하면 이벤트를 직접 만들고 관리할 수 있다.
이벤트 리스너를 관리하는 주요 메서드들은 다음과 같다.
4-1. 요청과 응답장 노드 기능

클라이언트(웹 브라우저)가 서버에 특정 정보를 달라고 요청하면, 서버는 그 요청을 처리한 후 결과를 응답으로 돌려준다. 이 통신은 대부분 HTTP(HyperText Transfer Protocol)라는 규칙을 따른다.
http 모듈 중 http.createServer() 함수는 서버를 만들고, 요청이 들어올 때마다 콜백 함수를 실행한다. 이 콜백 함수는 req(요청)와 res(응답) 두 개의 인수를 받는다.
HTTP 상태 코드
서버가 요청을 어떻게 처리했는지 알려주는 3자리 숫자다. 이 번호만 봐도 성공인지, 오류인지 등을 바로 알 수 있다.
4-2. REST와 라우팅 사용하기
REST(REpresentational State Transfer)는 서버의 자원을 정의하고 자원에 대한 주소를 지정하는 방법을 가리킨다. 일종의 약속이라고 봐도 무방하다. 자원이라고 해서 꼭 파일일 필요는 없고 서버가 행할 수 있는 것들을 통틀어서 의미한다고 보면 된다.
REST에서는 주소 외에도 HTTP 요청 메서드라는 것을 사용한다.
4-3. 쿠키와 세션 이해하기

사용자가 누구인지 기억하기 위해 서버는 요청에 대한 응답을 할 때는 쿠키라는 것을 보낸다. 쿠키는 유효 기간이 있으며 name=zerocho와같이 단순한 '키-값'의 쌍이다. 서버로부터 쿠키가 오면 웹 브라우저는 쿠키를 저장해두었다가 다음에 요청할 때마다 쿠키를 동봉해서 보낸다. 서버는 요청에 들어 있는 쿠키를 읽어서 사용자가 누군지 파악한다.
브라우저는 쿠키가 있다면 자동으로 동봉해서 보내주므로 따로 처리할 필요가 없다. 서버에서 브라우저로 쿠키를 보낼 때만 코드를 작성해 처리한다.
세션은 클라이언트(웹 브라우저)에 직접 저장되는 쿠키와 달리, 모든 정보가 서버에 저장되는 방식이다. 사용자가 웹사이트에 처음 접속하면, 서버는 그 사용자를 식별할 수 있는 고유한 세션 ID를 만든다. 그리고 이 세션 ID를 담은 작은 파일, 즉 '세션 쿠키'를 사용자 브라우저에 보낸다. 브라우저는 이 세션 쿠키를 메모리에 저장해 두었다가, 다음에 같은 서버에 요청할 때 자동으로 포함시켜 보낸다. 서버는 요청과 함께 온 세션 쿠키의 ID를 확인하고, 서버에 저장된 세션 정보를 찾아 해당 사용자의 상태를 파악한다. 이 덕분에 사용자는 매번 로그인 정보를 입력할 필요 없이 편리하게 웹 서비스를 이용할 수 있다. 세션은 보통 사용자가 브라우저를 닫거나 만료 시간이 지나면 자동으로 사라진다.
4-4. https와 http2

4-5. cluster

cluster 모듈은 기본적으로 싱글 프로세스로 동작하는 노드가 CPU 코어를 모두 사용할 수 있게 해주는 모듈이다. 포트를 공유하는 노드 프로세스를 여러 개 둘 수도 있으므로, 요청이 많이 들어왔을 때 병렬로 실행된 서버의 개수만큼 요청이 분산되게 할 수 있다. 서버에 무리가 덜 가게 하는 것이다. 이렇게 하면 서버의 부하를 효과적으로 분산시켜 성능을 극대화할 수 있다.
cluster 모듈은 마스터(Master) 프로세스와 워커(Worker) 프로세스라는 두 가지 개념을 사용한다. 마스터 프로세스는 사용자가 실행한 Node.js 파일이며, 이 마스터는 CPU 코어의 개수만큼 워커 프로세스를 생성하는 역할을 한다. 마치 한 명의 관리자가 여러 명의 일꾼을 고용하는 것과 같다.
워커 프로세스는 마스터가 생성한 일꾼들이다. 각각의 워커 프로세스는 독립적으로 Node.js 서버를 실행하며, CPU 코어를 하나씩 맡아 사용한다. 사용자로부터 웹 요청이 들어오면, 마스터 프로세스가 이 요청을 여러 워커 프로세스 중 하나에 분배해 처리하게 한다. 이렇게 하면 요청이 한 곳에 몰리지 않고 여러 코어로 분산되므로, 서버의 처리 속도와 안정성이 크게 향상된다.
1) 파일 시스템 접근을 위해 fs 모듈의 동기 메서드를 사용하면, 작업이 완료될 때까지 다음 코드가 실행되지 않는 ( ) 현상이 발생할 수 있다.
2) path 모듈에서 여러 경로를 합치되, /로 시작하는 경로가 있다면 그 앞의 경로를 모두 무시하고 절대 경로로 합쳐주는 메서드는 path.( ) 이다.
3) http 모듈로 서버를 만들 때, 클라이언트가 보낸 요청 정보를 담고 있는 객체의 이름은 ( ) 이다.
4) 웹 브라우저에서 window 객체가 전역 객체인 것처럼, Node.js에서는 ( ) 객체가 전역 객체이다.
5) setTimeout, setInterval, setImmediate 같은 타이머 함수들은 모두 고유한 ( )를 반환하여 타이머를 취소할 수 있게 해준다.
6) 코드의 실행 순서를 올바르게 고르세요.
const fs = require('fs');
const readStream = fs.createReadStream('./readme.txt');
readStream.on('data', (chunk) => {
// 파일의 일부 데이터가 읽힐 때마다 실행됨
console.log('읽은 데이터:', chunk.length);
});
readStream.on('end', () => {
// 파일 읽기가 모두 끝났을 때 실행됨
console.log('파일 읽기 종료');
});
보기
A. A → C → B
B. B → A → C
C. C → B → A
D. A → B → C
7) 코드 뒤 빈칸에 올 내용을 고르세요.
console.log("Node 버전:", process._____);
보기
A. version
B. nodeVersion
C. env
D. info
8) 이벤트 루프의 옳은 순서를 고르세요.
setTimeout(() => console.log("timeout"), 0);
Promise.resolve().then(() => console.log("promise"));
console.log("sync");
보기
A. sync → timeout → promise
B. sync → promise → timeout
C. promise → sync → timeout
D. timeout → sync → promise
그래서 동기 코드(A, C)가 먼저 실행되고 B가 마지막에 나온다.
7. A
8. B (3.4 타이머(timer), sync → promise → timeout)
출처 : 조현영, 『 Node.js 교과서 개정 3판』, 길벗(2022),
Wallarm, "HTTP/2란 무엇이고 HTTP/1과 어떻게 다른가요?"
Corner Node.js 1
Editor : GYEOMGYOEM
| [Node.js 1팀] 8장. 몽고디비 (0) | 2025.11.21 |
|---|---|
| [Node.js 1팀] 7장. MySQL (0) | 2025.11.14 |
| [Node.js 1팀] 5장. 패키지 매니저, 6장. 익스프레스 웹 서버 만들기 (0) | 2025.11.07 |
| [Node.js 1팀] 2장. 알아둬야 할 JavaScript (0) | 2025.10.10 |
| [Node.js 1팀] 1장. JavaScript (Introductions ~ Objects) (1) | 2025.10.03 |