React Native AsyncStorage 사용법
AsyncStorage 는 간단히 말해 "앱이 꺼져도 남는 key-value 저장소" 다. 브라우저의 localStorage 와 개념이 비슷하고, 실제 플랫폼 구현은 iOS 쪽은 파일/SQLite, Android 쪽은 SQLite/SharedPreferences 로 알려져 있다.
2020 년에 core 에서 빠지고 @react-native-async-storage/async-storage 로 community 패키지로 이관됐다. 예전 글 보고 react-native 직접 import 시도하다가 deprecated 경고 보는 게 요즘 가장 흔한 케이스. RN 0.60 이후 auto-linking 덕분에 설치만 하면 되지만, iOS 는 pod install 한 번 돌려줘야 한다.
왜 AsyncStorage 인가
컴포넌트 state 는 언마운트되면 사라지고, redux 도 앱 종료되면 날아간다. 로그인 토큰, 튜토리얼 완료 여부, 마지막 본 화면 같이 세션 간에 살아남아야 하는 작은 값에는 AsyncStorage 가 딱이다. 반대로 복잡한 관계형 데이터, 큰 리스트, 풀텍스트 검색이 필요하면 SQLite (react-native-sqlite-storage) 또는 MMKV (react-native-mmkv) 같은 대안을 써야 한다. 특히 MMKV 는 성능이 AsyncStorage 보다 훨씬 좋다는 벤치가 많아서 최근 마이그레이션 이야기가 자주 나옴.
설치
$ npm install @react-native-async-storage/async-storage
# 또는
$ yarn add @react-native-async-storage/async-storage
# iOS
$ cd ios && pod install
RN 0.64 기준. 0.60 이상이면 따로 react-native link 안 해도 auto-linking 이 됨.
기본 사용 — 저장 / 읽기
import AsyncStorage from '@react-native-async-storage/async-storage';
// 저장 — Promise 기반
const saveId = async (id) => {
try {
await AsyncStorage.setItem('userId', id);
} catch (e) {
console.warn('save failed', e);
}
};
// 읽기
const loadId = async () => {
try {
const id = await AsyncStorage.getItem('userId');
return id; // 없으면 null
} catch (e) {
return null;
}
};
핵심 포인트 두 가지:
1. 모든 API 가 비동기 (Promise) 다. callback 스타일도 지원하지만 요즘은 async/await 가 기본.
2. 저장 가능한 값은 문자열뿐. 숫자, 객체를 넣으려면 stringify 해야 한다.
JSON 저장
const saveUser = async (user) => {
await AsyncStorage.setItem('user', JSON.stringify(user));
};
const loadUser = async () => {
const raw = await AsyncStorage.getItem('user');
return raw ? JSON.parse(raw) : null;
};
이걸 한 프로젝트에서 여기저기 흩뿌려놓으면 key 네이밍이 뒤죽박죽 된다. 작은 wrapper 하나 만들어 두는 게 정신 건강에 좋다.
// storage.js
import AsyncStorage from '@react-native-async-storage/async-storage';
export const storage = {
async get(key, fallback = null) {
const raw = await AsyncStorage.getItem(key);
if (raw == null) return fallback;
try { return JSON.parse(raw); } catch { return raw; }
},
async set(key, value) {
const raw = typeof value === 'string' ? value : JSON.stringify(value);
await AsyncStorage.setItem(key, raw);
},
async remove(key) {
await AsyncStorage.removeItem(key);
},
};
여러 개 한번에 (multiGet / multiSet)
await AsyncStorage.multiSet([
['appVersion', '1.0.3'],
['lastLogin', String(Date.now())],
]);
const pairs = await AsyncStorage.multiGet(['appVersion', 'lastLogin']);
// [['appVersion', '1.0.3'], ['lastLogin', '1619...']]
루프 돌면서 setItem 반복 호출하지 말고 multiSet 으로 한번에 처리하면 훨씬 빠르다. 바쁘게 값 수십 개 저장할 일 있으면 체감 차이 크다.
머지 / 삭제 / 전체 초기화
// 기존 JSON 에 병합 (shallow merge)
await AsyncStorage.mergeItem('user', JSON.stringify({ lastSeen: Date.now() }));
// 단일 키 삭제
await AsyncStorage.removeItem('userId');
// 전부 날리기 — 로그아웃 처리에 자주 씀. 복구 불가.
await AsyncStorage.clear();
주의할 점
1. 암호화 안 됨. AsyncStorage 에 토큰을 그냥 평문으로 넣으면 root/jailbreak 된 디바이스에서는 들여다 볼 수 있다. 민감한 정보 (access token, refresh token, 결제정보 일부) 는 react-native-keychain 이나 expo-secure-store 쪽을 쓰자.
2. 용량 제한. Android 기본 빌드에서 AsyncStorage 전체 용량이 6MB 로 제한된다. 넉넉하게 쓰려면 앱 쪽에서 크기를 늘리는 설정을 해야 한다. 이걸 몰라서 "저장은 되는데 어느 순간부터 안 됨" 버그를 몇 시간 잡은 적이 있음.
// android/gradle.properties
AsyncStorage_db_size_in_MB=10
3. 성능. 매 프레임 setItem 을 호출하면 끊긴다. throttle / debounce 해서 일정 간격으로 저장하는 게 안전. 스크롤 위치 저장 같은 고빈도 업데이트는 특히 조심.
4. 앱 삭제 시 같이 지워짐. 앱 삭제 → 재설치 하면 데이터 날아간다. "앱 재설치해도 로그인 상태 유지" 하려면 Keychain (iOS) / AccountManager (Android) 같은 시스템 저장소가 필요.
참고 문서
- @react-native-async-storage/async-storage 공식
- RN 공식 deprecated 안내
- react-native-mmkv (빠른 대안)
- react-native-sqlite-storage
정리하면, AsyncStorage 는 "가볍고 비동기인 key-value 저장소" 로 딱 맞는 위치에만 쓰면 편하다. 민감 정보는 안 넣고, 큰 데이터는 안 넣고, 반복 쓰기는 throttle 건다. 이 세 가지 만 지키면 대부분 문제 없음.