본문 바로가기
Javascript/Node.js

쿠키와 세션 이해하기

by 모스키토끼 2019. 10. 22.

쿠키의 역할

  • 클라이언트가 누구인지 기억하기 위해서 서버는 요청에 대한 응답을 할 때 쿠키라는 것을 같이 보내준다.
  • 클라이언트는  서버로 요청 할 때 마다 서버로부터 받은 쿠키를 같이 보내주어 클라이언트가 누구인지를 파악한다.
  • 쿠키는 요청과 응답의 헤더에 저장된다.

서버에서 쿠키를 만들어 요청자의 브라우저의 넣기

const http = require('http');

const parseCookies = (cookie = '')=>
	cookie
    	.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);
    console.log(req.url, cookies);
    res.writeHead(200, { 'Set-Cookie': 'mycookie = test' });
    res.end('Hello Cookie');
})
	.listen(8082, () => {
    	console.log('8082번 포트에서 서버 대기 중입니다!');
    });
    

코드설명

  • 쿠키는 name=kwon;year=2019처럼 문자열 형식으로 오는 데이터를 {name: 'kwon', year: '2019'}와 같이 객체로 바꾸는 함수이다.
  • createserver 메서드의 콜백에서는 제일 먼저 req 객체에 담겨 있는 쿠키를 분석한다.
  • (req.headers.cookie)
  • 응답의 헤더에 쿠키를 기록해야 하므로 res.writeHead 메서드를 사용한다.
  • Set-Cookie는  브라우저에게 다음과 같은 값의 쿠키를 저장하라는 의미이다.

결과:

8082번 포트에서 서버 대기 중입니다! 
/ { '': '' } 
/favicon.ico { mycookie: 'test' }

결과를 확인해보면 { : }과 쿠키( { mycookie: 'test'})값 옆에 favicon.ico 두 가지 요청이 있다.

처음 브라우저가 서버에 요청을 할 때 쿠키를 가지고 있지 않아서 공백이 찍히고 서버에서 쿠키를 가지고 있지 않으니까 쿠키를 심으라고 브라우저에서 명령하므로써 두번째 요청도 같이 들어온다.

 

두 번째의 왼쪽부분은 req.url이고 파비코은 웹 사이트 탭 옆에 보이는 이미지를 뜻한다.

-> 브라우저는 파비콘이 뭔지 html에서 유추할 수 없으면 서버에 파비콘 정보에 대한 요청을 보낸다.

위 예제가 그러한 경우이므로 브라우저가 추가로 favicon.ico를 요청한 것이다.

 

쿠키가 나인지를 식별하기

<!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>
const http = require('http');
const fs = require('fs');
const url = require('url');
const qs = require('querystring');

const parseCookies = (cookie = '')=>
	cookie
    	.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)}; Expires=${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('./server4.html', (err, data) => {
        	if(err){
            	throw err;
            }
            res.end(data);
        });
    }
})
	.listen(8083, () => {
    	console.log('8083번 포트에서 서버 대기 중입니다!');
    });

코드설명

  • 클라이언트가 root 주소로 요청을 하게 되면 else 부분으로 요청이 들어와 server4.html 파일을 보여준다.
  • 클라이언트가 server4.html에서 name을 입력하고 전송을 하면 /login 주소로 요청이되고 if문에 걸리게 된다.
  • url 부분들을 해석하고 쿠키 조건을 만든다음 res.wirteHead로 redirect 상태코드를 주어 루트 주소로 쿠키와 함께 재요청을 하게 한다.
  • 재요청이 들어오면 이제 쿠키가 존재하기 때문에 else if에 들어가게 되고  성공 상태코드를 response해준다.

쿠키 설정시 사용하는 옵션들

  • 쿠키명=쿠키값
  • Expires=날짜: 쿠키가 보존되는 기간
  • Max-agee=초: Expires와 비슷하지만 날짜 대신 초를 입력가능
  • Domin=도메인명: 쿠키가 전송될 도메인을 특정함. 기본값은 현재 도메인
  • Path=URL: 쿠키가 전송될 URL을 특정함. 기본값은 '/'
  • Secure: HTTPS: HTTPS일 때만 쿠키 전송가능
  • HttpOnly: 설정 시 자바스크립트에서 쿠키에 접근할 수 없다.

위 코드는 쿠키가 노출, 조작될 위험이 있다.

 

서버가 사용자 정보를 관리

const http = require('http');
const fs = require('fs');
const url = require('url');
const qs = require('querystring');

const parseCookies = (cookie= '') =>
    cookie
        .split(';')
        .map(v => v.split('='))
        .map(([k, ...vs]) => [k, vs.join('=')])
        .reduce((acc, [k, v]) => {
            acc[k.trim()] = decodeURIComponent(v);
            return acc;
        }, {})
        
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 = Date.now();
        session[randomInt] = {
            name,
            expires,
        };
        res.writeHead(302, {
            Location: '/',
            'Set-Cookie': `session=${randomInt}; Expires=${expires.toGMTString()}; 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('./server4.html', (err, data) => {
            if (err) {
                throw err;
            }
            res.end(data);
        });
    }
})
    .listen(8084, () =>{
        console.log('8084번 포트에서 서버 대기 중입니다.');
    });

 

 

 

쿠키를 보낼 때 이름대신 randomInt라는 임의의 숫자를 전송하고 쿠키의 이름과 만료시간을 session이라는 객체에

넣는다. session객체의 인덱스 randomInt에 이름과 만료시간을 넣어놓고 정보를 접근하여 사용한다. 

'Javascript > Node.js' 카테고리의 다른 글

Https와 Http2  (0) 2019.11.06
Rest API와 라우팅  (0) 2019.11.06
이벤트 이해하기  (0) 2019.10.19
요청과 응답 이해하기(서버구현)  (0) 2019.10.19
버퍼와 스트림 이해하기  (0) 2019.10.17

댓글