클라이언트에서 보내는 요청은 누가 보내는지 모른다는 단점이 있다.
누가 보내는지 기억하기 위해 서버는 요청에 대한 응답을 할 때 쿠키라는 것을 같이 보낸다. 하지만 쿠키는 유효시간이 있으며 단순한 '키-값'의 쌍이다.
쿠키는 요청의 헤더(Cookie)에 담겨 전송되고, 브라우저는 응답의 헤더(Set-Cookie)에 따라 쿠키를 저장한다.
cookie.js
const http = require('http');
http.createServer((req, res) => {
console.log(req.url, req.headers.cookie);
res.writeHead(200, { 'Set-Cookie': 'mycookie=test'});
res.end('Hello Cookie');
})
.listen(8083, () => {
console.log('8083번 포트에서 서버 대기 중입니다!');
});
createServer 메서드의 콜백에서는 req 객체에 담겨 있는 쿠키를 가져온다. 쿠키는 req.headers.cookie에 들어 있다.
Set-Cookie는 브라우저한테 다음과 같은 값의 쿠키를 저장하라는 의미이다.
localhost:8083에 접속해보자
첫번째 요청에서는 쿠키 정보가 없기 때문에 undefined가 출력되엇다. 두번째 요청(/favicon.ico)에서는 응답의 헤더에 mycookie=test라는 쿠키를 심으라고 명령했기 때문에 출력이 되었다.
하지만 그 쿠키가 나인지 식별은 해주지 못하고 있다.
cookie2.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>쿠키&세션 이해하기</title>
</head>
<body>
<form action="/login">
<input id="name" name="name" placeholder="이름을 입력하세요" />
<button id="login">로그인</button>
</form>
</body>
</html>
cookie2.js
const http = require('http');
const fs = require('fs').promises;
const path = require('path');
const parseCookies = (cookie = '') =>
cookie
.split(';')
.map(v => v.split('='))
.reduce((acc, [k, v]) => {
acc[k.trim()] = decodeURIComponent(v);
return acc;
}, {});
http.createServer(async (req, res) => {
const cookies = parseCookies(req.headers.cookie); // { mycookie: 'test' }
// 주소가 /login으로 시작하는 경우
if (req.url.startsWith('/login')) {
const url = new URL(req.url, 'http://localhost:8084');
const name = url.searchParams.get('name');
const expires = new Date();
// 쿠키 유효 시간을 현재시간 + 5분으로 설정
expires.setMinutes(expires.getMinutes() + 5);
res.writeHead(302, {
Location: '/',
'Set-Cookie': `name=${encodeURIComponent(name)}; Expires=${expires.toGMTString()}; HttpOnly; Path=/`,
});
res.end();
// name이라는 쿠키가 있는 경우
} else if (cookies.name) {
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end(`${cookies.name}님 안녕하세요`);
} else { //주소가 /이면서 name이라는 쿠키가 없는 경우
try {
const data = await fs.readFile(path.join(__dirname, 'cookie2.html'));
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(data);
} catch (err) {
res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end(err.message);
}
}
})
.listen(8084, () => {
console.log('8084번 포트에서 서버 대기 중입니다!');
});
쿠키명 = 쿠키값 : 기본적인 쿠키의 값. mycookie=test 또는 name=zero 같이 설정한다.
Expires = 날짜 : 만료 기한. 해당 기한이 지나면 쿠키가 제거된다. 기본값은 클라이언트가 종료될 때 까지.
Max-age = 초 : Expires와 비슷하지만 날짜 대신 초를 입력할 수 있다. 해당 초가 지나면 쿠키가 제거된다.(Expires 보다 우선됨)
Domain = 도메인명 : 쿠키가 전송될 도메인을 특정할 수 있다. 기본값은 현재 도메인.
Path=URL : 쿠기가 전송될 URL을 특정할 수 있다. 기본값은 '/'이고 이 경우 모든 URL에서 쿠키를 전송할 수 있다.
Secure : HTTPS 일 경우에만 쿠키가 전송된다.
HttpOnly : 설정 시 자바스크립트에서 쿠키에 접근할 수 없다. 쿠키 조작을 방지하기 위해 설정하는 것이 좋다.
새로고침을 해도 로그인이 유지가 된다.
원하는대로 동작을 하지만 이방식은 Application탭에서 확인되는것 처럼 쿠키가 노출되어 있다. 따라서 민감한 정보를 쿠키에 넣어두는것은 적절하지 못하다.
session.js
const http = require('http');
const fs = require('fs').promises;
const path = require('path');
const parseCookies = (cookie = '') =>
cookie
.split(';')
.map(v => v.split('='))
.reduce((acc, [k, v]) => {
acc[k.trim()] = decodeURIComponent(v);
return acc;
}, {});
const session = {};
http.createServer(async (req, res) => {
const cookies = parseCookies(req.headers.cookie);
if (req.url.startsWith('/login')) {
const url = new URL(req.url, 'http://localhost:8085');
const name = url.searchParams.get('name');
const expires = new Date();
expires.setMinutes(expires.getMinutes() + 5);
const uniqueInt = Date.now();
session[uniqueInt] = {
name,
expires,
};
res.writeHead(302, {
Location: '/',
'Set-Cookie': `session=${uniqueInt}; Expires=${expires.toGMTString()}; HttpOnly; Path=/`,
});
res.end();
// 세션쿠키가 존재하고, 만료 기간이 지나지 않았다면
} else if (cookies.session && session[cookies.session].expires > new Date()) {
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end(`${session[cookies.session].name}님 안녕하세요`);
} else {
try {
const data = await fs.readFile(path.join(__dirname, 'cookie2.html'));
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(data);
} catch (err) {
res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end(err.message);
}
}
})
.listen(8085, () => {
console.log('8085번 포트에서 서버 대기 중입니다!');
});
cookie2와 달라진 부분은, 쿠키에 이름을 담아서 보내는 대신, uniqueInt라는 숫자 값을 보냈다.
사용자의 이름과 만료 시간은 uniqueInt 속성명 아래에 있는 session이라는 객체에 대신 저장한다.
이제 cookie.session이 있고 만료 기한을 넘기지 않았다면 session변수에서 사용자 정보를 가져와 사용한다.
해당 코드는 실제 서비스에 사용하면 안된다. 개념을 확인 하기 위한 코드이며, 보안상 매우 취약하다
참고 : ⌜node.js 교과서⌟ 책을 공부하며 요약・정리한 내용입니다.
'💻 Web_Back end > node.js' 카테고리의 다른 글
[node.js] 익스프레스 웹 서버 만들기 (0) | 2023.05.11 |
---|---|
[node.js] npm (0) | 2023.05.11 |
[node.js] http 모듈로 서버 만들기 (0) | 2023.05.10 |
노드 파일 시스템 접근하기 (0) | 2023.05.09 |
노드 기능2 (내장 객체, 내장 모듈) (1) | 2023.05.08 |