MMKV는 현재 React Native 생태계에서 로컬 데이터 저장소(Local Storage)의 사실상 표준(De-facto Standard)이다.
과거의 AsyncStorage를 완전히 대체하는 라이브러리로, 빠른 속도와 동기(Synchronous) 처리가 핵심이다. WeChat(Tencent)이 개발한 네이티브 라이브러리를 Marc Rousavy가 React Native용으로 포팅(react-native-mmkv)하여 개발했다.
MMKV는 AsyncStorage보다 50배 정도 더 빠르다. 그 이유는 두 가지 핵심 기술 때문이다.
MMKV는 JSI를 기반으로 동작하므로 RN 0.76+ 이상의 최신버전에선 New Architecture와 완벽하게 호환된다. 오히려 New Architecture가 기본 활성화되면서 MMKV의 성능 이점이 더욱 부각되고 있다. 구 아키텍처의 Bridge 방식보다 훨씬 빠르다.
기존 AsyncStorage의 가장 큰 단점은 모든 게 Promise(await)였다는 점이다.
// AsyncStorage (구형) - 비동기라 await 필수, 깜빡임 발생 가능
const loadUser = async () => {
const user = await AsyncStorage.getItem('user');
setUser(user);
};
하지만 MMKV는 동기식이다. 일반 변수처럼 바로 값을 가져온다.
// MMKV (신형) - 동기식이라 앱 켜자마자 데이터가 있음
const user = storage.getString('user');
// 바로 사용 가능 (로그인 상태 유지 등에 필수)
if (user) navigate('Home');
이 덕분에 앱 실행 시 로딩 스피너를 보여줄 필요 없이 즉시 저장된 상태를 복원할 수 있다. Zustand, Jotai 같은 상태 관리 라이브러리와 궁합이 좋다.
1) CRUD
import { MMKV } from 'react-native-mmkv';
// 1. 인스턴스 생성 (앱 전체에서 공유됨)
export const storage = new MMKV();
// 2. 저장 (Set) - 문자열, 숫자, 불리언 지원
storage.set('user.name', 'Marc');
storage.set('user.age', 21);
storage.set('is-dark-mode', true);
// 3. 읽기 (Get)
const username = storage.getString('user.name'); // 'Marc' or undefined
const age = storage.getNumber('user.age'); // 21
const isDark = storage.getBoolean('is-dark-mode'); // true
// 4. 삭제 (Delete)
storage.delete('user.name');
// 5. 전체 삭제
storage.clearAll();
2) Object
MMKV는 기본적으로 원시 타입(String, Number, Boolean)만 지원한다. 객체를 저장하려면 JSON.stringify를 써야 한다.
const user = { id: 1, role: 'admin' };
storage.set('user', JSON.stringify(user));
const savedUser = JSON.parse(storage.getString('user'));
3) 암호화
보안이 중요한 데이터(토큰 등)를 저장할 때 사용한다. MMKV는 기본적으로 default 라는 이름의 저장소를 하나 만들어 둔다. 하지만 이렇게 ID를 따로 주면 별도의 공간을 따로 만들어서 관리하겠다는 뜻이다. 다른 라이브러리나 앱의 다른 부분에서 MMKV를 쓰더라도, 데이터가 섞이지 않고 격리된다.
const storage = new MMKV({
id: 'secure-storage',
encryptionKey: 'my-secret-key' // 이 키를 사용해 암호화를 수행한다. 만약 누군가 루팅된 폰에서 이 앱의 데이터 파일을 탈취하더라도, 내용은 알 수 없는 암호문으로 보여 데이터를 볼 수 없다.
});
3) 다중 인스턴스
데이터를 격리해서 저장하고 싶을 때 (예: 사용자별 데이터 분리), 별도의 저장소를 만들 수 있다.
const user1Storage = new MMKV({ id: 'user-1' });
const user2Storage = new MMKV({ id: 'user-2' });
4) 리스너
데이터가 변경되었을 때 이벤트를 받을 수 있다. (React 밖에서 상태 변화 감지 시 유용)
const listener = storage.addOnValueChangedListener((key) => {
console.log(`${key}가 변경되었습니다!`);
});
5) 리스너
import { useMMKVString } from 'react-native-mmkv';
function App() {
const [name, setName] = useMMKVString('user.name');
return <TextInput value={name} onChangeText={setName} />;
}
6) Zunstand 에서의 사용
import { create } from 'zustand';
import { createJSONStorage, persist } from 'zustand/middleware';
import { MMKV } from 'react-native-mmkv';
const storage = new MMKV();
// Zustand store를 MMKV에 자동으로 동기화
export const MMKV_ZUSTAND_STORAGE = {
setItem: (name, value) => {
return storage.set(name, value);
},
getItem: (name) => {
const value = storage.getString(name);
return value ?? null;
},
removeItem: (name) => {
return storage.delete(name);
},
};
const useStore = create(
persist(
(set) => ({
token: null,
setToken: (token) => set({ token }),
}),
{
name: 'auth-storage',
storage: createJSONStorage(() => MMKV_ZUSTAND_STORAGE),
}
)
);
용량 제한
동기 처리의 양면성