http
모듈은 클라이언트의 요청을 처리한다. createServer(콜백)
메서드로 서버를 생성하고, 요청이 들어올 때마다 콜백을 실행한다. 콜백에는 요청을 뜻하는 req(request)
와 응답을 뜻하는 res(response)
가 매개변수로 들어있다.
import http from "http";
const server = http.createServer((req, res) => {
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
res.write("<h1>Hello Node!</h1>");
res.end("<p>Hello Server!</p>");
});
server.listen(4000, () => {
console.log("Server on http://localhost:4000");
});
콜백의 res.writeHead
는 응답의 정보가 담긴 헤더(header)
를 작성한다. 200
이라는 숫자는 http 상태 코드
로, 브라우저가 요청의 성공 여부를 판단하는 코드이다. 여러 상태 코드는 MDN - HTTP 상태 코드에서 확인할 수 있다.
res.write
는 본문을 작성하는 구간이고, res.end
는 응답의 종료를 의미한다.
listen(포트, 콜백)
으로 서버를 이벤틀 리스너에 등록한다. 여기서 콜백은 on
을 이용하여 따로 동작할 수 있다.
server.listen(4000);
server.on("listening", () => {
console.log("Server on http://localhost:4000");
});
server.on("error", (err) => {
console.log(err);
});
변경 사항은 자동으로 반영되지 않으니 서버를 종료했다가 다시 실행해야 한다.
createServer
를 여러 개 생성하여 여러 서버를 구동할 수도 있다. 이때 포트 번호는 저마다 달라야 한다.
fs
모듈의 promises
를 가져와 비동기 형식으로 html
파일을 읽었다.
import http from "http";
import { promises } from "fs";
const server = http.createServer(async (req, res) => {
try {
const data = await promises.readFile("./server.html");
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
res.end(data);
} catch (error) {
res.writeHead(500, { "Content-Type": "text/html; charset=utf-8" });
res.end(error.message);
}
});
server.listen(4000);
server.on("listening", () => {
console.log("Server on http://localhost:4000");
});
REST
는 REpresentational State Transfer
의 약자로, 주소를 통해 서버에 자원을 요청하는 일종의 규칙이다. url
에 /todolist
를 포함한다면 할 일 목록을 요청하는 것으로 볼 수 있다.
이때, 무슨 동작을 원하는지 알 수 없기 때문에 HTTP 요청 메서드
를 사용해 요청 동작을 수행한다. 자주 사용하는 메서드는 다음과 같다.
GET
: 서버 자원을 가져온다. 서버에 데이터를 보내야 하면 쿼리스트링(?key=value
)을 사용한다.POST
: 서버에 자원을 새로 등록한다.PUT
: 서버에 있는 자원을 요청에 담긴 자원으로 바꾼다.PATCH
: 서버에 있는 자원의 일부만 수정한다.DELETE
서버 자원을 삭제한다.http.createServer(async (req, res) => {
try {
if (req.method === "GET") {
if (req.url === "/") {
// "/" 페이지 처리
} else if (req.url === "/detail") {
// "/detail" 페이지 처리
}
} else if (req.method === "POST") {
if (req.url === "/new") {
// new 본문 처리
}
} else if (req.method === "PUT") {
// PUT 처리
} else if (req.method === "DELETE") {
// DELETE 처리
}
} catch (err) {
// error 처리
}
});
서버는 클라이언트의 요청을 구분하지 못한다. 요청을 구분하기 위한 방법으로 쿠키
와 세션
이 있다.
쿠키
는 키-값
으로 저장된 정보이며 유효기간이 있다. 서버에서 미리 클라이언트를 추정할 정보를 쿠키로 만들어 응답하고, 클라이언트는 서버에 요청을 보낼 때마다 쿠키를 동봉한다.
import http from "http";
const server = http.createServer(async (req, res) => {
res.writeHead(200, { "Set-Cookie": "mycookie=test" });
res.end("<h1>Hello Cookie!</h1>");
});
server.listen(4000);
server.on("listening", () => {
console.log("Server on http://localhost:4000");
});
개발자 도구의 네트워크
탭을 확인하면 Response Headers
에 Set-Cookie: mycookie=test
가 담겨 있음을 확인할 수 있다.
자주 사용하는 쿠키 설정은 아래와 같다.
쿠키명=쿠키값
: 기본 쿠키 값Expires=날짜
: 만료 기한. 기본값은 클라이언트 종료이다.Max-age=초
: 날짜 대신 초를 입력할 수 있다. 해당 초 경과 후 쿠키가 제거된다. Expires
보다 우선한다.Domain=도메인명
: 쿠키가 전송될 도메인을 특정한다. 기본값은 현재 도메인이다.Path=URL
: 쿠키가 전송될 URL을 특정한다. 기본값은 "/"이다. 기본값으로 설정했다면 모든 URL에 쿠키를 전송할 수 있다.Secure
: HTTPS일 경우에만 쿠키가 전송된다.HttpOnly
: true
일 시 자바스크립트에서 쿠키에 접근할 수 없다. 쿠키 조작 방지를 위해 설정하는 편이 좋다.어플리케이션
탭을 확인해 보면 쿠키의 문제점을 알 수 있다. 설정한 쿠키값이 그대로 노출된다. 이러한 이유로 개인정보를 쿠키에 저장하는 것은 적절하지 못하다. 이때 세션
을 사용할 수 있다. 쿠키에 session=value
를 저장하여 서버에서 개인정보를 처리하도록 한다.
const server = http.createServer(async (req, res) => {
let user = "Cookie";
if (req.headers.cookie) {
const [key, value] = req.headers.cookie.split("=");
if (value === "AA1234") {
user = "Test";
}
}
res.writeHead(200, {
"Set-Cookie": `session=AA1234`,
});
res.end(`<h1>Hello ${user}!</h1>`);
});
세션
값에 임의의 문자열을 기입했다. 서버에서 쿠키에 담긴 session
의 값을 확인하고 처리한다. 보안을 위한다면 검증된 라이브러리를 사용하는 것이 좋다.
https
는 웹 서버에 SSL
암호화를 추가하는 모듈이다. 요청과 응답 데이터를 암호화하여 누군가 요청을 가로채더라도 확인할 수 없게 만든다.
서버에 적용하려면 인증 기관에서 발급받은 인증서가 필요하다. Let's Encrypt 같은 기관에서 무료로 발급해준다고도 한다.
http2
모듈은 SSL
암호화와 더불어 최신 HTTP 프로토콜인 http/2
를 사용할 수 있다. http/1.1
보다 개선되어 훨씬 효율적으로 요청을 보낸다.