Splash Screen을 활용한 자동 로그인 처리

MODAC·2023년 8월 11일
0

knockknock

목록 보기
5/9
post-thumbnail

로그인 화면에 있는 자동로그인을 어떤 방식으로 구현할 지 고민했다.


  1. 리프레시 토큰이 만료될 떄까지 이메일 정보를 저장하고 로그인 화면 진입 시 저장된 이메일을 할당시켜주는 방법
    -> 이 경우 자동 로그인보다 아이디 저장이 더 알맞는 워딩인 것 같다.

  2. 자동 로그인 체크 시 토큰과 이메일을 저장하고 스플래시 화면에서 조건에 일치하는 경우 자동 로그인을 시켜주는 방법
    -> 스플래시 화면을 통해 앱 아이덴티티를 보여주며 로그인 프로세스를 실행시키는 것이 더 효과적이라고 생각했다.

1. 디바이스에 토큰 데이터를 저장

1) AsyncStorage

암호화 되지 않은 비동기적인 데이터를 관리하는 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,
};

2) 로그인 시 응답받은 토큰을 메모리에 저장

자동 로그인이 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'}]});
      }
    }
  };
  
  // ...중략

3) 스플래시스크린을 통한 자동로그인 구현

자동 로그인이 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);
    };
  }, []);
  
  //...중략

간단한 방법이지만 보안이 중요시 되는 현업이나 실무에서는 더 효율적이거나? 보안에 충실한 저장소를 사용하여 자동로그인을 구현하지 않을까 하는 생각이 든다.

1개의 댓글

comment-user-thumbnail
2023년 8월 11일

이렇게 유용한 정보를 공유해주셔서 감사합니다.

답글 달기