"서버를 구현한다"는 말은 꽤 넓은 의미라, 무엇을 하고 싶은 서버인지에 따라 접근이 달라져요.
그래도 처음 시작하는 사람 기준으로 기본적인 웹 서버 구현 절차를 단계별로 정리해드릴게요.
🧩 1. 서버의 개념 이해
서버는 쉽게 말해서 요청(Request)을 받아서 응답(Response)을 보내는 프로그램이에요.
예를 들어:
- 사용자가 브라우저에서 http://example.com 입력 → 요청 전송
- 서버가 요청을 받아 HTML, JSON 등을 응답 → 브라우저가 보여줌
⚙️ 2. 언어 선택
서버를 만들 수 있는 언어는 많아요:
| 언어 | 대표적인 프레임워크 | 특징 |
| JavaScript (Node.js) | Express.js, Fastify | 간단하고 빠르게 시작 가능 |
| Python | Flask, Django, FastAPI | 문법이 쉬워서 입문자에게 좋음 |
| Java | Spring Boot | 대규모 서비스에 적합 |
| Go | net/http, Gin | 빠르고 효율적 |
| Rust | Actix, Rocket | 성능 최강, 하지만 난이도 높음 |
처음이라면 👉 Python + Flask / Node.js + Express 추천드려요.
Q. Node.js 만으로 서버 만들 수 있을까? Express가 꼭 필요한가?
많은 초보 개발자들이 헷갈리는 부분인데,
지금 정확히 구분 짓고 가면 나중에 훨씬 이해가 빨라집니다.
하나씩 정리해드릴게요👇
🧩 1️⃣ Node.js만으로도 서버를 만들 수 있을까?
➡️ 가능합니다!
Node.js 자체에도 기본 내장 모듈(http) 이 있어서 --- http.createServer((req, res) => {})
별도의 설치 없이도 서버를 만들 수 있어요.
예시:
// http 모듈로 간단한 서버 만들기
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello Node.js!');
});
server.listen(3000, () => {
console.log('Server running at http://localhost:3000');
});
✅ 이렇게도 서버가 돌아갑니다.
⚙️ 2️⃣ 그런데 왜 Express.js를 많이 쓸까?
http 모듈만으로 서버를 만들 수 있지만,
👉 복잡한 기능을 직접 다 처리해야 해서 불편합니다.
예를 들어:
- 라우팅(/user, /login 등 경로 관리)
- POST 요청(body 데이터) 처리
- 정적 파일 서빙
- 에러 처리
- 미들웨어 관리
이런 걸 매번 직접 코드로 써야 하죠 😓
그래서 등장한 게 Express.js예요.
🚀 3️⃣ Express.js란?
Express.js는 Node.js 위에서 동작하는 웹 프레임워크입니다.
📦 설명하자면:
- Node.js의 http 모듈을 기반으로 만들어졌고,
- 웹 서버를 더 쉽게 만들 수 있도록 도구 / 규칙(구조)을 제공합니다.
예시 (Express.js 버전)
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello Express!');
});
app.listen(3000, () => {
console.log('Server running at http://localhost:3000');
});
➡️ 훨씬 깔끔하고 단순하죠?
라우팅, 요청 파싱, 에러 처리 등을 자동으로 처리해줍니다.
🧠 4️⃣ 프레임워크 vs 라이브러리의 차이
| 구분 | 라이브러리 | 프레임워크 |
| 주도권 | 내가 라이브러리를 호출함 | 프레임워크가 내 코드를 호출함 |
| 구조 | 내가 구조를 정함 | 프레임워크가 구조를 정해줌 |
| 예시 | jQuery, Lodash, React | Express.js, Django, Spring |
| 비유 | 재료만 주는 요리도구 | 레시피까지 정해주는 요리 키트 🍱 |
➡️ Express.js는 **서버 개발 구조를 정해주는 “프레임워크”**입니다.
(단, 구조가 유연해서 “가벼운 프레임워크”로 불리기도 해요.)
🧩 5️⃣ 결론 요약
| 질문 | 답변 |
| Node.js만으로 서버 만들 수 있어? | ✅ 가능 (http 모듈 사용) |
| Express.js는 꼭 설치해야 해? | ❌ 필수는 아님, 하지만 대부분 사용함 |
| Express.js는 뭐야? | Node.js 기반 웹 프레임워크 |
| 프레임워크랑 라이브러리 차이? | “누가 주도하느냐”의 차이 (Express는 프레임워크) |
이 질문은 진짜 “서버가 어떻게 응답을 주는지”의 핵심을 짚은 질문이에요.
지금처럼 정확히 이해하면, Express나 Flask 같은 프레임워크가 내부에서 뭘 하는지도 완벽히 알게 됩니다.
하나씩 차근차근 풀어드릴게요 👇
🧩 1️⃣ 전체 코드 복습
const http = require('http');
http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello Node.js!');
}).listen(3000);
이건 Node.js 기본 http 모듈로 웹 서버를 만든 코드예요.
- createServer() → 요청이 들어오면 실행될 콜백 함수를 등록
- (req, res) → 각각 요청(request) 객체 / 응답(response) 객체
- res.writeHead(...) → 응답 헤더를 작성
- res.end(...) → 응답 본문을 보내고 연결 종료
🧠 2️⃣ res.writeHead(statusCode, headers)의 의미
writeHead()는 **HTTP 응답의 "상태 코드(status code)"와 "헤더(headers)"**를 설정하는 함수예요.
형식:
res.writeHead(상태코드, 응답헤더)
예시:
res.writeHead(200, { 'Content-Type': 'text/plain' });
➡️ 이 한 줄이 아래와 같은 HTTP 응답을 만드는 거예요 👇
HTTP/1.1 200 OK
Content-Type: text/plain
📜 3️⃣ 첫 번째 인자 200 — 상태 코드 (Status Code)
200은 “요청이 정상적으로 처리됨”을 의미하는 HTTP 상태 코드예요.
즉, **“성공”**을 뜻합니다.
❓ 왜 { Status: 200 } 같은 객체로 안 쓰는가?
→ HTTP 프로토콜에서 상태 코드는 헤더가 아니라 "응답의 첫 줄"에 들어가기 때문이에요.
예시로 실제 HTTP 응답을 보면 👇
HTTP/1.1 200 OK
Content-Type: text/plain
- 200 OK ← 응답의 “상태 줄(status line)”
- Content-Type: text/plain ← 응답의 “헤더(header)”
- (이후 본문이 나옴)
즉,
상태 코드는 헤더와는 별도로 존재하는 정보이기 때문에
writeHead()의 첫 번째 인자로 따로 전달하는 거예요.
📬 4️⃣ 두 번째 인자 { 'Content-Type': 'text/plain' }
이건 **응답 헤더 (Response Header)**를 나타내는 객체예요.
key: value 쌍으로 되어 있습니다.
예시:
| 헤더 이름 | 의미 |
| Content-Type | 응답 본문의 데이터 타입 (text/plain, text/html, application/json 등) |
| Content-Length | 응답 데이터의 길이 |
| Set-Cookie | 쿠키 설정 |
| Cache-Control | 캐싱 정책 |
즉,
{ 'Content-Type': 'text/plain' }
은 브라우저에게
“이 응답은 단순한 텍스트야(
HTML 아니야)”
라고 알려주는 역할을 합니다.
💬 5️⃣ 왜 항상 200을 쓰는 건가요?
사실 “항상”은 아닙니다.
200은 **정상 처리(OK)**일 때 쓰는 기본 코드고,
요청의 결과에 따라 다른 코드를 써야 해요.
| 상태 코드 | 의미 |
| 200 | 성공 (OK) |
| 201 | 생성됨 (Created) |
| 301 | 영구 이동 (Redirect) |
| 400 | 잘못된 요청 (Bad Request) |
| 401 | 인증 필요 (Unauthorized) |
| 403 | 접근 금지 (Forbidden) |
| 404 | 찾을 수 없음 (Not Found) |
| 500 | 서버 내부 오류 (Internal Server Error) |
예를 들어:
if (req.url === '/') {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Home page');
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not found');
}
✅ 요약 정리
| 코드 요소 | 역할 | 설명 |
| res.writeHead(200, {...}) | 응답 상태 + 헤더 설정 | 200 = 성공 상태 코드 |
| 200 | 상태 코드 (status line) | “정상 처리됨”을 의미 |
| { 'Content-Type': 'text/plain' } | 응답 헤더 객체 | 응답 데이터의 타입을 명시 |
| { Status: 200 }로 안 쓰는 이유 | 상태 코드는 헤더가 아니라 응답 첫 줄에 따로 있음 | |
| 상태 코드는 왜 200? | 성공을 의미하며, 필요 시 404, 500 등으로 변경 가능 |
🧰 3. 예제: Node.js + Express로 서버 구현
아래는 가장 간단한 웹 서버 예제예요.
1️⃣ Node.js 설치
https://nodejs.org 에서 설치
2️⃣ 새 프로젝트 생성
mkdir my-server
cd my-server
npm init -y
npm install express
Q. npm init -y
npm init -y는 Node.js 프로젝트를 초기화할 때 사용하는 명령어예요.
하나씩 풀어서 설명드릴게요👇
🧩 npm이 뭐냐면
npm은 Node Package Manager의 약자예요.
Node.js에서 사용하는 **패키지(=라이브러리, 모듈)**를 설치하고 관리하는 도구예요.
즉, Node.js 세계에서
“패키지를 설치 / 프로젝트를 관리할 때 쓰는 프로그램”
⚙️ npm init이 하는 일
npm init 명령어는새Node.js 프로젝트를 시작할 때 기본 설정을 만드는 명령어예요.
실행하면 이런 대화식 질문들이 나와요 👇
name: (my-app)
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
그리고 다 입력을 마치면 package.json 이라는 파일이 생겨요.
📦 package.json 파일이란?
이 파일은 프로젝트의 설정 정보 / 의존성(라이브러리) 목록을 담고 있어요.
예를 들어 이런 식이에요 👇
{
"name": "my-server",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {},
"license": "ISC"
}
⚡ -y 옵션의 의미
-y는 **“yes”**의 약자예요.
즉, npm init -y 하면
위에서 물어보는 질문을 전부 기본값으로 자동 입력하고 바로 package.json을 만들어 줍니다.
➡️ “빠르게 초기 설정만 하고 싶을 때” 사용합니다.
💡 정리
| 명령어 | 설명 |
| npm init | 프로젝트 설정을 하나씩 물어보면서 생성 |
| npm init -y | 모든 값을 기본으로 자동 생성 |
예를 들어 아래처럼 하면 👇
mkdir my-server
cd my-server
npm init -y
➡️ my-server 폴더 안에 package.json이 자동으로 생깁니다.
그다음에 이런 식으로 라이브러리(express)를 설치하죠 👇
npm install express
Q. npm init 질문
🧩 npm init 질문 중 주요 항목들
1️⃣ entry point: (index.js)
- 뜻: “이 프로젝트의 **시작 파일(메인 파일)**은 뭐야?”
- Node.js가 프로젝트를 실행할 때, 가장 먼저 실행할 자바스크립트 파일을 지정하는 거예요.
예시:
entry point: (index.js)
그럼 package.json 안에 이렇게 기록돼요 👇
"main": "index.js"
즉, 누군가 이 패키지를 가져다 쓸 때
require('my-package')
라고 하면 index.js가 불러와지는 거예요.
💡 요약
entry point = 프로그램이 “여기서부터 시작해!” 하는 파일
(보통 index.js로 두지만, app.js나 server.js로 바꿔도 돼요)
2️⃣ keywords
- 뜻: “이 프로젝트를 설명하는 검색용 키워드들”
- npm에 공개할 때 사람들이 찾기 쉽게 해주는 태그 같은 거예요.
예시:
keywords: server, express, api
➡️ package.json에는 이렇게 저장돼요:
"keywords": ["server", "express", "api"]
💡 요약
keywords = 검색 태그 (npm 등록 시 도움이 됨).
개인 프로젝트라면 그냥 비워둬도 전혀 상관없어요.
3️⃣ license
- 뜻: “이 프로젝트를 다른 사람이 쓸 때, 어떤 이용 조건으로 허락할까?”
- 즉, “내 코드 마음대로 써도 돼?” 하는 법적 조건이에요.
예시:
- MIT → 자유롭게 사용 가능 (가장 흔함)
- ISC → MIT랑 거의 비슷 (기본값)
- GPL → 공개된 코드라면 수정/배포 시 공개 의무 있음
- UNLICENSED → 외부에 배포 안 할 때 사용
예시 결과
"license": "ISC"
💡 요약
license = 내 코드의 사용 허가 조건
개인 공부용이면 그대로 두고, 배포할 때만 신경 쓰면 돼요.
🎯 한눈에 요약
| 항목 | 의미 | 예시 | 꼭 필요? |
| entry point | 시작 파일 | index.js | ✅ |
| keywords | 검색용 태그 | ["server", "api"] | ❌ |
| license | 코드 사용 조건 | "MIT" | ✅ (기본값으로 OK) |
Q. cd
cd 명령어는 디렉토리(폴더)를 이동하는 명령어입니다.
📘 정리
- 명령어: cd
- 풀네임: change directory
👉 “디렉토리를 바꾼다”라는 뜻이에요.
💡 예시로 이해하기
예를 들어 이런 구조가 있다고 해요:
my-project/
├── index.js
└── src/
└── app.js
터미널에서:
cd my-project
➡️ my-project 폴더로 들어감
cd src
➡️ my-project/src 폴더로 들어감
cd ..
➡️ 한 단계 위로 올라감 (즉, 부모 폴더로 이동)
📂 자주 쓰는 관련 명령어들
| 명령어 | 설명 |
| pwd | 현재 위치(경로)를 보여줌 → print working directory |
| ls (macOS/Linux) / dir (Windows) | 현재 폴더의 파일 목록 보기 |
| cd 폴더명 | 해당 폴더로 이동 |
| cd .. | 상위 폴더로 이동 |
| cd / | 루트(최상위) 폴더로 이동 |
| mkdir 폴더명 | 새 폴더 만들기 (make directory) |
💬 즉,
- cd = 폴더 이동
- mkdir = 폴더 만들기
- touch(Mac/Linux) or type nul > 파일명(Windows) = 파일 만들기
3️⃣ index.js 파일 생성
const express = require('express');
const app = express();
const port = 3000;
// GET 요청 처리
app.get('/', (req, res) => {
res.send('Hello, Server!');
});
// 서버 실행
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
Q. const express = require('express'); const app = express();
지금 말씀하신 내용 거의 다 맞아요.
이제 제가 정확히 어떻게 연결되어 있는지 조금만 더 깊게 정리해드릴게요 👇
🧩 1️⃣ require('express')가 하는 일
맞습니다 ✅
const express = require('express');
이건 Node.js의 모듈 시스템(CommonJS) 문법이에요.
즉, "express"라는 이름의 외부 패키지를 불러와서(import) 내 파일에서 쓸 수 있게 하는 것이에요.
🔍 실행 순서 요약
- require('express')
→ Node.js가 node_modules/express 폴더를 찾아감 - 거기서 **index.js (또는 main으로 지정된 파일)**을 읽음
- 그 안에서 정의된 코드를 불러와서
→ 그 결과(=함수)를 express 변수에 저장
즉,
express 변수 안에는 “Express 프레임워크의 핵심 함수”가 들어있어요.
🧠 2️⃣ express는 뭐냐면…
express는 단순히 **객체/데이터가 아니라 “함수(function)”**입니다.
그래서 아래 코드처럼 **실행()**시켜야 해요 👇
const app = express();
⚙️ 3️⃣ express()는 뭘 하는 함수냐면
이건 “Express 애플리케이션 객체(app)”를 생성하는 함수예요.
이 객체가 바로:
- 라우팅(/, /users, /login 등) 처리
- 미들웨어 등록
- 요청/응답 관리
- 서버 실행 (app.listen())
같은 일을 담당합니다.
즉, express()는
“Express 프레임워크 엔진을 초기화해서 내 앱을 위한 서버 인스턴스를 만들어주는 함수”예요.
🧱 4️⃣ app은 뭐냐면
app은 express()가 반환한 서버 객체입니다.
이걸 이용해서 서버 기능을 정의해요 👇
app.get('/', (req, res) => {
res.send('Hello Express!');
});
이런 식으로 **라우트(route)**를 등록하거나,
미들웨어를 추가하거나, 서버를 실행합니다 👇
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
📦 5️⃣ 그럼 “express는 코드가 써진 파일들이 있는 거야?” → ✅ 맞아요!
express는 npm에서 설치된 **패키지(라이브러리)**예요.
즉, 내가 작성한 코드가 아니라, 누군가 만들어둔 코드 묶음이에요.
설치 경로는 대개 이렇습니다 👇
my-project/
├── index.js ← 내가 작성한 서버 코드
├── package.json
└── node_modules/
└── express/ ← 여기에 express 코드가 들어있음
그래서 require('express') 하면
Node.js가 node_modules/express 폴더 안에 있는 코드를 읽어옵니다.
🔁 요약
| 코드 | 설명 |
| require('express') | express 패키지(=외부 코드)를 불러옴 |
| express | express 모듈이 export한 “함수” |
| express() | express 서버 애플리케이션 객체 생성 |
| app | 생성된 express 애플리케이션 인스턴스 |
| app.listen() | 서버를 실행시킴 |
🧠 정리하자면:
require('express') → express 모듈의 코드를 읽어옴
express() → 그 모듈의 “생성 함수” 실행
app → 내가 조작할 수 있는 express 서버 객체
Q. app.listen(port, () => {})
app.listen()은 Express 서버를 실제로 실행하는 함수이고,
그 뒤에 붙은 () => {} 콜백 함수는 말씀하신 대로 서버가 포트에 성공적으로 연결된 후 실행되는 코드예요.
이걸 조금 더 깊게 정리해드릴게요 👇
🧩 1️⃣ app.listen()의 역할
Express에서 app.listen()은
👉 HTTP 서버를 시작(start) 하는 함수예요.
즉,
“이 서버를 특정 포트(port) 번호로 열어라!”
라는 명령이에요.
🔧 예시
const express = require('express');
const app = express();
const port = 3000;
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
실행하면 콘솔에 이렇게 나와요 👇
Server running on http://localhost:3000
이제 브라우저에서 http://localhost:3000 접속하면
이 서버로 요청이 들어옵니다.
⚙️ 2️⃣ 내부적으로 무슨 일이 일어나냐면
app.listen()은 사실 아래와 거의 동일한 일을 합니다 👇
즉,
- Node.js의 기본 http 모듈을 사용해서
- Express의 app을 **요청 핸들러(request handler)**로 등록하고
- 그걸 지정한 포트로 “청취(listen)”하도록 만드는 거예요.
💡 그래서 이름이 listen = “요청을 듣는다” 인 거예요.
(“요청을 기다리는 상태로 만든다”라는 뜻)
🧠 3️⃣ app.listen(port, callback) 매개변수 설명
| port | 서버가 열릴 포트 번호 | 3000 → http://localhost:3000 |
| callback | 서버가 실행된 후 호출되는 함수 | () => console.log('서버 실행!') |
그래서 이건 정확히 이렇게 읽힙니다 👇
“3000번 포트로 서버를 열고, 실행이 완료되면 콜백 함수를 실행해라.”
📡 흐름으로 보면 이렇게 돼요
🧱 4️⃣ 예시로 더 명확히 보기
실행하면:
그리고 브라우저에서 http://localhost:3000을 열면
“Hello Express!”가 뜹니다.
✅ 정리
| app.listen() | Express 서버 시작 |
| port | 서버가 열릴 포트 번호 |
| callback | 서버 실행 완료 시 실행될 함수 |
| 내부 동작 | http.createServer(app).listen(port, callback) 과 거의 동일 |
| 이름 뜻 | "요청을 듣는다(listen)" — 즉, 연결 대기 상태 |
💬 요약 한 줄
app.listen(port, callback)
→ “이 포트에서 요청을 기다려라. 연결되면 이 콜백 함수를 실행해라.”
4️⃣ 실행
node index.js
브라우저에서 http://localhost:3000 접속 → “Hello, Server!” 출력 🎉
🧱 4. 서버가 하는 일들
서버는 다음과 같은 역할을 수행해요:
- 요청 처리 (예: 로그인, 회원가입, 데이터 조회)
- DB와 통신 (MySQL, MongoDB 등)
- 파일 업로드 / 다운로드
- API 제공
- 보안 관리 (인증, 인가 등)
🚀 5. 다음 단계
서버 기초를 다지고 나면 이런 걸 배워가면 좋아요:
- REST API / GraphQL 개념
- 데이터베이스 연결 (예: PostgreSQL, MongoDB)
- 환경 변수 관리 (dotenv)
- 배포 (AWS, Render, Vercel 등)
- Docker로 서버 컨테이너화
'프로그래밍' 카테고리의 다른 글
| Date.now() / 1730000000000 / 1970-01-01 00:00:00 (UTC)의 관계 (0) | 2026.01.08 |
|---|---|
| clearInterval(tt) (0) | 2026.01.06 |
| querySelectorAll() 에서 query란? (0) | 2026.01.06 |
| async / Promise / await (0) | 2026.01.06 |
| AI 콘텐츠 블로그 탐지 (0) | 2025.10.31 |
| 서버 오픈 시간 늦는 이유 (0) | 2025.10.23 |
| 구글 스프레드시트 스크립트 :: Google Apps Sciprt(GAS) (0) | 2025.10.23 |
| 인증, 권한 관리 (0) | 2025.10.10 |