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

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

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

이를 보안한 것이 바로 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]);
'Frontend > 상태관리(Redux, Storage, ContextAPI)' 카테고리의 다른 글
[React Native] Hook (1) - useMemo, useCallback (0) | 2022.04.04 |
---|---|
[React Native] Redux-toolkit 적용 (0) | 2022.04.02 |
[React Native] Context API with Hook (0) | 2022.02.21 |