#1. Cookie
1. Cookie의 개념
1) Cookie란?
- 변수를 클라이언트(웹브라우저)에 텍스트 파일 형식으로 저장해 놓는것을 말한다.
- 저장 주체가 백엔드일 경우 백엔드가 갖고 있는 변수 값을 프론트엔드에 보관시킨다는 의미이다.
- 백엔드에 접속하는 개인에 따라 맞춤형 정보를 저장할 수 있기 때문에 개인화 기능 구현에 활용된다.
- 저장 위치가 브라우저이기 때문에 프론트엔드에서도 쿠키를 저장하거나 읽어올수 있는 API가 존재한다. (같은 값을 공유 가능)
- 하지만 보안에는 취약하다는게 단점이다.
- 따라서 민감한 정보는 기록해서는 안된다.
- 백엔드와 프론트엔드가 변수값을 공유할 수 있는 가장 원시적인 방법이다.
2) Cookie의 특성
- 일반 변수는 프론트엔드가 페이지를 이동하면 사라진다.
- 기본적으로 브라우저를 닫기 전 까지는 사이트 내의 모든 페이지가 공유하는 전역변수의 역할을 한다.
- 설정에 따라 일정 유효기간 동안은 브라우저가 재시작해도 변수값을 유지할 수 있다.
2. Cookie 사용
1) 패키지 설치
yarn add cookie-parser
- 데이터를 저장, 조회 할 때 암호화 처리를 해준다.
- 이 때 암호화에 사용되는 key 문자열은 개발자가 직접 정의해야 한다.
2) Cookie 저장에 필요한 조건
이름 | 설명 |
maxAge | 유효시간(ms단위). 설정하지 않을 경우 브라우저를 닫으면 삭제된다. |
domain | 유효 도메인 지정. 지정하지 않을 경우 현재 도메인 ex) 도메인이 naver.com 인 경우, naver.com으로 설정하면 해당 도메인 내의 모든 사이트가 쿠키를 공유할 수 있다. |
httpOnly | boolean (true/false). false인 경우 https에서도 식별 가능하다. |
path | 유효경로 (기본값 "/"). 특별한 경우가 아니라면 지정하지 않는다. |
signed | 암호화 여부. app.use(cookieParser('암호화키'); 형식으로 암호화 키 지정한다. |
3. Cookie값 확인 예제
HTML
<!DOCTYPE html>
<html lang="en">
<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>Document</title>
</head>
<body>
<h1>쿠키 테스트</h1>
<input type="text" id="user_input" placeholder="입력하세요" />
<button type="button" id="write">쿠키 저장하기</button>
<button type="button" id="read">쿠키 읽어오기</button>
<button type="button" id="delete">쿠키 삭제하기</button>
<h2 id="console"></h2>
<script src="//cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
const backendUrl = '/cookie';
/** 쿠키 저장하기 버튼 클릭 이벤트 */
document.getElementById('write').addEventListener('click', async (e) => {
const userInput = document.getElementById('user_input').value;
try {
// Ajax 요청 보내기 -> 백엔드가 전달한 결과값이 response.data에 저장된다.
// // backend가 node가 아닌 경우 post,put,delete 파라미터 전송 방법
// const form = new FormData();
// form.append('msg', userInput);
// const response = await axios.post(backendUrl, form);
// backend가 node인 경우 post, put, delete 파라미터 전송 방법
const response = await axios.post(backendUrl, { msg: userInput });
document.getElementById('console').innerHTML = response.data;
} catch (error) {
const errorMsg = '[' + error.response.status + '] ' + error.response.statusText;
alert(errorMsg);
}
});
/** 쿠키 읽어오기 버튼 클릭 이벤트 */
document.getElementById('read').addEventListener('click', async (e) => {
try {
// Ajax 요청 보내기 -> 백엔드가 전달한 결과값이 response.data에 저장된다.
const { data } = await axios.get(backendUrl);
const html = '<h2>일반값: ' + data.my_msg + '</h2><h2>암호화: ' + data.my_msg_signed + '</h2>';
document.getElementById('console').innerHTML = html;
} catch (error) {
const errorMsg = '[' + error.response.status + '] ' + error.response.statusText;
alert(errorMsg);
}
});
/** 쿠키 삭제하기 버튼 클릭 이벤트 */
document.getElementById('delete').addEventListener('click', async (e) => {
try {
const { data } = await axios.delete(backendUrl);
document.getElementById('console').innerHTML = data;
} catch (error) {
const errorMsg = '[' + error.response.status + '] ' + error.response.statusText;
alert(errorMsg);
}
});
</script>
</body>
</html>
Node.js
import express from 'express'; // express 호출
import bodyParser from 'body-parser'; // POST 파라미터 처리
import cookieParser from 'cookie-parser'; // Cookie 처리
import serveStatic from 'serve-static'; // 특정 폴더의 파일을 URL로 노출시킴
const app = express(); // express 사용
const port = 3001; // 포트 번호 지정
/** HTML, CSS, IMG, JS 등의 정적 파일을 URL에 노출시킬 폴더 연결 */
// "http://아이피(혹은 도메인):포트번호" 이후의 경로가 router에 등록되지 않은 경로라면 static 모듈에 연결된 폴더 안에서 해당 경로를 탐색한다.
app.use('/', serveStatic('public'));
/** POST 파라미터 수신 모듈 설정, 추가되는 미들웨어들 중 가장 먼저 설정해야한다. */
// body-parser를 이용해 application/x-www-form-urlencoded 파싱
// extended: ture -> 지속적 사용
// extended: false -> 한번만 사용
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.text()); // TEXT형식의 파라미터 수신 가능
app.use(bodyParser.json()); // JSON형식의 파라미터 수신 가능
/** 쿠키를 처리할 수 있는 객체 연결 */
// cookie-parser는 데이터를 저장, 조회 할 때 암호화 처리를 동반한다.
// 이 때 암호화에 사용되는 key문자열을 개발자가 정해야 한다.
app.use(cookieParser('test123'));
app
.post('/cookie', (req, res, next) => {
// POST로 전달된 파라미터 받기
const msg = req.body.msg;
// 일반 쿠키 저장하기 -> 유효시간을 30초로 설정
res.cookie('my_msg', msg, {
maxAge: 30 * 1000,
path: '/',
});
// 암호화된 쿠키 저장하기 -> 유효시간을 30초로 설정
res.cookie('my_msg_signed', msg, {
maxAge: 30 * 1000,
path: '/',
signed: true
});
res.status(200).send('Success');
})
.get('/cookie', (req, res, next) => {
// 일반 쿠키값들은 req.cookie 객체의 하위 데이터로 저장된다. (일반 데이터)
const my_msg = req.cookies.my_msg;
const my_msg_signed = req.signedCookies.my_msg_signed;
const result_data = {
my_msg: my_msg,
my_msg_signed: my_msg_signed,
};
res.status(200).send(result_data);
})
.delete('/cookie', (req, res, next) => {
// 저장시 domain, path 를 설정했다면 삭제시에도 동일한 값을 지정해야 한다.
res.clearCookie('my_msg', { path: '/' });
res.clearCookie('my_msg_signed', { path: '/' });
res.status(200).send('clear');
});
app.listen(port, () => {
console.log('-----------------------------------');
console.log('| Start Express Server |');
console.log('-----------------------------------');
});
#2. Session
1. Session의 개념
1) Session이란?
- Cookie와 마찬가지로 사이트 내의 모든 페이지가 공유하는 전연변수이다.
- 단, 유효시간은 설정 할 수 없기 때문에 브라우저가 닫히거나 마지막 접속 이후 5분간 재접속이 없다면 자동 폐기된다.(시간 설정 가능)
- 특정 클라이언트에게 종속된 개인화 정보를 백엔드가 직접 저장 / 관리하는 형태이다.
- 많은 데이터를 보관 할 수 는 없지만, 쿠키보다 보안에 유리하기 때문에 로그인 정보 관리 등에 사용된다.
2) 패키지 설치
yarn add express-session
- secret: 암호화 키 입력
- resave: 세션이 초기화 되지 않더라도 새로 저장할지 여부 (일반적으로 false)
- saveUninitialized: 세션이 저장되기 전에 기존의 세션을 초기화 상태로 만들지 여부
2. 로그인 상태 관리하기
HTML
<!DOCTYPE html>
<html lang="en">
<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>Document</title>
</head>
<body>
<h1>Node Login Example</h1>
<hr />
<form method="post" action="/session/login" id="before-login" style="display: none">
<div>
<label for="user_id">아이디</label>
<input type="text" name="user_id" id="user_id" />
</div>
<div>
<label for="user_pw">비밀번호</label>
<input type="password" name="user_pw" id="user_pw" />
</div>
<hr />
<button type="submit">로그인</button>
</form>
<div id="after-login" style="display: none">
<h1>안녕하세요.</h1>
<a href="#" id="logout-link">로그아웃</a>
</div>
<script src="//cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
// 페이지 로드시 로그인 여부 검사
(async () => {
try {
const response = await axios.get('/session/login');
// 백엔드에서 전달된 결과가 로그인 성공을 의미하는 경우
document.getElementById('before-login').style.display = 'none';
document.getElementById('after-login').style.display = 'block';
} catch (error) {
// 실패할 경우
document.getElementById('before-login').style.display = 'block';
document.getElementById('after-login').style.display = 'none';
}
})();
document.getElementById('before-login').addEventListener('submit', async (e) => {
e.preventDefault();
const user_id = document.getElementById('user_id').value;
const user_pw = document.getElementById('user_pw').value;
try {
// Ajax 요청 보내기 -> 백엔드가 전달한 결과값이 response.data에 저장된다.
const response = await axios.post('/session/login', { userid: user_id, userpw: user_pw });
// 백엔드에서 전달된 결과가 로그인 성공을 의미하는 경우
document.getElementById('before-login').style.display = 'none';
document.getElementById('after-login').style.display = 'block';
} catch (error) {
const errorMsg = '[' + error.response.status + '] ' + error.response.statusText;
console.error(errorMsg);
alert('로그인에 실패했습니다. 아이디나 비밀번호를 확인하세요.');
}
});
document.getElementById('logout-link').addEventListener('click', (e) => {
e.preventDefault();
// 즉시실행 비동기 처리 함수
(async () => {
try {
// Ajax 요청 보내기 -> 백엔드가 전달한 결과값이 response.data에 저장된다.
const response = await axios.delete('/session/login');
// 백엔드에서 전달된 결과가 로그인 성공을 의미하는 경우
document.getElementById('before-login').style.display = 'block';
document.getElementById('after-login').style.display = 'none';
} catch (error) {
const errorMsg = '[' + error.response.status + '] ' + error.response.statusText;
console.error(errorMsg);
alert('로그아웃에 실패했습니다. 잠시 후 다시 시도해 주세요.');
}
})();
});
</script>
</body>
</html>
Node.js
import express from 'express'; // express 호출
import bodyParser from 'body-parser'; // POST 파라미터 처리
import expressSession from 'express-session' // Session 처리
import serveStatic from 'serve-static'; // 특정 폴더의 파일을 URL로 노출시킴
const app = express(); // express 사용
const port = 3001; // 포트 번호 지정
/** HTML, CSS, IMG, JS 등의 정적 파일을 URL에 노출시킬 폴더 연결 */
// "http://아이피(혹은 도메인):포트번호" 이후의 경로가 router에 등록되지 않은 경로라면 static 모듈에 연결된 폴더 안에서 해당 경로를 탐색한다.
app.use('/', serveStatic('public'));
/** POST 파라미터 수신 모듈 설정, 추가되는 미들웨어들 중 가장 먼저 설정해야한다. */
// body-parser를 이용해 application/x-www-form-urlencoded 파싱
// extended: ture -> 지속적 사용
// extended: false -> 한번만 사용
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.text()); // TEXT형식의 파라미터 수신 가능
app.use(bodyParser.json()); // JSON형식의 파라미터 수신 가능
/** 세션 설정 */
app.use(expressSession({
// 암호화 키
secret: 'test123',
// 세션이 초기화 되지 않더라도 새로 저장할지 여부 (일반적으로 false)
resave: false,
// 세션이 저장되기 전에 기존의 세션을 초기화 상태로 만들지 여부
saveUninitialized: false,
}));
app
.post('/session/login', (req, res, next) => {
// req로 요청 값을 대입한다.
const id = req.body.userid;
const pw = req.body.userpw;
console.log('id = ' + id);
console.log('pw = ' + pw);
let login_ok = false;
// 원래는 데이터베이스에서 값이 같은지를 확인해야한다.
if(id == 'node' && pw == '1234') {
console.log('로그인 성공');
// 값이 같다면 login_ok 변수를 true로 변환.
login_ok = true;
}
let result_code = null;
let result_msg = null;
if(login_ok) {
// 세션에 값을 대입한다.
req.session.userid = id;
req.session.userpw = pw;
result_code = 200;
result_msg = 'success';
} else {
result_code = 403;
result_msg = 'fail'
};
const json = { rt: result_msg };
res.status(result_code).send(json);
})
.delete('/session/login', async (req, res, next) => {
let result = 'success';
let code = 200;
try {
// 세션 삭제
await req.session.destroy();
} catch (e) {
logger.error(e.message);
result = e.message;
code = 500;
}
const json = { rt: result };
res.status(code).send(json);
})
.get('/session/login', (req, res, next) => {
const id = req.session.userid;
const pw = req.session.userpw;
let result_code = null;
let result_msg = null;
// id와 pw값이 있다면
if(id !== undefined && pw !== undefined) {
console.log('현재 로그인 상태입니다.');
result_code = 200;
result_msg = 'success';
} else {
console.log('현재 로그인 상태가 아닙니다.');
result_code = 400;
result_msg = 'fail';
}
const json = { rt: result_msg };
res.status(result_code).send(json);
});
app.listen(port, () => {
console.log('-----------------------------------');
console.log('| Start Express Server |');
console.log('-----------------------------------');
});
'국비수업 > Node.js' 카테고리의 다른 글
[Node.js/express] File Upload (0) | 2022.07.04 |
---|---|
[Node.js/express] 메일보내기 (0) | 2022.07.01 |
[Node.js] Express (0) | 2022.06.29 |
[Node.js] HTTP Server (0) | 2022.06.28 |
[Node.js] HTTP Client (0) | 2022.06.27 |