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

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

작성일: 2023년 08월 11일0

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


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

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

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

    Notion Image

    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);
        };
      }, []);
      
      //...중략
    

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