클라이언트가 보내는 요청의 단점, 누가 보낸 건지 몰라.
대신 로그인 구현하면 되는데 로그인 한후에 새로고침해도 로그아웃 안되도록
클라이언트가 서버에 누구인지 지속적으로 알려줘야함
쿠키
클라이언트 로컬에 저장되는
유효기간 있고 name=zerocho 처럼 단순한 Key-Value쌍의 작은 데이터 파일
쿠키의 구성요소
1. 이름 : 각각의 쿠키를 구별하는데 사용함
2. 유효시간: 쿠키의 유지시간
3. 도메인: 쿠키를 전송할 도메인
4. 경로: 쿠키를 전송할 요청 경로
5. 값: 쿠키와 이름과 관련된 값
1. 클라이언트 -> 페이지 request 요청
2. 서버에서 쿠키를 생성
3. http헤더에 쿠키를 포함해서 respond 응답
4. 브라우저가 종료되어도 쿠키의 기한이 정해져 있지 않고 명시적으로 지우지 않는다면 반 영구적으로 쿠키가 남아있게됨
클라이언트 | 요청(쿠키가지지 않은 상태) -------------> 쿠키와 함께 응답 <------------- 쿠키와 함께 요청 ---------------> 응답(쿠키 가진 상태) <--------------- |
서버 |
세션
브라우저가 종료되기 전까지 클라이언트의 요청을 유지하게 해주는 기술
세션은 어쨌든 쿠키를 기반으로 하고 있음
서버측에서 관리하긴함
사용자 많아질수록 서버 메모리 많이 먹어
클라이언트한테 아이디부여하는데 그게 세션아이디임
- 클라이언트가 서버에 로그인 요청
- 서버는 클라이언트의 로그인 요청의 유효성을 확인하고(아이디와 비밀번호 검사) unique한 id를 sessionid라는 이름으로 저장
- 서버가 응답할 때 응답헤더에 set-cookie: sessionid:a1x2fjz를 추가하여 응답
4 . 클라이언트는 이후 서버에 요청할 때 전달받은 sessionid:a1x2fjz쿠키를 자동으로 요청헤더에 추가하여 요청
5. 서버에서는 요청헤더의 sessionid 값을 저장된 세션저장소에서 찾아보고 유효한지 확인후 요청을 처리하고 응답
세션의 내용은 서버에 저장되기 때문에 계속하여 늘어날 경우 서버에 부하가 발생
쿠키와 세션의 차이점
가장 큰 차이점 정보가 저장되는 위치
저장위치
쿠키는 서버에 저장되지 않고 클라이언트 로컬에 저장됨
세션은 서버의 자원을 써서 로컬과 서버에 저장됨
장단점
보안면에서는 세션이 좋고
속도면에서는 쿠키가 빠름
세션은 쿠키를 이용해 id만 저장하고 서버에 저장해
세션도 만료시간 정할 수 있지만 브라우저가 종료되면 만료시간 상관없이 날라감
쿠키는 브라우저 종료해도 파일로 남아있음
얘를 보완해서 JWT가 나옴 책에도 나와~
책보면서 직접 코드 따라해보기
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번 포트에서 서버 대기 중입니다");
});
터미널
경로 입력하고 서버대기 중 확인
createServer 메서드의 콜백에서는 req 객체에 담겨있는 쿠키 가져옴
쿠키는
req.headers.cookie 에 들어있음
req.headers는 요청의 헤더 의미 (쿠키는 요청과 응답의 헤더를 통해 오감)
응답의 헤더에 쿠키를 기록해야 돼서
res.writeHead 메서드를 사용함
"Set-Cookie": "mycookie=test" 는 "다음에 오는 쿠키를 저장해라 : 다음"
로컬주소에 접속
req.url과 req.headers.cookies에 대한 정보를 로깅하도록 함
req.url은 주소의 path와 search부분을 알림 (사실 이부분 이해안돼서 찾아봤는데 이해안됨 그래도 읽어보자)
위 코드까지는 쿠키 심기만 한거고
그 쿠키가 나인지를 식별 못하고 있음
이제는 사용자를 식별하는 방법을 알아보자
cookie2.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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");
//쿠키는 문자열임 이를 쉽게 사용하기 위해 js객체 형식으로 바꾸는 parseCookies함수
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);
//주소가 /login으로 시작하는 경우,url과 querystring모듈로 각각 주소와 주소에 딸려오는 query분석한다
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);
//헤더에 302응답코드,리다이렉트 주소와 함께 쿠키를 넣어
//브라우저는 이 응답코드를 보고 페이지를 해당주소로 리다이렉트함
//헤더는 한글을 설정할 수 없어서 name변수를encodeURIComponent메서드로 인코딩함
res.writeHead(302, {
Location: "/",
"Set-Cookie": `name=${encodeURIComponent(
name
)}; Expires=${expires.toGMTString()};HttpOnly;path=/`,
});
res.end();
//그 외의 경우(/로 접속했을때 등),먼저 쿠키있나없나확인. 없으면 로그인할 수 있는페이지보냄
//처음 방문한경우 쿠키없으므로 cookie2.html 전송됨 쿠키있다면 로그인한상태로 간주해인사말보냄
//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번 포트에서 서버 대기중입니다!");
});
set-Cookie로 쿠키 설정할 때
만료시간 expires 과 HttpOnly, Path같은 옵션 부여함
쿠키 설정할 때 각종 옵션 넣을 수 있고 옵션 사이 세미콜론(;) 써서 구분
쿠키에는 한글 쓰기, 줄바꿈 하면 안됨
- 쿠키명=쿠키값 기본적 쿠키값 예) mycookie=test
- Expires=날짜: 만료기한. 기본값은 클라이언트가 종료될 때까지
- Domain= 도메인명: 쿠키가 전송될 도메인 특정할 수 있음. 기본값은 현재 도메인
- Path=url : 쿠키가 전송될 url을 특정할 수 있음 기본값은'/'이고 이경우 모든 url에서 쿠키 전송할 수 있음
- secure: HTTPS 일 경우에만 쿠키가 전송됨
- HttpOnly: 설정 시 자바스크립트에서 쿠키에 접근할 수 없음 쿠키 조작을 방지하기 위해 설정하는 것이 좋음
이제 이름 노출안되는 버전
세션 방식.
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;
}, {});
const session = {};
http
.createServer(async (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 uniqueInt = Date.now();
session[uniqueInt] = {
name,
expires,
};
res.writeHead(302, {
Location: "/",
"Set-Cookie": `session=${uniqueInt}; Expires=${expires.toGMTString()};HttpOnly;Path=/`,
});
res.end();
//세션 쿠키가 존재하고 만료기간이 지나지 않았다면
} else if (
cookies.session &&
session[cookies.session].expires > new Date()
) {
res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
res.end(`${session[cookies.session].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(8085, () => {
console.log("8085번 포트에서 서버 대기 중입니다");
});
코드 따라썼는데 자꾸 안된다고 에러뜸
바로 이 에러~!!
찾아서 따라해서 이전 포트? 종료하고 다시 함
로컬주소 들어가서 정보 입력하니까!
아까 넣었던 쿠키 위에 쌓였다.
이 방식이 세션임
아까랑 다른 건 쿠키에 이름을 담아서 보내는 대신, uniqueInt라는 숫자값을 보냄. 사용자의 이름과 만료시간은 uniqueInt 속성명 아래에 있는 session이라는 객체에 대신 저장함
이제 cookie.session이 있고 만료기한 넘기지 않았으면 session 변수에서 사용자 정보를 가져와 사용한다.
안전하게 사용하기 위해서는 다른 사람들이 만든 검증된 코드를 사용하는게 좋대
그건 책 5장에서~~~
그.런.데.
내가 코드를 다 이해했다? 전혀 아니지요.
map 메서드
배열.map((요소, 인덱스, 배열) => { return 요소 });
const oneTwoThree = [1, 2, 3];
let result = oneTwoThree.map((v) => {
console.log(v);
return v;
});
// 콘솔에는 1, 2, 3이 찍힘
oneTwoThree; // [1, 2, 3]
result; // [1, 2, 3]
oneTwoThree === result; // false
reduce 메서드
배열.reduce((누적값, 현잿값, 인덱스, 요소) => { return 결과 }, 초깃값);
result = oneTwoThree.reduce((acc, cur, i) => {
console.log(acc, cur, i);
return acc + cur;
}, 0);
// 0 1 0
// 1 2 1
// 3 3 2
result; // 6
acc(누적값)이 초깃값인 0부터 시작해서 return하는대로 누적되는 것을 볼 수 있습니다. 초깃값을 적어주지 않으면 자동으로 초깃값이 0번째 인덱스의 값이 됩니다.
그렇다고 함 이것이 바로 설명 링크.. 읽어도 잘모르겠지만 암튼 보자
NodeJS address already in use 문제 해결 - JooTC
NodeJS address already in use 문제 해결 방법 Error: listen EADDRINUSE: address already in use :::5000 현재 다른 프로세스에서 사용 중이라 해당 서비스를 시작할 수 없다는 에러입니다.
jootc.com
출처
'노드 node.js' 카테고리의 다른 글
node.js 로그인 창 만들기(npm,express,cookie)/ 에러 해결 app crashed - waiting for file changes before starting... (0) | 2021.09.16 |
---|---|
express 배우기 책 따라하는 중 p229-250 (0) | 2021.09.16 |
템플릿 엔진 Template Engine (0) | 2021.09.14 |
http랑 https 차이점 (0) | 2021.09.14 |
싱글스레드, 멀티스레드/ 블로킹,논블로킹/동기,비동기 [개념 정리] (0) | 2021.09.10 |