로그인 화면에 있는 자동로그인을 어떤 방식으로 구현할 지 고민했다.
리프레시 토큰이 만료될 떄까지 이메일 정보를 저장하고 로그인 화면 진입 시 저장된 이메일을 할당시켜주는 방법
-> 이 경우 자동 로그인보다 아이디 저장이 더 알맞는 워딩인 것 같다.
자동 로그인 체크 시 토큰과 이메일을 저장하고 스플래시 화면에서 조건에 일치하는 경우 자동 로그인을 시켜주는 방법
-> 스플래시 화면을 통해 앱 아이덴티티를 보여주며 로그인 프로세스를 실행시키는 것이 더 효과적이라고 생각했다.
암호화 되지 않은 비동기적인 데이터를 관리하는 Key-Value 저장 시스템으로
디바이스 메모리에 데이터를 저장하는 오픈소스 라이브러리이다.
디바이스 메모리에 엑세스토큰과 리프레시토큰을 저장하고 이를 참조하여 자동로그인을 구현한다.
먼저 async storage에 토큰을 저장하고 참조할 수 있는 모듈 파일을 만들었다.
비동기적 데이터 관리와 앱 성능 향상을 위해 프로미스를 반환하는 비동기 함수로 asyncStorage 데이터 처리 함수를 만들었다.
//authUtil.ts
import AsyncStorage from '@react-native-async-storage/async-storage';
//! 비밀번호 유효성 검사
const isPasswordValid = (text: string) =>
/^(?=.*[a-zA-Z])(?=.*\d)(?=.*[!@#$%^&*()\-_=+{}[\]:;'"<>,.?/\\|]).{8,}$/.test(text);
//! 이메일 유효성 검사
const isEmaildValid = (text: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(text);
//! AsyncStorage 데이터 접근
const storageGetValue = async (key: string) => {
try {
const value = await AsyncStorage.getItem(key);
if (value !== null) {
return JSON.parse(value);
} else {
return null;
}
} catch (e: any) {
console.log(e.message);
return null;
}
};
//! AsyncStorage 데이터 저장
const storageSetValue = async (key: string, value: any) => {
try {
if (value !== null) {
await AsyncStorage.setItem(key, JSON.stringify(value));
} else {
return null;
}
} catch (e: any) {
console.log(e.message);
return null;
}
};
//! AsyncStorage 데이터 삭제
const storageDeleteValue = async (key: string) => {
try {
await AsyncStorage.removeItem(key);
} catch (e: any) {
console.log(e.message);
return null;
}
};
//! AsyncStorage 리셋
const storageResetValue = async () => {
try {
await AsyncStorage.clear();
} catch (e: any) {
console.log(e.message);
return null;
}
};
export {
isPasswordValid,
isEmaildValid,
storageGetValue,
storageSetValue,
storageDeleteValue,
storageResetValue,
};
자동 로그인이 true일 때 tokens 키에 accesstoken과 refreshtoken을 저장한다.
// ...중략
import axios from 'axios';
import {isPasswordValid, storageSetValue, storageDeleteValue} from '../../util/authUtil';
const Login: React.FC<AuthProps> = ({url, navigation}) => {
const [data, setData] = useState({email: '', password: ''});
const {password, email} = data;
const [autoLogin, setAutoLogin] = useState(false);
const loginAuth = async () => {
if (url !== undefined) {
try {
const response = await axios.post(`${url}api/v1/users/login`, data);
const {accessToken, refreshToken, userId} = response.data;
// 자동 로그인 정보 수집
if (autoLogin) {
await storageSetValue('tokens', {accessToken, refreshToken});
} else await storageDeleteValue('tokens');
//메인화면으로 이동
navigation.navigate('MainTab');
} catch (error: any) {
navigation.reset({routes: [{name: 'Login'}]});
}
}
};
// ...중략
자동 로그인이 true일 때 앱 재실행 시 메모리에 저장된 토큰을 참조하여 refreshToken을 리프레시 api로 요청보내고 새로운 엑세스토큰과 리프레시토큰,userId를 응답받는다.
//...중략
import {storageGetValue, storageSetValue} from '../../util/authUtil';
import axios from 'axios';
const AuthSplashScreen: React.FC<AuthProps> = props => {
//? 첫 실행 시 동작
const verifyTokens = async ({navigation, url}: any) => {
// 메모리에 저장된 토큰 호출
const tokens = await storageGetValue('tokens');
if (tokens) {
try {
const header = {headers: {Authorization: `Bearer ${tokens.accessToken}`}};
// 토큰 갱신 후 저장
const response = await axios.post(
`${url}api/v1/users/refreshToken`,
{refreshToken: tokens.refreshToken},
header,
);
const {accessToken, refreshToken, userId} = response.data;
await storageSetValue('tokens', {accessToken, refreshToken});
// 로그인 성공 후 메인탭 이동
navigation.navigate('MainTab');
} catch (error) {
console.log(error);
// 로그인 실패 -> 재로그인
navigation.reset({routes: [{name: 'Login'}]});
}
} else {
// 로그인 실패 -> 재로그인
navigation.reset({routes: [{name: 'Login'}]});
}
};
useEffect(() => {
const timer = setTimeout(() => {
verifyTokens(props);
}, 1000);
// 컴포넌트가 언마운트되면 타이머 정리
return () => {
clearTimeout(timer);
};
}, []);
//...중략
간단한 방법이지만 보안이 중요시 되는 현업이나 실무에서는 더 효율적이거나? 보안에 충실한 저장소를 사용하여 자동로그인을 구현하지 않을까 하는 생각이 든다.
이렇게 유용한 정보를 공유해주셔서 감사합니다.