이번 포스팅에서는 세션(session)의 개념에 대해 알아보고 세션을 활용해 간단한 로그인 기능을 구현해보도록 하겠다.
< 목차 >
- 세션이란?
- 로그인 기능 구현해보기
< 세션이란? >
이전 포스팅에서 쿠키(cookie)란 어떤 것인지에 대해 살펴본 적이 있다. 웹브라우저에서 서버에 요청을 보냈을 때 웹서버는 브라우저에 쿠키 데이터를 저장하고 이후의 요청들에 대한 응답에 쿠키를 사용하게 된다. 하지만 이렇게 쿠키를 브라우저에 저장해 놓고 사용하게 되면 보안과 관련해서 여러 이슈들이 발생한다. 쿠키에 개인정보와 같은 데이터를 보관하게 되면, 민감한 정보들이 공개되는 상황이 발생하게 되는 것이다. 이러한 이슈가 발생하는 원인은 데이터의 저장 주체가 브라우저이기 때문이다. 이를 방지하고자 데이터를 저장하는 주체를 클라이언트가 아닌 서버로 하는 것이 바로 세션(session)의 개념이다.
세션에서 사용되는 방식은 웹서버가 브라우저에게 쿠키 데이터를 줄 때 데이터 자체가 아닌 일종의 아이디(id) 혹은 키(key)와 같은 값을 브라우저에게 주는 것이다. 그리고 데이터 자체는 브라우저가 아닌 서버쪽에 저장해 놓는다. 브라우저의 쿠키에는 데이터가 아닌 키값이 저장되어 있고 서버에 요청을 보낼 때 키값을 같이 보냄으로써 서버에 저장된 데이터를 조회할 수 있도록 하는 것이다.
이러한 세션 방식에도 단점은 존재한다. 모든 데이터를 서버쪽에서 관리하고 있기 때문에 많은 사람이 로그인 하고있는 상황이라면 서버컴퓨터에 부하가 온게 된다. 그럼에도 세션을 사용하는 이유는 보안상의 이슈가 가장 크다고 볼 수 있을 것이다. 데이터는 서버에 저장되어 있고 브라우저에 저장된 키값을 이용해 서버쪽 데이터를 조회하는 방식이기 때문에 높은 보안성을 갖는다.
< 로그인 기능 구현해보기 >
이제 세션을 이용해서 간단한 로그인 기능을 구현해보자. 데이터베이스를 사용하고 있지 않기 때문에 다음과 같은 user.js 파일 안에서 사용자 정보들을 저장하고 관리하고 있다고 가정하자.
let user = [
{
userid:'admin',
userpw:'admin',
username:'관리자'
},
{
userid:'web7722',
userpw:'1234',
username:'곽인구'
},
{
userid:'test',
userpw:'test',
username:'테스트용'
}
]
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;
}
module.exports = {
user,
findUser
}
user 배열에 사용자의 정보들을 객체 형태로 담았고 사용자가 로그인할 때 입력한 정보와 서버쪽에 저장되어 있는 정보가 일치할 경우 true값을 return하는 findUser 함수를 정의했다. 또한 서버가 구동되는 javascript 파일에서 사용자 정보를 가져다 사용할 수 있도록 module.exports 로 user 배열과 findUser 함수를 내보냈다.
아래와 같은 형식으로 메인페이지와 로그인 페이지를 간단하게 만들었다.
<!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>
</ul>
</body>
</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" value="로그인">
</form>
</body>
</html>
로그인 페이지에서는 <form>엘리먼트와 <input>엘리먼트를 이용해 사용자가 입력한 아이디는 name="userid" , 패스워드는 name="userpw" 로 post 방식을 사용해 전송하게끔 하였다.
서버 파일은 아래와 같다.
const express = require('express')
const nunjucks = require('nunjucks')
const {user, findUser} = require('./user.js') // user 데이터
const app = express();
const session = {}
app.use(express.urlencoded({extended: true}))
app.set('view engine', 'html')
nunjucks.configure('./views', {
express: app,
watch: true
})
app.get('/', (req, res)=>{
if (req.headers.cookie) {
let data = req.headers.cookie
let sessionId = data.split('=')[1]
res.render('index_login.html', {
sessionId
})
} else {
res.render('index.html')
}
})
// user
app.get('/user/login', (req, res)=>{
let msg = req.query.msg
res.render('./user/login.html', {
msg
})
})
app.post('/user/login', (req, res)=>{
let {userid, userpw} = req.body; // post로 받은 값 (사용자가 입력한 값)
// user : 서버 관리자가 가지고 있는 정보
// 1단계
// form에서 받은 값과 user 변수에 있는 내용이 일치하는지 확인
let loginFlag = findUser(userid, userpw)
if ( loginFlag ) {
// 성공일 때 세션을 생성하고 메인으로 돌려주기
const privateKey = parseInt(Math.random()*10000000000)
const item = user.filter(v=>v.userid === userid)
// console.log(item)
session[privateKey] = item[0]
// console.log(session)
res.setHeader('Set-Cookie', `connect.id=${privateKey}; path=/`)
res.redirect('/')
} else {
// 실패일 때 로그인 페이지로 msg랑 같이 던져주기
res.redirect('/user/login?msg=아이디와 패스워드가 일치하지 않습니다.')
}
})
app.get('/user/profile', (req, res)=>{
let data = req.headers.cookie;
let sessionId = data.split('=')[1];
let userData = session[sessionId]
console.log(userData)
if (sessionId) {
res.render('./user/profile.html',{
userData
})
}
})
app.listen(3000, ()=>{
console.log("server onload")
})
사용자가 입력한 로그인 아이디와 패스워드 내용을 가지고 "/user/login" 경로로 post 방식의 요청이 들어왔을 때 세션 방식으로 로그인 기능을 구현해보기 위해 다음과 같은 처리를 해주도록 하였다.
app.post('/user/login', (req, res)=>{
let {userid, userpw} = req.body; // post로 받은 값 (사용자가 입력한 값)
// user : 서버 관리자가 가지고 있는 정보
// 1단계
// form에서 받은 값과 user 변수에 있는 내용이 일치하는지 확인
let loginFlag = findUser(userid, userpw)
if ( loginFlag ) {
// 성공일 때 세션을 생성하고 메인으로 돌려주기
const privateKey = parseInt(Math.random()*10000000000)
const item = user.filter(v=>v.userid === userid)
session[privateKey] = item[0]
res.setHeader('Set-Cookie', `connect.id=${privateKey}; path=/`)
res.redirect('/')
} else {
// 실패일 때 로그인 페이지로 msg랑 같이 던져주기
res.redirect('/user/login?msg=아이디와 패스워드가 일치하지 않습니다.')
}
})
user.js 파일에서 가져온 findUser() 함수는 user 배열에 객체 형태로 저장되어 있는 사용자 정보와 사용자가 로그인 시 입력한 정보가 일치할 경우 true값을 리턴하게 되고 이를 loginFlag 변수에 담아 if문에 사용하였다.
parseInt( Math.random()*10000000000 )를 이용해 난수를 생성하였고 filter() 메소드를 사용해 user 배열에서 사용자가 입력한 정보와 일치하는 사용자 정보를 골라내었다.
골라낸 사용자 정보 객체를 value값으로 하고 난수가 담겨있는 privateKey 변수를 key값으로 해서 전역에 생성해 놓은 session 객체 안에 집어넣었다. session 객체 안에는 브라우저가 서버 데이터를 조회할 때 사용될 키값과 그에 해당하는 사용자 데이터가 저장되게 된다.
res.setHeader('Set-Cookie', `connect.id=${privateKey}; path=/`) 를 이용해 웹서버에서 브라우저에 응답을 할 때 session 객체 안에 저장된 키값을 전달해 주었다. 이 값은 세션 아이디로 브라우저에 저장되게 된다. 브라우저에 세션을 저장하고 난 후 res.redirect('/')를 사용해서 "/" 경로로 이동시켰다.
"/" 경로로 요청이 왔을 때는 다음과 같은 처리를 해주었다.
app.get('/', (req, res)=>{
if (req.headers.cookie) {
let data = req.headers.cookie
let sessionId = data.split('=')[1]
res.render('index_login.html', {
sessionId
})
} else {
res.render('index.html')
}
})
req.headers.cookie를 사용해 요청헤더에 세션이 존재할 경우(사용자가 로그인 되어 있을 경우) split() 메소드를 통해 난수 형태의 키값만을 가져와서 sessionId 변수에 담았다. 그리고 sessionId 변수와 함께 index_login.html 파일을 랜더해 주도록 하였다.
아래는 index_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>
</head>
<body>
<h1>
<a href="/">Logo</a>
</h1>
메인페이지
<ul>
<li><a href="/user/login">로그인</a></li>
{% if sessionId %}
<li><a href='/user/profile'>프로필 정보</a></li>
{% endif %}
</ul>
</body>
</html>
nunjucks 템플릿 엔진을 사용해 세션값이 있을 경우 프로필 정보를 조회할 수 있는 항목이 드러나도록 하였다.
이제 사용자가 프로필 정보 링크를 클릭하면 웹브라우저는 저장되어 있는 세션 아이디와 함께 '/user/profile' 경로로 웹서버에 요청을 보내게 된다. 요청헤더의 cookie 부분에 세션 아이디가 담겨져서 웹서버에게 전달되고 웹서버는 전달받은 세션 아이디를 사용해 서버쪽에 저장되어 있는 사용자 정보 데이터를 조회하게 된다. 세션 아이디에 따라 알맞은 사용자 정보가 조회될 수 있도록 다음과 같은 처리를 해주도록 하자.
app.get('/user/profile', (req, res)=>{
let data = req.headers.cookie;
let sessionId = data.split('=')[1];
let userData = session[sessionId]
console.log
if (sessionId) {
res.render('./user/profile.html',{
userData
})
}
})
req.headers.cookie 를 이용해 브라우저가 요청과 함께 전달한 세션 아이디를 가져온다. split() 메소드를 사용해 난수 형태의 키값만을 가져와서 session 객체에 저장되어 있는 사용자 정보과 키값이 일치하는 데이터를 조회한다. 세션 아이디를 사용해 조회한 사용자 정보 데이터를 userData라는 변수에 담아서 profile.html 파일과 함께 랜더해준다.
아래는 profile.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>
<ul>
<li>userid : {{userData.userid}}</li>
<li>userpw : {{userData.userpw}}</li>
<li>username : {{userData.username}}</li>
</ul>
</body>
</html>
프로필 정보 링크를 클릭했을 때 전달받은 세션 아이디와 매칭되는 사용자 정보가 화면에 나타나는 것을 확인할 수 있다.
'Node > Express' 카테고리의 다른 글
Node.js - express (9) express.Router() 사용하기 (0) | 2022.02.10 |
---|---|
Node.js - express (8) 라우터와 미들웨어 (router & middleware) (0) | 2022.02.10 |
Node.js - express (6) cookieParser 함수 만들기 (1) | 2022.02.07 |
Node.js - express (5) 쿠키(cookie) (0) | 2022.02.07 |
Node.js - express (4) 정적인 파일 처리하기 (0) | 2022.02.04 |