서버는 요청을 받는 응답자이고, 클라이언트는 요청을 보내는 요청자입니다.
웹 서버: 웹사이트를 보여주는 서버.
파일 서버: 파일을 저장하고 전송하는 서버.
게임 서버: 온라인 게임에서 플레이어 간 연결을 관리하는 서버.
호스팅 서버: 웹사이트를 저장하고 관리하는 서버.
NPM 명령어 로 프로젝트를 생성합니다.
node-server
프로젝트 폴더를 생성하고, npm init
명령어로 package.json
파일을 생성합니다.
$ mkdir node-server
$ cd node-server
$ npm init -y
서버 만들기 전에 w3schools의 Node.js HTTP 모듈을 참고하세요.
HTTP 서버는 클라이언트(브라우저)의 요청을 받고, 이에 응답하는 프로그램입니다.
Node.js는 http 모듈을 사용해 쉽게 서버를 생성할 수 있습니다.
http.createServer()
메서드로 서버를 생성합니다.server.listen()
메서드로 서버를 실행합니다.// server.js
// Node.js의 기본 내장 모듈인 'http' 모듈을 불러옵니다.
const http = require('http');
// http 서버를 생성하는 메서드
// 콜백 함수로 request(요청)과 response(응답) 객체를 매개변수로 받습니다.
const server = http.createServer((req, res) => {
// 요청이 들어오면 응답합니다.
res.writeHead(200, { 'Content-Type': 'text/html' }); // 응답 헤더 설정
res.write('<h1>Hello, Node.js!</h1>'); // 응답 본문
res.end('<p>http 모듈 공부 중...</p>'); // 응답 종료
});
// 서버가 8080 포트에서 실행되도록 설정합니다.
// .listen() : 특정 포트에서 서버를 실행하고 클라이언트의 요청을 기다립니다.
server.listen(8080, () => {
console.log('8080 포트에서 서버가 실행 중입니다.');
});
$ node server.js
8080 포트에서 서버가 실행 중입니다.
HTTP 상태 코드는 클라이언트의 요청에 대한 서버의 응답 상태를 나타내는 코드입니다.
참고: MDN HTTP 상태 코드
Node.js에서 리스닝 이벤트는 특정 상황(이벤트)이 발생했을 때 실행되는 코드
서버가 클라이언트의 요청을 받아들이기 시작할 때 발생하는 이벤트입니다.
server.listen()
메서드는 서버를 실행하고 클라이언트의 요청을 기다리는 메서드입니다.listening
이벤트가 발생합니다.server.on('listening', 콜백함수)
로 리스닝 이벤트를 등록할 수 있습니다.server.on('error', 콜백함수)
로 에러 이벤트를 등록할 수 있습니다.// server.js
const http = require('http');
// 서버 생성
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write('<h1>Hello, Node.js!</h1>');
res.end('<p>http 모듈 공부 중...</p>');
});
// `listening` 이벤트 등록
server.on('listening', () => {
console.log('서버가 정상적으로 실행되었습니다!');
});
// `error` 이벤트 등록
server.on('error', (error) => {
console.error('서버 실행 중 오류 발생:', error.message);
});
// 서버 실행
server.listen(8080, () => {
console.log('8080 포트에서 서버가 실행 중입니다.');
});
$ node server.js
8080 포트에서 서버가 실행 중입니다.
서버가 정상적으로 실행되었습니다!
크롬의 주소창에 http://localhost:8080
을 입력하면, "Hello, Node.js!"가 출력됩니다.
터미널에서는 메시지가 보이지만, 크롬의 개발자 도구 콘솔창에서 메시지가 보이지 않는 이유는 터미널 출력(console.log)와 클라이언트(브라우저) 출력이 서로 다른 개념이기 때문입니다.
이유
1. 서버 측 로그
- console.log는 Node.js 서버에서 실행되는 코드에서 발생한 로그를 출력하기 때문에
- 위 메시지는 서버의 터미널에서만 확인할 수 있습니다.
클라이언트(브라우저) 로그
파일을 수정할 때마다 서버를 재시작하는 것이 번거롭다면, nodemon
패키지를 사용해보세요.
$ npm install -g nodemon
$ nodemon server.js
{
"scripts": {
"start": "nodemon server.js"
}
}
$ npm start
응답 콜백에 html을 직접 넣어주는 것이 아니라 파일을 따로 만들어 파일시스템(fs)을 이용해 읽어서 보내는 방법
fs
파일을 읽어오는 모듈fs.promises.readFile()
파일을 읽어오는 메서드res.end(data)
로 파일을 응답 본문으로 보내면서 요청 종료$ touch index.html
<!-- index.html -->
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>Node.js로 서버 만들기</title>
</head>
<body>
<h1>Node.js로 서버 만들기</h1>
<p>파일을 읽어와 응답하는 서버</p>
</body>
</html>
aync/await
문법을 사용하면 비동기 처리를 동기 처리처럼 작성할 수 있습니다.try-catch
문법을 사용하면 에러 처리를 쉽게 할 수 있습니다.// fs-test.js
const http = require('http');
const fs = require('fs');
const server = http.createServer(async (req, res) => {
try {
// fs.promises.readFile() : 파일을 읽어오는 메서드
const data = await fs.promises.readFile('./index.html');
// 200이면 성공
res.writeHead(200, { 'Content-Type': 'text/html' });
// 파일을 읽어온 data를 응답 본문으로 보내면서 요청 종료
res.end(data);
} catch (error) {
console.error(error);
// 500이면 서버 오류
res.writeHead(500, { 'Content-Type': 'text/plain' });
// 에러 메시지를 응답 본문으로 보내면서 요청 종료
res.end(error.message);
}
});
// 8080 서버 실행
server.listen(8080, () => {
console.log('8080 포트에서 서버가 실행 중입니다.');
});
Node.js의 HTTP 모듈을 사용하면 서버에서 요청 객체(req)와 응답 객체(res)를 사용할 수 있습니다.
req.url
: 클라이언트가 요청한 URL 주소req.method
: 클라이언트가 요청한 HTTP 메서드req.headers
: 클라이언트의 요청 헤더 정보res.writeHead(상태코드, 헤더객체)
: 응답 헤더 설정res.write(데이터)
: 응답 본문 작성res.end(데이터)
: 응답 종료REST(Representational State Transfer)는 자원을 이름(자원의 표현)으로 구분하여 해당 자원의 상태(정보)를 주고 받는 모델입니다.
req.url
로 요청한 URL 주소를 확인하고, 해당 URL에 따라 다른 응답을 보내는 방식if문
을 사용해 URL에 따라 다른 응답을 보내는 방식을 라우팅이라고 합니다.<!-- index.html -->
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>Node.js로 서버 만들기</title>
</head>
<body>
<h1>Main</h1>
</body>
</html>
<!-- about.html -->
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>About : Node.js로 서버 만들기</title>
</head>
<body>
<h1>About</h1>
</body>
</html>
// rest.js
const http = require('http');
const fs = require('fs');
const server = http.createServer(async (req, res) => {
try {
if (req.url === '/') {
const data = await fs.promises.readFile('./index.html');
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(data);
} else if (req.url === '/about') {
const data = await fs.promises.readFile('./about.html');
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(data);
} else {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('<h1>404 Not Found</h1>');
}
} catch (error) {
console.error(error);
res.writeHead(500, { 'Content-Type': 'text/plain' });
res.end(error.message);
}
});
server.listen(8080, () => {
console.log('8080 포트에서 서버가 실행 중입니다.');
});
서버를 실행하고, 크롬의 주소창에 http://localhost:8080
, http://localhost:8080/about
를 입력해보세요.
크롬 브라우저에서 개발자 도구를 열고 Network
탭을 확인하면, 요청과 응답 상태 코드를 확인할 수 있습니다.
쿼리스트링(Query String)은 URL 주소에 데이터를 포함하여 서버로 전달하는 방식입니다.
req.url
로 요청한 URL 주소를 확인하고, URL에 포함된 쿼리스트링을 분석하는 방식?
를 사용해 쿼리스트링을 추가하고, &
로 여러 개의 쿼리스트링을 구분합니다.url.parse()
메서드로 URL을 분석하고, querystring
모듈로 쿼리스트링을 분석합니다.http://localhost:8080/?category=shoes&size=230
?
: 쿼리스트링의 시작category=shoes
: category가 shoes인 쿼리스트링&
: 쿼리스트링 구분자size=230
: size가 230인 쿼리스트링url.parse()
메서드로 URL을 분석하면, URL의 여러 정보를 객체로 반환합니다.url.parse(주소)
: URL을 분석하여 URL 객체를 반환합니다.url.parse(주소, true)
: URL을 분석하여 URL 객체와 쿼리스트링 객체를 반환합니다.// 이런 URL이 있다면
'http://localhost:8080/search?category=shoes&color=black&size=260'
// url.parse()로 분석하면 이렇게 분리됩니다
{
protocol: 'http:',
host: 'localhost:8080',
pathname: '/search',
query: 'category=shoes&color=black&size=260'
}
querystring
모듈은 URL의 쿼리스트링을 객체로 변환하거나, 객체를 쿼리스트링으로 변환하는 모듈입니다.querystring.parse(쿼리스트링)
: 쿼리스트링을 객체로 변환합니다.querystring.stringify(객체)
: 객체를 쿼리스트링으로 변환합니다.const querystring = require('querystring');
const parsedQuery = querystring.parse(parsedUrl.query);
// 이제 parsedQuery는 객체 형태가 됩니다
{
category: 'shoes',
color: 'black',
size: '260'
}
https://shop.com/?category=shoes&color=black&size=260
이는 다음을 의미합니다.
// querystring.js
const http = require('http');
const url = require('url');
// querystring 모듈을 불러옵니다.
const querystring = require('querystring');
const server = http.createServer((req, res) => {
// URL 문자열을 URL 객체로 변환
const parsedUrl = url.parse(req.url);
// URL 객체에서 쿼리스트링을 객체로 변환
const query = querystring.parse(parsedUrl.query);
// 상품 검색 처리
if (query.category || query.color || query.size) {
// 검색 조건 메시지 생성
let searchMessage = '<h2>검색 조건:</h2>';
if (query.category) searchMessage += `<p>카테고리: ${query.category}</p>`;
if (query.color) searchMessage += `<p>색상: ${query.color}</p>`;
if (query.size) searchMessage += `<p>사이즈: ${query.size}</p>`;
// 검색 결과 메시지 (실제로는 데이터베이스 조회 결과가 들어갈 부분)
const resultMessage = `
<h2>검색 결과:</h2>
<div>
<h3>상품명: ${query.color} ${query.category}</h3>
<p>사이즈: ${query.size}</p>
<p>가격: 89,000원</p>
</div>
`;
// HTML 응답
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(`
<h1>쇼핑몰 상품 검색</h1>
${searchMessage}
${resultMessage}
`);
} else {
// 검색 조건이 없을 때
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(`
<h1>상품을 검색해주세요</h1>
<p>예시: /?category=shoes&color=black&size=260</p>
`);
}
});
// 서버 실행
server.listen(8080, () => {
console.log('쇼핑몰 서버가 8080 포트에서 실행 중입니다.');
});
서버를 실행하고, 크롬의 주소창에 http://localhost:8080/?category=shoes&color=black&size=260
를 입력해보세요.