Frontend/상태관리(Redux, Storage, ContextAPI)

[React Native] Async-Storage / Encrypted-Storage 필요성과 사용법

0BigLife 2022. 4. 3. 13:43
728x90

개발은 밥심

 오늘은 스토리지에 대해서 다뤄볼까 합니다. 스토리지를 다루기 위해 두 가지 라이브러리를 정리해보려고 하는데, Async-storage와 Encrypted-storage가 바로 그 주인공입니다. 가장 먼저, 스토리지(Storage)를 왜 써야하는지, 어떤 데이터를 넣어야하며- 그렇다면 redux는 어떻게 활용 측면에서 비교가 되는지 정리를 하고 마지막엔 코드 예제까지 정리하면서 마무리 해보려고 합니다-!


 어제 작성한 글에서는 효율적인 상태 관리를 위해서 Redux-toolkit을 다뤄보았다. Redux의 store는 데이터 저장 공간으로 활용되며 실제로 앱이 켜져있는 상태에서 데이터를 불러오는 성능이 가장 뛰어나다. 하지만 앱이 꺼지면 데이터가 사라지는 일시적 저장 공간이기 때문에 그 한계 또한 명확하다. (컴퓨터로 치면 RAM 같은 존재..) 

Async Storage 한 줄 소개


 따라서 데이터를 유지해줄 공간이 필요하다. 앱이 꺼졌다 켜진 후에도 데이터 유지가 가능한 것이 바로 Async-storage이며, 웹으로 치면 로컬스토리지와 유사하다. 그렇다면 Encrypted-storage는 왜 필요할까? 그 이유는 Async-storage 공식 문서에서 찾아볼 수 있다. 공식 문서 한 줄 소개에서는 Async-storage를 'An asynchronous, unencrypted, persistent, key-value storage system for RN'이라고 정의되어 있다. 그대로 해석하면, '비동기적 - 암호화되지 않으며 - '키-값'으로 저장되는 시스템' 이라고 한다. 데이터가 암호화되지 않는 스토리지이기 때문에 누구든지 값을 열어볼 수 있어 귀중한 토큰을 보관할 시에는 적합하지 않다. 

Encrypted Storage 한 줄 소개


 이를 보안한 것이 바로 Encrypted-storage이다. '비동기적 - '키-값'으로 저장'은 동일하며 실제로 구현 방식도 async-storage와 동일하다. 공식 문서의 한 줄 소개만 보아도 Async storage의 약점인 보안을 강화시키기 위한 라이브러리임을 알 수 있다. 따라서, 보안이 철저히 필요한 토큰들은 Async storage가 아닌 Encrypted storage에 넣어주면 된다. 

 정리하면 다음과 같다.

  • Redux(store) : 앱이 켜진 상태에서만 유지되는 데이터 저장
  • Async storage : 보안에 민감하지 않으며, 앱이 꺼져도 유지되는 데이터 저장
  • Encrypted storage : 보안이 필요하며, 앱이 꺼져도 유지되는 데이터 저장

 

코드 예제

이제 구현 방식에 대해 정리해보자. 참고로 설치 방법은 간단한 검색만으로도 가능하니 여기서는 생략한다. 공식 문서 링크 참고 ! (추가적으로 예전에 작성했던 async-storage 에 대한 글도 참고해볼 것..!)

Async Storage : https://github.com/react-native-async-storage/async-storage
Encrypted Storage : https://github.com/emeraldsanto/react-native-encrypted-storage

 

GitHub - react-native-async-storage/async-storage: An asynchronous, persistent, key-value storage system for React Native.

An asynchronous, persistent, key-value storage system for React Native. - GitHub - react-native-async-storage/async-storage: An asynchronous, persistent, key-value storage system for React Native.

github.com

 

GitHub - emeraldsanto/react-native-encrypted-storage: React Native wrapper around EncryptedSharedPreferences and Keychain to pro

React Native wrapper around EncryptedSharedPreferences and Keychain to provide a secure alternative to Async Storage. - GitHub - emeraldsanto/react-native-encrypted-storage: React Native wrapper ar...

github.com

 

 필자 기준 스토리지에 저장(setItem), 불러오기(getItem), 삭제(removeItem) 정도 알거나 아니면 추가적으로 필요한 메서드는 문서를 찾아서 쓰면 된다. 라이브러리에서 AsyncStorage를 가져와서 내부 함수들을 불러오는 방식으로 구현한다.

//스토리지에 저장하기 - setItem

const storeData = async (value) => {
  try {
    const jsonValue = JSON.stringify(value) //문자열로 변환하여 넣어주기 위함
    await AsyncStorage.setItem('key', jsonValue)
  } catch (e) {
    // saving error
  }
}

 

 반드시 기억해야할 점은, 키-값 형태로 보관됨에 있어 저장되는 값은 무조건 문자열로 취급되어야한다. 따라서, 저장할 때에는 JSON.stringfy() 를 써주고, 가져올 때에는 JSON.parse() 를 사용해서 구현해야 한다. 비동기 처리를 위하여 async/await과 에러 핸들링을 위해 try-catch 문을 써준다. key 값이 공백 또는 null 일 때에는 error를 throw 해주고, value 가 존재하지 않는다면 null을 반환하도록 한다. 
 

//스토리지에서 값 불러오기 - getItem

const getData = async () => {
  try {
    const jsonValue = await AsyncStorage.getItem('key')
    return jsonValue != null ? JSON.parse(jsonValue) : null;
  } catch(e) {
    // error reading value
  }
}


Encrypted-storage 역시 동일하다. AsyncStorage 대신 EncryptedStorage만 넣어주면 된다 !  

아래 코드는 로그인할 때에 반환되는 토큰들을 스토리지에 넣어주는 코드이다. 동일하게 비동기를 위한 async/await 과 에러 핸들링을 위한 try-catch문과 함께 짰으며, 이 경우 refreshToken을 보안을 위하여 Encrypted storage에 setItem 함수를 사용하여 넣어주었다. 

import EncryptedStorage from 'react-native-encrypted-storage';

const onSubmit = useCallback(async () => {
    if (loading) {
      return;
    }
    try {
      setLoading(true);
      const response = await axios.post(`${Config.API_URL}/login`, {
        email,
        password,
      });
      dispatch(
        userSlice.actions.setUser({
          name: response.data.name,
          email: response.data.email,
          accessToken: response.data.accessToken,
        }),
      );
      await EncryptedStorage.setItem(
        'refreshToken',
        response.data.refreshToken,
      );
    } catch (error) {
      const errorResponse = (error as AxiosError).response;
      if (errorResponse) {
        Alert.alert(errorResponse.data.message);
      }
    } finally {
      setLoading(false);
    }
  }, [dispatch, email, loading, password]);

 

하단 코드는 스토리지에서 토큰을 지우기 위하여 removeItem 함수를 사용한 예제이다. 로그아웃한 사용자는 저장공간에 토큰이 존재하면 안되기 때문에 제거해주는 것이다. 

  import EncryptedStorage from 'react-native-encrypted-storage';

  const onLogout = useCallback(async () => {
    try {
      await axios.post(
        `${Config.API_URL}/logout`,
        {},
        {
          headers: {
            authorization: `Bearer ${accessToken}`,
          },
        },
      );
      Alert.alert('알림', '로그아웃 되었습니다.');
      dispatch(
        userSlice.actions.setUser({
          name: '',
          email: '',
          accessToken: '',
        }),
      );
      await EncryptedStorage.removeItem('refreshToken');
    } catch (error) {
      const errorResponse = (error as AxiosError).response;
      console.error(errorResponse);
    }
  }, [accessToken, dispatch]);
728x90