저번 시간에는 카카오 로그인 API를 활용해서 카카오 계정을 이용해 가입하는 것을 만들어 보았다. 이번 시간에는 카카오 계정 뿐만 아니라 로컬 로그인 로그아웃도 만들어 보도록 하겠다.
들어가기에 앞서
말 그대로 구조를 무시하고 쓸 수 있게 한 것이라고 보면 된다.
비구조 할당문에 대해 배워보자. 비구조 할당문이란 구조분해할당 (Destructuring assignment)라고도 불린다. 사실 '구조분해'할당문이 더 이해가 쉬운 표현이다. 기존의 '구조'를 분해해서 '변수'로 바로 쓸 수 있게 하는 것이기 때문이다.
let arr = [1,2,3,4,5,6,7,8,9];
let [a,b,c, ...last]=arr;
console.log(a);//1
console.log(b);//2
console.log(c);//3
console.log(last);//[4,5,6,7,8,9]
위 소스에서 보면 두번째 코드가 이에 해당한다.
let [a,b,c, ...last]=arr;은
a = arr[0];
b = arr[1];
c = arr[2];
last = arr.slice[3,];과 같은 의미이다.
하지만, 참조가 아니라 깊은 복사이다.
그래서, 이 코드 후에 arr 배열 값이 변하더라도
a,b,c,last의 값은 변하지 않는다.
let arr2 = [1,2,3];
let copy = arr2;
let copy2 = [...arr2];
arr2[0]='penguin';
console.log(copy); // [ 'penguin', 2, 3 ]
console.log(copy2); //[ 1, 2, 3 ]
위의 copy는 얕은 복사로 해당 영역의 메모리를 참조하는 변수이다.
반면, copy2는 깊은 복사로 선언 당시의 값을 복사한다. 후에 arr2 배열 값이 변화가 있더라도 copy2는 변하지 않는다.
구조분할 할당문은 객체에서도 쓰일 수 있다.
let obj = {a:10, b:20, c:30, d:40};
let = {a:a2, ...last2} = obj;
let {a:name, b:age, c:key, d:weight} = obj;
console.log(name);//10
console.log(age);//20
console.log(key);//30
console.group(weight);//40
console.log(a2);//10
console.log(last2);//{ b: 20, c: 30, d: 40 }
여기서 구조분할 할당문의 진가가 드러난다.
console.log(obj.name);
console.log(name);
의 값이 같다. 즉, 보기도 간편하고 코드의 길이도 짧아지는 것이다.
앞으로 나올 코드지만, 이해하기에는 더 쉬울 것 같아 소개한다.
const {session} = req;
이 코드는 req.session의 값을 session이라는 변수에 저장한다는 뜻이다. 굳이
const session = req.session;
이렇게 쓸 필요가 없어진 것이다.
전에 했던 부분에서 아래 부분을 추가해준다.
#index.html
<a href="/auth/kakao/unlink">카카오 탈퇴</a>
#server.js
app.get('/auth/kakao/unlink',authMiddleware,async(req,res)=>{
const {session} =req;//req.session을 session에 담는다.
const{access_token}=session.authData.kakao;
let unlink;
try{
unlink = await axios({
method: "post",
url:"https://kapi.kakao.com/v1/user/unlink",
headers:{
Authorization:`Bearer ${access_token}`
}
})
}catch(e) {res.json(e.data);}
//이 값이 떨어진 이유는 이미 카카오측에서는 우리 아이디를 로그아웃& 회원탈퇴 완료 시킨거임.
//카카오측은 처리가 완료된 상태이고 우리도 세션을 지워줘야함
const {id} = unlink.data;//unlink.data.id의 값을 담아서 사용할거임
//요청이 필요...우리만으로 카카오 데이터를 삭제할 수 없기에
//카카오에 요청해야함.그리고 응답을 받아야 함.
//axios 사용해야
if(session.authData["kakao"].id == id){
delete session.authData;
}
res.redirect('/?msg=success logout');
})
위의 authMiddleware의 로그인이 되어있는지 되어있지 않은지 확인하기 위해 만들었다. 그 원리는 잠시 후에 설명할 것이다.
const {session} =req;
const{access_token}=session.authData.kakao;
.......중략.......
const {id} = unlink.data;
const session =req.session;과 같은 맥락이다.
코드 해석을 쉽게하기 위해 가독성을 좋게 하기위한 목적으로 쓰였다고 보면 된다. 만약 이렇게하지 않으면 이 코드에서 req.session.###가 넘쳐나서 쉽게 해석하기 어려웠을 것이다.
axios는 promise API를 활용하는 HTTP 비동기 통신
axios는 fetch와 같은 기능으로 비동기 통신을 담당한다. 다른 사이트의 정보를 가져올 수도 있고 보낼 수도 있다.(단, 가져올 때는 다른 사이트가 cors를 해놔야 가능) 페이지가 바뀌지 않고 바로 요청을 보내고 받을 수 있게끔 하는 것으로 많은 개발자들의 수고를 덜어준 고마운 library이다.
try{
unlink = await axios({
method: "post",
url:"https://kapi.kakao.com/v1/user/unlink",
headers:{
Authorization:`Bearer ${access_token}`
}
})
}catch(e) {res.json(e.data);}
위 소스는 카카오에 회원이 우리 사이트에서 회원탈퇴를 한다는 요청을 보내주는 것이다.
headers:{Authorization:`Bearer ${access_token}`}
위 코드는 headers의 항목에 Authorization이라는 항목을 만들고 거기에 Bearer ${access_token}
값을 넣어 보낸다는 것이다. 아무래도 get으로 보내다보니 보안상 accss_token의 값을 숨기려고 이러한 형식을 쓰게한 것 같다.
if(session.authData["kakao"].id == id){
delete session.authData;
}
session에 저장된 kakao의 id의 값이 비구조할당문에서 설정했던 id와 같다면 세션을 지운다. 세션을 지움으로써 로그아웃이 된다.
app.get('/auth/logout',(req,res)=>{
delete req.session.authData;
res.redirect('/?msg=logoutsuccess');
세션에 저장된 로그인 정보를 삭제하고, 다시 index.html로 돌아가는데 'logoutsuccess'라는 메세지창이 뜨게한다.
delete req.session.authData;
{% if logininfo ==undefined %}
<!--로그인 처리 안됨-->
<a href="/auth/kako">카카오 로그인</a>
<a href="/login">로그인</a>
{% else %}
<!--로그인 처리 완료-->
<a href="/auth/info">회원정보</a>
<a href="/auth/kakao/unlink">카카오 탈퇴</a>
<a href="/auth/logout">로그아웃</a>
{% endif %}
{% if msg %}
<script type = "text/javascript">
alert("{{msg}}");
</script>
{% endif %}
/******************설정******************************/
const express = require('express');
const app = express();
const nunjucjks = require('nunjucks');
const axios = require('axios');
const qs = require('qs');
const session = require('express-session');
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:false,}));
//console.log('test');
app.use(session({
secret:'adasda',
resave:false,
secure:false,
saveUninitialized:false,
}))
const REST_API = "c60cf11573ac60fd10341b9770620769";
const secret_key = "p9HOwz9WPxIy0QGtNw13ovGZ5VsRo4VZ";
const redirect_uri = "http://localhost:3000/auth/kakao/callback"; // 여기서 지정한 포트 번호가 아니면 모두 에러
app.set('view engine', 'html');
nunjucjks.configure('views', {
express:app,
})
const kakao = {
clientID : REST_API,
clientSecret : secret_key,
redirectUri : redirect_uri,
}
/*******************메인 페이지*************/
app.get('/', (req, res) => {
const {msg} = req.query;
res.render('index.html',{
msg,
logininfo:req.session.authData,
});
})
/***********카카오 로그인 페이지***************/
app.get('/auth/kako', (req, res)=>{
const kakaoAuthURL = `https://kauth.kakao.com/oauth/authorize?client_id=${kakao.clientID}&redirect_uri=${kakao.redirectUri}&response_type=code&scope=profile,account_email`;
res.redirect(kakaoAuthURL);
})
app.get('/auth/kakao/callback', async (req, res)=>{
//axios => Promise Object
const {session,query} =req;//객체 구조분해>기본변수 할당
const {code} = query;//req.query.code =>code
//userid와 userpw 값을 가지고 DB조회하는게 맞음.
//userid : root userpw:root 간단하게 작동만 되게 만든거임.
try{
token = await axios({
method: 'POST',
url: 'https://kauth.kakao.com/oauth/token',
headers:{
'content-type':'application/x-www-form-urlencoded'
},
data:qs.stringify({// 객체를 String으로 변환.
grant_type:'aut