자! 모듈에 대한 공부는 어느정도 했으니 node.js를 사용해서 웹 서버를 만들어보죠. 웹 서버를 만들기 위해서 http모듈을 사용할 것 입니다.
const http = require("http");
const server = http.createServer((req, res) => {
res.write("<h1>hi node.js!</h1>"); // 클라이언트로 데이터를 보냅니다.
res.end("<p>hello my first node.js server!</p>"); // 클라이언트로 데이터를 보내고 응답을 종료합니다.
});
server.listen(8080);
server.on("listening", () => {
console.log("8080포트 서버 대기 중!");
});
server.on("error", (err) => {
console.error(err);
});
end
메서드를 사용할 때 인자가 있으면 그 인자를 마지막 데이터로 보내고 응답을 종료합니다.on
메서드로 각각 listening
과 error
이벤트에 해당되는 콜백 함수를 등록할 수 있습니다.html 파일을 클라이언트로 보낼 수도 있습니다. 특히 앞 장에서 배운 버퍼를 보낼 수도 있어요.
const http = require("http");
const fs = require("fs");
const server = http.createServer((req, res) => {
fs.readFile("./server.html", (err, data) => {
if (err) throw err;
res.end(data); // toString()으로 변환하지 않고 바로 버퍼를 보냈죠?
});
});
server.listen(8080);
server.on("listening", () => {
console.log("8080포트 서버 대기 중이다. 오버!");
});
server.on("error", (err) => {
console.error(err);
});
위에서 만들어본 서버는 모든 요청에 대해서 항상 같은 응답을 반환합니다. 즉, 클라이언트를 구별하지 않는다는 말이죠. 그럼 구별하려면 어떻게 해야 할까요? 여기서 그 유명한 쿠키와 세션이 등장합니다.
클라이언트를 기억하기 위해서 서버는 첫 요청에 대해 응답할 때 쿠키라는 것을 보내줍니다. 브라우저는 쿠키를 저장하고 서버에 요청을 보낼 때 이 쿠키를 같이 보내죠. 즉, 내가 누구에요! 라고 계속 말해주는 것입니다. 따라서 우리는 클라이언트로 보내는 쿠키를 구현하기만 하면됩니다. 이후엔 브라우저가 자동으로 쿠키를 보낼거거든요.
쿠키는 요청과 응답의 헤더에 저장됩니다. 요청과 응답 각각은 헤더(header)와 본문(body)으로 되어있어요. 자 그럼 로그인 기능을 맛보기로 만들면서 브라우저로 쿠키를 보내봅시다.
const http = require("http");
const fs = require("fs");
const url = require("url");
const qs = require("querystring");
const parseCookies = (cookies = "") =>
cookies
.split(";")
.map((v) => v.split("="))
.map(([k, ...vs]) => [k, vs.join("=")])
.reduce((acc, [k, v]) => {
acc[k.trim()] = decodeURIComponent(v);
return acc;
}, {});
http
.createServer((req, res) => {
const cookies = parseCookies(req.headers.cookie);
if (req.url.startsWith("/login")) {
const {query} = url.parse(req.url);
const {name} = qs.parse(query);
const expires = new Date();
expires.setMinutes(expires.getMinutes() + 5);
res.writeHead(302, {
Location: "/",
"Set-Cookie": `name=${encodeURIComponent(
name
)}; Expire=${expires.toGMTString()}; HttpOnly; Path=/`,
});
res.end();
} else if (cookies.name) {
res.writeHead(200, {"Content-Type": "text/html; charset=utf-8"});
res.end(`${cookies.name}님 안녕하세요.`);
} else {
fs.readFile("./server.html", (err, data) => {
if (err) throw err;
res.end(data);
});
}
})
.listen(8081, () => {
console.log("8081포트에서 서버 대기중입니다.");
});
쿠키를 설정 시에 다양한 옵션을 줄 수 있습니다.
그런데 만약 위와 같이 코드를 작성해서 브라우저 쿠키를 보내게 되면 사용자의 개인정보가 누출될 수 있는 위험이 있겠죠. 따라서 서버단에서 정보를 처리해줘야합니다. 즉, 세션 처리하는 로직을 보죠.
const session = {};
http
.createServer((req, res) => {
const cookies = parseCookies(req.headers.cookie);
if (req.url.startsWith("/login")) {
const {query} = url.parse(req.url);
const {name} = qs.parse(query);
const expires = new Date();
expires.setMinutes(expires.getMinutes() + 5);
const randomInt = new Date();
session[randomInt] = {
name,
expires,
};
res.writeHead(302, {
Location: "/",
"Set-Cookie": `session=${randomInt}; Expires=${expires.toISOString()}; HttpOnly; Path=/`,
});
res.end();
} else if (
cookies.session &&
session[cookies.session].expires > new Date()
) {
res.writeHead(200, {"Content-Type": "text/html; charset=utf-8"});
res.end(`${session[cookies.session].name}님 반갑습니다.`);
} else {
fs.readFile("./server.html", (err, data) => {
if (err) throw err;
res.end(data);
});
}
})
.listen(8081, () => {
console.log("8081포트 작동 중");
});
이 방법을 사용하면 실제 정보를 브라우저에 저장하는 것이 아니라 설정한 randomInt를 통해서만 소통하게 됩니다. 하지만 실제로는 세션을 변수에 저장하지 않습니다. 메모리에 초기화되거나 부족하면 세션을 사용할 수 없기 때문이죠. 절대로 이렇게 사용하지마세요. 그냥 쿠키와 세션의 개념을 잡기 위해 정리해본 겁니다.
- 조현영『Node.js 교과서』, (주)도서출판 길벗(2019년 2월 2일), p.103~ 110