Node.js

3-5. 노드 쿠키의 모든 것

홍주누 2023. 4. 13. 16:04

I. 쿠키의 필요성

 

1. 요청에는 한 가지 단점이 있음

- 누가 요청을 보냈는지 모름(IP 주소와 브라우저 정보 정도만 알 수 있음)

- 로그인을 구현하면 되지만, 그러려면 쿠키세션이 필요

 

2. 쿠키 : 키=값의 쌍

- name=junu

- 매 요청마다 서버에 동봉해서 보냄

- 서버는 쿠키를 읽어 누구인지 파악

 

 

 

 

 

II. 쿠키 서버 만들기

 

1. 쿠키 넣는 것을 직접 구현

- writeHead : 요청 헤더에 입력하는 메서드

- Set-Cookie : 브라우저에게 쿠키를 설정하라고 명령

 

2. 쿠키 : 키=값의 쌍

- name=junu

- 매 요청마다 서버에 동봉해서 보냄

 

// 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번 포트에서 서버 대기 중입니다!');
  });

위 코드를 실행시키고 나서 8083 포트에 접속한다.

 

그 다음 개발자도구(F12)를 열고, Network(네트워크) 탭에서 locathost > Headers 에 있는 Response Headers 를 살펴보면

아래와 같이 Set-Cookie: mycookie-test 를 확인할 수 있다.

 

 

또한 아래와 같이, Application(응용 프로그램) 탭에 있는 Cookies > http://localhost:8083 을 살펴보면 쿠키 정보를 확인할 수 있다.

쿠키의 유효기간(Expires)을 따로 설정하지 않는다Session 이 되는데, 이는 브라우저가 닫히기 전까지 유효하다는 뜻이다.

 

 

그리고 콘솔창을 확인해보면 아래와 같이 정보가 나와있는 것을 볼 수 있다.

이 때, / 요청과 /favicon.ico 요청이 있다.

favicon.ico 는 크롬에서 이 이미지를 알아서 요청하기 때문에 발생하는 요청으로, 그냥 무시하면 된다.

 

 

 

III. 헤더와 본문

 

http 요청과 응답은 헤더와 본문을 가짐

- 헤더요청 또는 응답에 대한 정보를 가짐

- 본문은 주고 받는 실제 데이터를 가짐

- 쿠키는 부가적인 정보이므로 헤더에 저장

 

 

 

IV. 쿠키로 나를 식별하기

 

쿠키에 내 정보를 입력

- parseCookies : 쿠키 문자열을 객체로 변환

- 주소가 /login 인 경우와 / 인 경우로 나뉨

- /login 인 경우 쿼리스트링으로 온 이름을 쿠키로 저장

- 그 외의 경우 쿠키가 있는지 없는지 판단

// 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 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;
    }, {});

http.createServer(async (req, res) => {
  const cookies = parseCookies(req.headers.cookie); // { mycookie: 'test' }
  // 주소가 /login으로 시작하는 경우
  if (req.url.startsWith('/login')) {
    const { query } = url.parse(req.url);
    const { name } = qs.parse(query);
    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 {
    try {
      const data = await fs.readFile('./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번 포트에서 서버 대기 중입니다!');
  });

실행 결과 화면

 

 

 

✨ cookie2.js 파일에서  /login 요청일 때, res.writeHead 를 주의깊게 살펴보자

name=${encodeURIComponent(name)} : 이름 데이터를 인코딩해서 받아라.

- cookie 에서는 한글을 사용하면 보안적으로 문제가 되기 때문에, 한글을 입력할 수 없게끔 되어있다.

따라서, 한글 데이터를 전달받으려면 encodeURIComponent 로 인코딩하여 받아야한다.

(이거 안쓰면 쿠키에 이상한 글자가 들어갔다는 에러 발생)

 

Expires=${expires.toGMTString()} : 쿠키의 만료기간(유효기간) 설정

- 위에서 말했듯이, 쿠키의 만료기간을 설정하지 않으면 Session 이 되고, 이는 브라우저 종료 시에 쿠키가 만료된다는 것을 의미한다.

반드시 필요한 설정은 아니지만 필요하다면, 해당 코드 3줄 상단에 있는 expires.setMinutes(expires.getMinutes() + 5) 처럼,

세션의 만료기간을 '현재시간으로부터 5분 후' 로 설정할 수 있다.

 

HttpOnly : JavaScript로 쿠키에 접근할 수 없게끔 한다.

- 보통 쿠키들은 JavaScript로 접근하게 되면 보안에 위협을 가하기 때문에, 특히 로그인을 위해 사용하는 쿠키에는 거의 필수적인 요소이다.

 

Path=/ : / 아래에 있는 주소에서는 쿠키가 모두 유효하다.

 

 

위의 코드들을 잘 읽어보고 난 다음에 분석에 성공하셨다면, 직접 실습해보면서 따라하는 것을 추천한다.

그리고 ✨ 부분을 집중해서 공부하고 나면, 5분이라는 시간은 거뜬히 지났을 것이다.

그렇다면, 8084 페이지에서 새로고침을 눌러보자.

로그인 되어있던 것이 풀리지 않았습니까?

과연 왜 그런 것일까?

 

여러분들은 이미 정답을 알고 있다.

이번 게시글을 잘 읽어보면서 정답을 스스로 찾아보자.