쿠키란 인터넷 사용자가 어떠한 웹 사이트를 방문할 경우 그 사이트가 사용하고 있는 서버를 통해 인터넷 사용자의 컴퓨터에 설치되는 작은 기록 정보 파일을 의미한다.

 

쿠키는 서버와 클라이언트에서 모두 저장하고 사용 가능하며, 일정 기간 동안 데이터를 저장할 수 있기 때문에 로그인 상태를 일정 시간 유지해야 하는 웹 사이트에서 사용한다. 또한 이 기록 파일에 담긴 정보는 인터넷 사용자가 같은 웹사이트를 방문할 때마다 읽히고 수시로 갱신된다.

 

이때 민감한 개인정보를 쿠키에 넣어두는것은 적절하지 못하다. 쿠키는 생각보다 쉽게 노출되며(개발자도구의 Application 탭) 또한, 조작될 위험도 있다.

그러므로 세션을 이용해서 서버가 사용자 정보를 관리하도록 해야한다.

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('='))
    .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 = +new Date();
    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번 포트에서 서버 대기중입니다!');
  });

 

http.createServer((req, res) => {
  const cookies = parseCookies(req.headers.cookie); //요청의 헤더에서 쿠키를 추출
  if (req.url.startsWith('/login')) { //요청의 url이 /login으로 시작할 경우. login 시도
    const { query } = url.parse(req.url); //요청에서 queryString을 추출
    const { name } = qs.parse(query); //queryString에서 name의 값을 추출
    const expires = new Date(); //현재 시간이 담긴 Date객체 생성
    expires.setMinutes(expires.getMinutes() + 5); //5분후 만료된다.
    const randomInt = +new Date(); //세션 식별자로 쓰일 Date 객체
    session[randomInt] = {
      name,
      expires,
    };
    /*응답으로 보낼 페이지의 헤더. 상태코드가 302 이므로 다른 페이지로 임시 이동 한다.*/
    res.writeHead(302, {
      Location: '/', //이동할 페이지
      'Set-Cookie': `session=${randomInt}; Expires=${expires.toGMTString()}; HttpOnly; Path=/`, //브라우저에 저장할 쿠키
    });
    res.end(); //응답 전송
  }
  /*브라우저에서 전송한 쿠키에 session이 담겨있고 세션이 만료되지 않았다면 true 아니면 false*/
  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) => { //전송할 html 파일을 읽어서 전송
      if (err) {
        throw err;
      }
      res.end(data);
    });
  }
})
  .listen(8083, () => {
    console.log('8084번 포트에서 서버 대기중입니다!');
  });

 

쿠키에 이름을 담아서 보내는 대신 randomInt 라는 임의의 순자를 보냈다. 이것을 세션 아이디라고 하며 서버에 사용자 정보를 저장하고 클라이언트와는 세션 아이디로만 소통한다.

(세션 아이디는 꼭 쿠키를 사용해서 주고 받지 않아도 된다. 실제 배포용 서버에서는 세션을 위와 같이 변수에 저장하지 않는다. 서버가 멈추거나 재시작되면 메모리에 저장된 변수가 초기화되고 또한 서버의 메모리가 부족하면 세션을 저장하지 못하는 문제도 생기기 때문이다. 그래서 보통은 데이터베이스에 넣어둔다.)

 

그러므로 사용자의 이름과 만료시간 같은 민감한 정보는 session이라는 객체에 대신 저장한다.

cookie.session이 있고 만료기한을 넘기지 않았다면 session 변수에서 사용자 정보를 가져와서 사용한다.

이러한 방식을 세션이라 한다.

쿠키명=쿠키값 기본적인 쿠키의 값. ex) name=tester
Expires=날짜 만료기한. 이 기한이 지나면 쿠키가 제거된다. 기본값은 클라이언트가 종료될때까지
Max-age=초 Expires와 비슷하지만 날짜 대신 초를 입력한다. 해당 초가 지나면 쿠키가 제거된다. Expires보다 우선한다.
Domain=도메인명 쿠키가 전송될 도메인을 특정한다. 기본값은 현재 도메인이다.
Path=URL 쿠키가 전송될 URL을 특정할 수 있다. 기본값은 '/'이고 이 경우 모든 URL에서 쿠키를 전송할 수 있다.
Secure HTTPS일 경우에만 쿠키가 전송된다.
HttpOnly 설정 시 자바스크립트에서 쿠키에 접근할 수 없다. 쿠키 조작을 방지하기 위해 설정하는 것이 좋다.
/*
쿠키 예시
'Set-Cookie': `name=${encodeURIComponent(name)}; Expires=${expires.toGMTString()}; HttpOnly; Path=/`
*/
const parseCookies = (cookie = '') => //쿠키 추출
  cookie 매개변수로 받은 cookie
    .split(';') //; 를 기준으로 쿠키들을 나눈것을 반환한다.
    .map(v => v.split('=')) //위의 split에서 넘겨받은 쿠키 문자열을 = 를 기준으로 나누어 [키, 값]의 형태로 배열에 담아 반환한다.
    .reduce((acc, [k, v]) => { //위의 map에서 넘겨받은 배열을 acc에 누적시킨다.
      acc[k.trim()] = decodeURIComponent(v); //acc에 공백을 지운 키와 디코드한 값을 누적
      return acc; //누적값을 return
    }, {});

 

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

REST API  (0) 2019.04.03
이벤트  (0) 2019.04.02
express  (0) 2019.03.08
로그 - winston 모듈  (0) 2019.03.08
템플릿 엔진 모듈 - ejs, pug 모듈  (0) 2019.03.07

+ Recent posts