을 하면 원래 다 사라지는게 정상이다. 왜냐면 새로고침 === 새로운 vue앱 생성 이기 때문에 이전에 있던 vuex의 state도 새로 시작하는 것이 원래는 맞다. 하지만 30초 전에 로그인 한 정보가 새로고침 한다고 해서 다 날아가는 것은 정말 non sense이다. 그렇기에 이를 방지하고자 앞으로의 내용을 한 번 살펴보자.
사실 리액트 duckzil-pad를 작성하며 보안성때문에 토큰을 localstorage에 저장하면 매우 취약하다는 것을 알고있지만 서버와의 통신을 최소화한다는 의미와 프론트에서 해결한다는 의미이에서 한 번 작성해보겠다.
우선 http는 async => actions => 여기를 작업해야겠다 라는 생각이 들어야한다.
export default {
async login(context, payload) {
return context.dispatch('auth',{
...payload,
mode:'login'
})
},
async signup(context, payload) {
return context.dispatch('auth',{
...payload,
mode:'signup'
})
},
async auth(context, payload) {
const mode = payload.mode;
let url = '로그인 URL'
if(mode==='signup'){
url = '회원가입 URL'
}
const response = await fetch(url, {
method: "POST",
body:JSON.stringify({
email: payload.email,
password: payload.password,
returnSecureToken:true
})
});
const responseData = await response.json();
if(!response.ok){
const error = new Error(responseData.message || 'Failed to authenticate. Check ur login data')
throw error;
}
localStorage.setItem('token', responseData.idToken);
localStorage.setItem('userId', responseData.localId);
context.commit('setUser',{
token: responseData.idToken,
userId: responseData.localId,
tokenExpiration : responseData.expiresIn
})
},
autoLogin(context) {
const token = localStorage.getItem('token');
const userId = localStorage.getItem('userId');
if (token && userId) {
context.commit('setUser',{
token:token,
userId:userId,
tokenExpiration:null
})
}
},
logout(context){
context.commit('setUser',{
token : null,
userId : null,
tokenExpiration : null
})
}
}
우선 중복되는 코드들을 auth(){}
로 refactoring 하였다.
그 후 login, signup 메서드에 구조분해를 활용하여 mode만 이름을 부여하여 dispatch 하였다.
autoLogin
메서드에서는 로그인(auth(){}
)할 때 localStorage에 값들을 set 하였고 이 값들을 get하여 기존에 state(getter actually)에 값을 설정하도록 로직을 작성하였다.
<script>
export default {
...
created() {
this.$store.dispatch('autoLogin')
}
}
</script>
let timer;
export default {
async login(context, payload) {
return context.dispatch('auth',{
...payload,
mode:'login'
})
},
async signup(context, payload) {
return context.dispatch('auth',{
...payload,
mode:'signup'
})
},
async auth(context, payload) {
const mode = payload.mode;
let url = '로그인 URL'
if(mode==='signup'){
url = '회원가입 URL'
}
const response = await fetch(url, {
method: "POST",
body:JSON.stringify({
email: payload.email,
password: payload.password,
returnSecureToken:true
})
});
const responseData = await response.json();
if(!response.ok){
const error = new Error(responseData.message || 'Failed to authenticate. Check ur login data')
throw error;
}
const expiresIn = +responseData.expiresIn *1000
const expirationDate = new Date().getTime() + expiresIn;
localStorage.setItem('token', responseData.idToken);
localStorage.setItem('userId', responseData.localId);
localStorage.setItem('tokenExpiration', expirationDate);
timer = setTimeout(function () {
context.dispatch('logout')
}, expiresIn)
context.commit('setUser',{
token: responseData.idToken,
userId: responseData.localId,
})
},
autoLogin(context) {
const token = localStorage.getItem('token');
const userId = localStorage.getItem('userId');
const tokenExpiration = localStorage.getItem('tokenExpiration')
const expiresIn = +tokenExpiration - new Date().getTime();
if(expiresIn<0){
return
}
timer = setTimeout(function () {
context.dispatch('logout')
}, expiresIn);
if (token && userId) {
context.commit('setUser',{
token:token,
userId:userId,
})
}
},
logout(context){
localStorage.removeItem('token');
localStorage.removeItem('userId');
localStorage.removeItem('tokenExpiration');
clearTimeout(timer);
context.commit('setUser',{
token : null,
userId : null,
})
}
}
로그인할 때마다 생기는 토큰이 만료되는 미래 시각을 expirationDate로 저장하고
이를 위해 expiresIn를 추가하고 responseData.expiresIn에 1000을 곱하는 식으로 설정한다.
setTimeout를 추가한 뒤 만료까지의 시간을 밀리초로 나타낸 expiresIn을 입력하고 이 시간이 지나면 함수가 실행되도록 설정하는데 이때 함수는 dispatch('logout')으로 토큰이 만료되었을 때 logout을 디스패치하도록 설정한다.
또한 한 번에 하나의 타이머만 활성화될 수 있도록 전역 변수인 timer를 추가하고 timer = setTimeout으로 설정함으로써 본 파일 전역에서 timer 변수로 타이머를 삭제할 수 있다.
clearTimeout에서 timer를 입력하면 현재 진행 중인 타이머를 삭제할 수 있다.
만료까지의 시간이 얼마나 남았는지를 확인하고 이를 바탕으로 새 타이머를 설정하도록 해 보자.