앱을 켤 때마다 로그인 토큰이랑 설정값을 읽어오는데, 스플래시 화면에서 홈으로 넘어가는 순간 잠깐 깜빡였다. 원인을 파보니 AsyncStorage 읽기가 비동기라 값이 오기 전에 화면이 먼저 그려지고 있었다.
AsyncStorage는 이름 그대로 전부 await를 해야 한다. 토큰 하나 읽는데도 Promise를 기다려야 하니까, 앱 초기화 흐름에 자꾸 비동기 구간이 끼어든다. 값이 몇 개 안 되는 간단한 키-값 저장인데도 그렇다.
MMKV로 교체
react-native-mmkv는 Tencent가 만든 MMKV를 React Native에 붙인 라이브러리다. 핵심은 읽기·쓰기가 동기(synchronous) 라는 것. await 없이 바로 값이 나온다. 내부적으로 JSI를 써서 브릿지를 안 거치기 때문에 AsyncStorage보다 훨씬 빠르다.
npx expo install react-native-mmkv
Expo면 dev client가 필요하다. 순수 JS 앱이 아니라 네이티브 모듈이라서 Expo Go에서는 안 돌아간다.
// lib/storage.ts
import { MMKV } from 'react-native-mmkv';
export const storage = new MMKV();
이게 끝이다. 이제 어디서든 동기로 읽고 쓴다.
storage.set('token', 'abc123');
storage.set('darkMode', true);
const token = storage.getString('token'); // await 없음
const dark = storage.getBoolean('darkMode');
getString, getNumber, getBoolean으로 타입별로 꺼낸다. 객체를 넣으려면 JSON.stringify해서 문자열로 저장하면 된다.
컴포넌트에서 바로 쓰기
동기라서 useState 초기값에 바로 넣을 수 있다. AsyncStorage로는 useEffect 안에서 읽어서 세팅해야 했던 걸, 한 줄로 줄인다.
import { useMMKVBoolean } from 'react-native-mmkv';
function Settings() {
// storage와 연결된 상태 훅
const [dark, setDark] = useMMKVBoolean('darkMode');
return <Switch value={!!dark} onValueChange={setDark} />;
}
useMMKVBoolean, useMMKVString 같은 훅을 쓰면 저장소 값이 바뀔 때 자동으로 리렌더된다. 별도 상태 관리 없이 저장과 UI가 같이 움직인다.
암호화도 옵션 하나
토큰 같은 민감한 값은 암호화해서 저장할 수 있다. 인스턴스 만들 때 encryptionKey만 주면 된다.
export const secureStorage = new MMKV({
id: 'secure',
encryptionKey: 'my-secret-key',
});
키 자체는 expo-secure-store나 Keychain에 넣어두고 꺼내 쓰는 게 안전하다.
정리하면, 간단한 키-값 저장이라면 AsyncStorage보다 MMKV가 낫다. 동기라서 초기 로딩 깜빡임이 사라지고, 코드도 await 지옥에서 벗어난다. 관계형 데이터가 필요한 게 아니라 토큰·설정 같은 값만 다룬다면 갈아탈 이유가 충분했다.