웹에서 개발할 당시에는 세션 또는 로컬에 토큰을 저장하여 로그인 상태를 유지하게 했습니다.
하지만 앱에서는 세션과 로컬을 활용할 수 없기 때문에 AstncStorage를 활용하여 디바이스에 정보를 저장하여야 합니다.
AsyncStorage 설치
$ npm install @react-native-community/async-storage
AsyncStorage에 데이터 저장하기
// Token.js
import AsyncStorage from '@react-native-async-storage/async-storage';
// AccessToken 저장
export const setAccessToken = async token => {
await AsyncStorage.setItem('accessToken', JSON.stringify(token));
};
// refreshToken 저장
export const setRefreshToken = async token => {
await AsyncStorage.setItem('refreshToken', JSON.stringify(token));
};
// 유저 정보 저장
export const setCurrentUser = async currentUser => {
await AsyncStorage.setItem('currentUser', JSON.stringify(currentUser));
};
AsyncStorage를 불러와 setItem 메서드로 디바이스에 정보를 저장합니다. 이 때 첫 번째 인자에 저장할 정보의 key, 두 번째 인자에 저장할 데이터를 작성합니다.
import axios from 'axios';
import {createSlice, createAsyncThunk} from '@reduxjs/toolkit';
import {Alert} from 'react-native';
import api from '../api';
import {
setAccessToken,
setRefreshToken,
} from '../Token';
const login = createAsyncThunk('login', async (data, {rejectWithValue}) => {
try {
const res = await axios.post(api.login(), data, {});
setAccessToken(res.data.accessToken);
setRefreshToken(res.data.refreshToken);
setCurrentUser(res.data.user);
return res.data;
} catch (err) {
Alert.alert('로그인 정보', '이메일과 비밀번호를 확인해주세요.');
return rejectWithValue(err.response.data);
}
});
const initialState = {
loginState: false,
...
};
export const AccountsSlice = createSlice({
name: 'accounts',
initialState,
reducers: {
...
},
extraReducers: {
[login.fulfilled]: (state, action) => {
state.loginState = true;
state.currentUser = action.payload.user;
},
...
}
});
로그인 후 발급받은 access 토큰과 refresh 토큰을 AsyncStorage에 저장합니다. 진행한 프로젝트에서는 회원가입 후 바로 로그인 처리를 했으므로 회원가입에서도 같은 작업을 해줍니다. 로그인 상태를 바꾼 뒤 유저 정보도 AsyncStorage와 store에 저장해줍니다.
AsyncStorage에서 저장된 데이터 불러오기
앱을 새로고침 했을 때, state에 저장된 정보는 모두 삭제되어 있으므로 Asyncstorage에 저장된 정보를 바탕으로 로그인 여부를 확인합니다.
// Token.js
import AsyncStorage from '@react-native-async-storage/async-storage';
export const getAccessToken = async () => {
const token = await AsyncStorage.getItem('accessToken');
return token.replace('"', '').replace('"', '');
};
export const getRefreshToken = async () => {
const token = await AsyncStorage.getItem('refreshToken');
return token.replace('"', '').replace('"', '');
};
// {"exp": 50, "rank": "Y2", "shoeSize": 290, "upto": 2, "wingspan": 210, ...}
export const getCurrentUser = async () => {
const info = await AsyncStorage.getItem('currentUser');
const currentUser = JSON.parse(info);
return currentUser;
};
다음과 같이 getItem메서드를 활용하여 'accessToken'과 'refreshToken' 그리고 'currentUser'에 저장된 토큰을 가져오는 함수를 작성합니다. 유저 정보는 객체형태이므로 저장된 문자열을 불러와 json로 변환해주어 사용합니다.
저는 앱을 실행할 때 유저정보를 다시 state에 붙여주기 위해 앱 실행 시 async storage에 저장된 유저 정보를 확인하여 로그인 처리를 하였습니다. 하지만 함수를 불러와 이 부분에서 웹에서 작업할 때와 약간 다른점이 있었습니다. AsyncStorage는 동기식이던 LocalStorage와 다르게 비동기 방식으로 동작하기 때문에 객체에 저장해서 사용하려면 undefined에러가 발생합니다.
// 제대로 동작하지 않는 코드 ,,,
useEffect(() => {
...
const user = getCurrentUser()
if (user) {
dispatch(fetchCurrentUser(res));
} else {
removeAccessToken();
removeRefreshToken();
removeCurrentUser();
}
}, [dispatch]);
따라서 다음과 같은 처리가 불가능하여 promise 객체와 .then을 활용하여 비동기 처리를 하였습니다.
// 앱이 실행되고 처음 호출되는 함수에 적용
useEffect(() => {
...
getCurrentUser().then(res => {
if (res) {
dispatch(fetchCurrentUser(res));
} else {
removeAccessToken();
removeRefreshToken();
removeCurrentUser();
}
});
}, [dispatch]);
이렇게 하면 앱을 실행했을 때 유저 정보가 존재하면 로그인 처리를 하고 state에 유저정보를 저장할 수 있습니다.
AsyncStorage 데이터 삭제하기
//Token.js
import AsyncStorage from '@react-native-async-storage/async-storage';
// AccessToken 삭제
export const removeAccessToken = () => {
AsyncStorage.removeItem('accessToken');
};
// RefreshToken 삭제
export const removeRefreshToken = () => {
AsyncStorage.removeItem('refreshToken');
};
// 유저 정보 삭제
export const removeCurrentUser = () => {
AsyncStorage.removeItem('currentUser');
};
로컬이나 세션과 마찬가지로 removeItem 메서드를 활용하여 삭제하고자하는 데이터의 key를 인자로 넣어주면 됩니다.
회원관련 에러나 로그아웃 시 해당 함수들을 불러와 AsyncStorage를 비웠습니다.
import axiosTemp from '../axios';
const logout = createAsyncThunk('logout', async (arg, {rejectWithValue}) => {
try {
const res = await axiosTemp.post(api.logout(), {}, await getConfig());
removeAccessToken();
removeRefreshToken();
removeCurrentUser();
return res.data;
} catch (err) {
removeAccessToken();
removeRefreshToken();
removeCurrentUser();
return rejectWithValue(err.response.data);
}
});
로그아웃 시도 시 모든 정보를 지웠습니다.
여기에 사용된 axiosTemp는 axios 인스턴스로 response시 access토큰 만료 여부를 판단하고 refresh토큰으로 재발급 요청을 보내는 친구입니다. 이와 관련해서는 axios interceptor를 공부하시면 됩니다. 이 글에서는 중요하지 않으니 패스하겠습니다.
'개발일기 > App' 카테고리의 다른 글
[React Native] path alias 설정하기 (0) | 2024.10.07 |
---|---|
앱스토어 등록 일지 03. RN 프로젝트 폴더 구조 정리 & 필수 라이브러리 설치 (2) | 2024.10.04 |
앱스토어 등록 일지 02. github 저장소에 프로젝트 푸시하기 (6) | 2024.10.02 |
앱스토어 등록 일지 01. 안드로이드 개발 환경 구축 & 안드로이드 스튜디오 연동하기 (React Native) (3) | 2024.09.30 |