Back to all posts

Node.js로 기본 HTTP 서버 구동하고 요청/응답 흐름 이해하기

Node.js의 http 모듈을 사용해 로컬 환경에 아주 간단한 서버를 띄우고, 클라이언트의 요청(req)과 서버의 응답(res)이 어떻게 주고받히는지 기초 동작을 직접 코드로 확인했습니다.

2026년 05월 07일3

시작하며

웹 개발을 공부할 때 '서버'라는 개념은 늘 추상적으로 다가왔다. 화면에 보이는 프론트엔드와 달리, 눈에 보이지 않는 곳에서 요청을 받아 응답을 준다는 사실만 알고 있을 뿐이었다. 그래서 이번에는 가장 기초적인 수준에서 Node.js를 이용해 직접 서버를 띄우고 통신하는 과정을 코드로 따라 해보았다.

이번에 이해하려는 것

이번 코드의 목표는 아주 단순하다. 내 컴퓨터(로컬)에서 특정 포트로 들어오는 요청을 기다리고, 요청이 들어오면 간단한 HTML 텍스트(Hello World)를 응답으로 돌려주는 것이다. 이 과정을 통해 서버가 어떻게 요청을 듣고(listen), 반응하는지 기본 흐름을 파악해보려 한다.

코드 전체

아래는 공부하며 작성한 전체 코드다. 코드 안에는 이해를 돕기 위해 남겨둔 개인적인 주석이 그대로 포함되어 있다.

  
const http = require('http');  
  
// localhost -> 127.0.0.1 -> loop back -> 서버를 실행한 컴퓨터  
const host = 'localhost';  
const port = 3000;  
  
// req -> request -> 요청  
// res -> response -> 응답  
const server =  http.createServer((req, res) => {  
    res.writeHead(200, {'Content-Type': 'text/html'});  
    res.end('<h1>Hello World</h1>');  
});  
  
server.listen(port, host, () => {  
    console.log('server running')  
});

코드 흐름 해석

이번 코드에서 먼저 본 부분은 통신을 위한 기본 세팅이다.

const http = require('http');

Node.js에 내장된 http 모듈을 불러온다. 이 모듈이 있어야 웹 통신을 위한 서버를 만들 수 있다.

// localhost -> 127.0.0.1 -> loop back -> 서버를 실행한 컴퓨터  
const host = 'localhost';  
const port = 3000; 

서버의 주소와 문 번호를 정해주는 부분이다. 주석에 적은 localhost는 흔히 아는 IP 주소인 127.0.0.1과 같다. 이를 '루프백(loop back)'이라고 부르는데, 결국 외부 네트워크가 아니라 서버를 실행한 컴퓨터 자기 자신을 가리킨다는 뜻이다. port는 컴퓨터 내에서 프로그램을 구분하는 통로 번호 정도로 이해했다. 여기서는 3000번 통로를 사용하기로 했다.

// req -> request -> 요청  
// res -> response -> 응답  
const server =  http.createServer((req, res) => {  
    res.writeHead(200, {'Content-Type': 'text/html'});  
    res.end('<h1>Hello World</h1>');  
});

이 부분이 서버의 핵심이다. http.createServer는 서버를 생성하는 함수인데, 클라이언트(예: 웹 브라우저)가 요청을 보낼 때마다 안의 콜백 함수를 실행한다.
콜백 함수는 두 가지 객체를 인자로 받는다.

  • req (request): 클라이언트가 보낸 요청에 대한 정보를 담고 있다.
  • res (response): 서버가 클라이언트에게 줄 응답을 만들어내는 객체다.

함수 내부를 보면, res.writeHead를 통해 응답의 상태 코드(200은 성공을 의미)와 보낼 데이터의 형식(text/html)을 설정했다. 그리고 res.end를 통해 클라이언트에게 최종적으로 보낼 데이터(<h1>Hello World</h1>)를 담아 응답을 마무리한다.

server.listen(port, host, () => {  
    console.log('server running')  
});

마지막으로 생성만 한 서버를 실제로 켜는 부분이다. listen 함수를 이용해 아까 정한 hostport에서 들어오는 요청을 기다리게(listen) 만든다. 서버가 정상적으로 실행되면 콘솔창에 server running이라는 메시지를 출력하도록 했다.

헷갈릴 수 있는 부분

res.end()의 역할

단순히 데이터를 보내는 것처럼 보이지만, end()는 응답을 끝내는 역할을 한다. 이 함수가 호출되지 않으면 클라이언트는 서버가 아직 할 말이 남았다고 생각해 계속 기다리게 된다(결국 타임아웃 에러 발생). 즉, '이제 통신 끝!' 하고 알려주는 아주 중요한 마침표다.

hostport의 관계

흔히 '서버 주소'라고 하면 하나의 주소만 떠올리기 쉽다. 하지만 host(IP)가 건물의 주소라면, port는 그 건물 안의 호실 번호와 같다. 하나의 컴퓨터(host)에서도 여러 포트(port)를 열어 각각 다른 서버 프로그램을 동시에 실행할 수 있다.

정리

이번 코드를 따라 쳐보면서 눈에 보이지 않던 서버 통신의 기초적인 사이클을 이해할 수 있었다. 클라이언트가 요청(req)을 보내면, 서버는 그 요청을 받아 적절한 처리를 한 뒤 응답(res)을 돌려주는 구조다. 또한, 내 컴퓨터 자신을 가리키는 localhost(127.0.0.1)와 프로그램의 통로인 port의 개념도 확실히 잡을 수 있었다.