쿠키에 이어 세션에 대해 알아보자.
세션은 무엇인가?
방문자의 요청에 따른 정보를 웹 서버가 세션 아이디 파일을 만들어 서버에 저장하는 것이다.
쿠키와 다른 점은 쿠키는 정보를 사용자의 브라우저에, 세션은 서버에 저장한다는 것.
쿠키와 세션은 각각의 장단점이 있기 때문에 보통은 이 둘을 적절하게 조합해서 사용한다.
세션이 작동하는 방식에 대해 간략하게 알아보자.
1. 사용자가 서버로 로그인을 요청
2. 요청에 담긴 id, pw 정보가 유효하면 세션이 서버의 메모리에 저장됨
3. 이 때 세션 식별키로 난수 SessionId를 생성 후, 이 SessionId를 쿠키에 담아 브라우저로 전송
4. 이후 브라우저는 모든 요청에 쿠키(SessionId)를 함께 전송한다.
5. 서버는 브라우저가 보낸 SessionId를 키로 서버 메모리에서 사용자의 session 정보를 확인
6. 유효하다면/유효하지 않다면, 각각에 대응하는 응답을 제공해준다.
오늘 해볼 것이 이 세션과 쿠키를 NodeJS로 구현해보는 것이다.
지난 번에 만들었던 게시판에 세션 기능을 추가해
로그인 된 사람만 게시판을 보고, 활동할 수 있도록하는 권한,
로그인 여부에 따라 다른 html이 렌더링되는 기능을 구현해보자.
1. Intro
지금부터는 갈수록 많아지는 코드를 좀 더 조작, 관리하기 쉽도록 파일을 나누고 export/import하는 식으로
데이터를 조직화 할 것이다.
/* server.js */
// set up
const express = require('express')
const app = express()
const nunjucks = require('nunjucks')
app.use(express.urlencoded({extended:true}))
app.set('view engine', 'html')
nunjucks.configure('views', {express:app})
// 메인 페이지
app.get('/', (req,res) => {
res.render('index.html')
}
// 서버 구동
app.listen(3000, () => {
console.log('서버 구동')
}
/* index.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>
<a href="/">logo</a>
</h1>
메인페이지
<ul>
<li><a href="/user/login">로그인</a></li>
<li><a href="/board/list">게시판</a></li>
</ul>
</body>
</html>
처음으로는 메인 페이지와 html을 만든 후, 여기에 더해 db 역할을 해줄 js파일을 하나 더 만들고,
거기에 유저 데이터를 넣어준 후, server.js 파일로 가져온다.
/* userdata.js */
let user = [
{
userid:'admin',
userpw:'admin',
username:'관리자'
},
{
userid:'2b',
userpw:'2b',
username:'2b'
},
{
userid:'test',
userpw:'test',
username:'테스트용'
}
]
module.exports = {
user:user
}
userdata.js를 작성했다면 다시 server.js로 돌아가 데이터를 import하면 된다.
/* server.js */
const {user} = require('/user')
2. 로그인 기능 구현
세션을 이용해 로그인 기능을 구현해보자.
총 2개의 라우터가 필요하다.
첫 번째는 로그인 form을 렌더링해줄 역할을 할 라우터, (get)
두 번째는 form에 정보를 입력 후 요청을 전송했을 때, 그 정보를 받아 상황에 맞게 응답을 줄 라우터이다. (post)
우선 비교적 간단한 get 라우터부터 작성하자.
/* server.js */
app.get('/user/login', (req, res) => {
let msg = req.query.msg // 이게 뭔지는 나중에 설명
res.render('./user/login.html', {
msg:msg })
})
html 파일 경로에 대해 한 번 짚고 넘어가자.
우리가 nunjucks를 세팅할 때, configure method의 첫 번째 인자값으로 'views' 라는 폴더 명을 정하고,
그 폴더 안에 html 파일들을 넣었다.
즉, 우리가 render의 첫 번째 인자값으로 주는 인자값은 사실 파일의 경로와 이름인데,
파일의 상대경로는 views 기준으로 잡혀왔고, 그래서 지금까지는 그냥 파일명만 써주어도 문제가 없었던 것이다.
이제부턴 html 파일들도 용도에 따라 분리해 위치를 지정할 것이므로 파일명 앞에 매우 빈번하게 경로가 붙을 것이다.
예를 들어 방금 쓴 login.html의 경로는 views 폴더 안의 user 폴더가 된다.
/* login.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>
{% if msg %}
<script type="text/javascript">
alert('{{msg}}')
</script>
{% endif %}
</head>
<body>
<h1>
<a href="/">logo</a>
</h1>
로그인 페이지
<form method="post" action="/user/login">
<input type='text' name="userid">
<input type='text' name="userpw">
<input type='submit' name="로그인">
</form>
</body>
</html>
{% %} 내부에 쓰여있는 것은 nunjucks의 조건문이다.
msg가 true라면 {% if msg %} ~ {% endif %} 안에 있는 코드를 실행한다.
{{ }}는 nunjucks에서 변수를 선언하는 방식으로,
조건을 만족하면 script element에서 msg라는 변수를 내용으로 알람창을 띄울 것이다.
nunjucks의 문법은 나중에 따로 다루도록하고 지금은 넘어간다.
이제 post 라우터를 작성해주는데,
이 안에는 사용자가 userid, userpw를 입력해 제출하는 경우,
그 데이터를 가지고 있는 사용자의 정보 (userid, userpw)와 대조해 id와 pw가 일치하면 로그인을,
그렇지 않으면 다시 시도하라는 메시지를 출력하고 돌려보내는 코드를 짜 넣어야한다.
일치하면 true를, 아니면 false를 반환하는 함수를 user.js에서 작성해보자.
(대조할 데이터가 그 파일에 있으므로)
/* user.js */
function findUser(id, pw) {
for(let i = 0; i<user.length; i++) {
if(user[i].userid === id && user[i].userpw === pw) {
return true
}
}
return false
}
배열(user) 내 객체를 하나씩 가져와서 사용자가 입력한 데이터와 비교한 후, 일치하는게 있다면 true,
모든 객체를 비교했음에도 일치하는 객체가 없었다면 false를 반환한다.
이 함수도 module.export 객체에 포함시켜 코드를 다시 써준다.
/* user.js */
module.exports = {
user:user,
findUser:findUser
}
server.js에서도 가져올 데이터를 담을 변수를 다시 설정해주어야한다.
/* server.js */
const {user, findUser} = require('/user')
이제 이렇게 가져온 함수를 라우터 안에서 호출해 사용할 것이다.
/* server.js */
app.post('/user/login', (req, res) => {
let userid = req.body.userid
let userpw = req.body.userpw
let useritem
let loginFlag = findUser(userid, userpw)
if (loginFlag == true) {
const privateKey = parseInt(Math.random() * 1000000000)
for(let i = 0; i <user.length; i++) {
if(user[i].userid === userid) {
useritem = {...user[i]}
}
}
session[privateKey] = {
userid:useritem.userid,
userpw:useritem.pw
}
console.log('session: ', session)
// 어떤 형태로 session이라는 변수가 존재하는지 확인해보고 넘어가자.
res.setHeader('set-Cookie', `connect.id=${privateKey}; path:/`)
res.redirect('/?msg=로그인 성공')
}
// 여기까지가 로그인에 성공할 경우 반응
else {
res.redirect('/user/login?msg=아이디나 패스워드가 일치하지 않습니다.')
// 로그인에 실패한 경우
}
사용자가 데이터를 전송하면 userid, userpw를 읽어 가지고 있는 데이터와 대조해 일치하는 데이터가 있는지 체크한다.
일치하는게 있다면 (loginFlag == true) 해당 데이터를 담은 객체를 가져와
useritem이라는 새로 선언한 지역 변수안에 넣어주고, 난수 생성 메소드로 privateKey를 생성한 후,
privateKey와 객체를 합쳐준다. (개인 키가 로그인한 유저에 할당된 것)
이 후, 이 데이터를 쿠키로 만들어 브라우저에 전송한다.
일치하는 데이터가 없다면 (loginFlag == false)
로그인 페이지로 리다이렉트 시킨 후, 메시지를 띄워준다.
이렇게 세션의 기본원리를 구현해보았는데,
express에는 이를 조금 더 편하게 할 수 있는 기능이
내장되어 있다. 몇몇 추가적인 패키지를 설치해 함께 사용한다면
지금까지 한 내용을 훨씬 빠르게 실행할 수 있을 것이다.
다음 포스트에서 그것들을 함께 다뤄보도록 하자.
'Nodejs > Server' 카테고리의 다른 글
비동기 통신 - AJAX #1 (0) | 2022.03.16 |
---|---|
Session #2 - 미들웨어 (0) | 2022.02.13 |
Server의 이해 - Cookie #2 (0) | 2022.02.08 |
Server의 이해 - Cookie #1 (0) | 2022.02.07 |
JavaScript의 활용 - 게시판 서버 만들기 #2 (0) | 2022.02.06 |