-
노드 쿠키 이해하기노드 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