ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 노드 쿠키 이해하기
    노드 2023. 4. 13. 15:47

    쿠키의 필요성

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

    • 누가 요청을 보냈는지 모른다. (IP 주소와 브라우저 정보 정도만 안다)
    • 로그인을 구현하면 해결되는 부분
    • 쿠키와 세션이 필요하다

     

    쿠키 : 키 = 값의 쌍

    • name = pocarl
    • 매 요청마다 서버에 동봉해서 보낸다.
    • 서버는 쿠키를 읽어 누구인지 파악

     

    처음에 요청을 보낼 때 서버가 응답할때  쿠키도 같이 보내준다. 

    그러면 브라우저는 쿠키를 저장해 놓다가 나중에 요청을 보낼때 쿠키와 함께 요청을 보내면 서버는 쿠키를 읽어서 그 요청이 누구에게서 왔는지 확인이 가능하다.  

     

     

    REST 요청 확인하는 법을 잠깐 다시 보고 가자.

    • Name은 요청 주소, Method는 요청 메서드, Status는 HTTP 응답 코드
    • Protocol은 HTTP 프로토콜,  Type은 요청 종류 (xhr은 AJAX 요청)

     

    쿠키 서버 만들기

    쿠키 넣는 것을 직접 구현

    • writeHead : 요청 헤더에 입력하는 메서드
    • 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번 포트에서 서버 대기 중입니다!');
      });

    헤더에 Set-Cookie를 사용한 것을 볼 수 있다. 

    이렇게 서버에서 쿠키를 보내주면 브라우저는 Set-Cookie를 파악해서 브라우저에다가 mycookie=test라는 쿠키를 넣는다. 

    그 다음 요청부터는 알아서 브라우저가 쿠키를 전달한다. 그러면 req.headers.cookie 이런 식으로 쿠키를 읽을 수 있게 된다. (요청의 헤더에 있는 쿠키)

    응답의 헤더로 Set-Cookie에 mycookie=test라는 쿠키가 있는 것을 확인 가능하다.

    이러면 브라우저의 Application 탭의 Cookies라는 항목에서 해당 쿠키가 들어있는 것을 볼 수 있다. 

    *브라우저를 닫으면 쿠키는 사라진다. 

     

    두 번째 요청부터는 브라우저가 쿠키를 알아서 전달한다고 했다. 실제로 그런지 확인을 해보자.

    새로고침을 해보면 새로고침을 할 때마다 쿠키의 정보를 읽을 수 있는 것을 확인 가능하다.

    console.log(req.url, req.headers.cookie); 코드에 의해서 쿠키를 읽는 것을 볼 수 있다. 

    *favicon.ico는 크롬에서 아이콘을 찾기 위해서 보내주는 거라 무시하면 된다. favicon이 궁금하면 찾아보길 바란다.

     

    헤더와 본문

    http 요청과 응답은 헤더와 본문을 가진다.

    • 헤더는 요청 또는 응답에 대한 정보를 가진다. 
    • 본문은 주고받는 실제 데이터이다. 
    • 쿠키는 부가적인 정보이므로 헤더에 저장된다.  

     

    쿠키로 나를 식별하기

    쿠키에 내 정보를 입력

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

    parseCookies는 req.headers.cookie를 (문자열) 객체로 바꿔주는 함수다. 만약 쿠키에 mycookie : test 이런 값이 있다면

    그것을 {mycookie: 'test'} 로 바꿔준다. 

     

    처음 실행했을때는 /login도 아니고 쿠키가 있는 것도 아니어서  else의 try 부분이 실행된다. 

     

    그리고 내용을 입력을 하고 로그인을 누르면 

    이런식으로 요청이 간 것을 볼 수 있다. 

    *요청 주소나 쿠키에 한글이 들어가면 알 수 없는 외계어처럼 나오는 것을 볼 수 있는데 이유는 인코딩 때문이다. 

    https://kmryu807.tistory.com/30

     

    Section 6. 자바스크립트 빌트인 전역 프로퍼티와 함수

    I. 빌트인 전역 프로퍼티 스스로 다른 프로퍼티나 메서드를 갖지 않고 값만 반환하는 특징이 있다. console.log(globalThis.Infinity); //Infinity console.log(globalThis.NaN); //NaN console.log(globalThis.undefined); //undefine

    kmryu807.tistory.com

     

    cookie2.html에서

    <form action="/login">
        <input id="name" name="name" placeholder="이름을 입력하세요" />
        <button id="login">로그인</button>
    </form> 을 하면 

    로그인을 한 다음에 데이터는 쿼리 스트링(?)으로 보내진다. 즉, 포칼이라는 것이 보내지는데

    get 요청으로 name이 넘어간다. 

    따라서 형태가 ?name=포칼이 이런 형태로 데이터가 서버로 전달이 되는 것이다. 

     

    서버에서는 당연히 /login 요청을 했으니 해당 부분

    if (req.url.startsWith('/login)) {} 부분이 실행이 된다. 

    해당 부분에서는 쿼리 스트링에서 name부분만 추출을 한 것을 볼 수 있다. 

    const { query } = url.parse(req.url);

    const { name } = qs.parse(query);

     

    그러면 name에는 포칼이 라는 데이터가 들어있다. 

    그리고 res.writeHead를 통해 Set-Cookie를 사용해서 직접 쿠키를 포칼이 라는 것으로 넣은 것을 볼 수 있다. 

    *302는 리다이렉션을 뜻한다. 302를 하면 / 주소로 다시 돌려 보내라~ 라는 뜻이다.

    res.writeHead(302, {
          Location: '/',
          'Set-Cookie': `name=${encodeURIComponent(name)}; Expires=${expires.toGMTString()}; HttpOnly; Path=/`,
        });

    /login으로 요청을 보냈지만 응답을 받으면서 /주소로 리다이렉트 되는 것이다. 

     

    *쿠키에 여러 옵션을 넣은 것을 볼 수 있다. 대표적으로 Expires를 셋팅하지 않으면 그 쿠키는 세션 쿠키가 된다. 세션 쿠키는 브라우저를 끄면 쿠키가 사라진다. 

    Expires를 통해 쿠키의 만료시간을 설정할 수 있다. 유효기간이 지난 쿠키는 브라우저가 서버에 자동으로 보내지 않는다. 

     

    *HttpOnly는 자바스크립트로 쿠키에 접근을 못하게 막는 코드이다. 해당 쿠키에 접근할 수 있다면 보안의 위협이 있기 때문이다. 

     

    *Path는 지금 있는 서버 주소 localhost:8084에 /아래에 있는 주소에서는 쿠키가 다 유효하다 라는 뜻이다. 

     

    그 다음 요청에서는 else if (cookies.name) {} 부분이 실행될 것이고 결과는 다음과 같다

     

    '노드' 카테고리의 다른 글

    노드 https, http2  (0) 2023.04.14
    노드 세션 사용하기  (0) 2023.04.13
    노드 POST, PUT, DELETE 요청 보내기  (0) 2023.04.13
    노드 REST API 서버 만들기  (1) 2023.04.13
    노드 fs로 HTML 읽어 제공하기  (0) 2023.04.13
Designed by Tistory.