AsyncStorage는 React Native의 localStorage이고, MMKV는 그보다 30배 빠른 네이티브 저장소입니다.

앱을 종료해도 유지되어야 하는 데이터(토큰, 설정, 온보딩 여부 등)를 저장할 때 로컬 저장소가 필요합니다.


AsyncStorage

BASH
npm install @react-native-async-storage/async-storage
TSX
import AsyncStorage from '@react-native-async-storage/async-storage';

// 저장
await AsyncStorage.setItem('username', '홍길동');

// 객체 저장 (직렬화 필요)
await AsyncStorage.setItem('user', JSON.stringify({ id: 1, name: '홍길동' }));

// 읽기
const username = await AsyncStorage.getItem('username');
const user = JSON.parse(await AsyncStorage.getItem('user') ?? '{}');

// 삭제
await AsyncStorage.removeItem('username');

// 여러 항목 동시 처리
await AsyncStorage.multiSet([
  ['token', 'abc123'],
  ['refreshToken', 'xyz789'],
]);

const [[, token], [, refreshToken]] = await AsyncStorage.multiGet([
  'token',
  'refreshToken',
]);

// 전체 삭제
await AsyncStorage.clear();

커스텀 Hook

TSX
function useLocalStorage<T>(key: string, initialValue: T) {
  const [value, setValue] = useState<T>(initialValue);
  const [loaded, setLoaded] = useState(false);

  useEffect(() => {
    AsyncStorage.getItem(key).then((stored) => {
      if (stored !== null) setValue(JSON.parse(stored));
      setLoaded(true);
    });
  }, [key]);

  const setStoredValue = useCallback(async (newValue: T | ((prev: T) => T)) => {
    setValue((prev) => {
      const resolved = typeof newValue === 'function'
        ? (newValue as (prev: T) => T)(prev)
        : newValue;
      AsyncStorage.setItem(key, JSON.stringify(resolved));
      return resolved;
    });
  }, [key]);

  return [value, setStoredValue, loaded] as const;
}

// 사용
function SettingsScreen() {
  const [isDark, setIsDark, loaded] = useLocalStorage('darkMode', false);
  if (!loaded) return <ActivityIndicator />;
  return <Switch value={isDark} onValueChange={setIsDark} />;
}

MMKV

BASH
npm install react-native-mmkv
TSX
import { MMKV } from 'react-native-mmkv';

const storage = new MMKV();

// 동기적으로 동작 — await 불필요!
storage.set('username', '홍길동');
storage.set('count', 42);
storage.set('isLoggedIn', true);

// 읽기
const username = storage.getString('username');
const count = storage.getNumber('count');
const isLoggedIn = storage.getBoolean('isLoggedIn');

// 객체 저장
storage.set('user', JSON.stringify({ id: 1, name: '홍길동' }));
const user = JSON.parse(storage.getString('user') ?? '{}');

// 삭제
storage.delete('username');

// 전체 키 목록
const keys = storage.getAllKeys();

// 키 존재 여부
const exists = storage.contains('username');

// 전체 삭제
storage.clearAll();

AsyncStorage vs MMKV 비교

항목AsyncStorageMMKV
동작 방식비동기 (await 필요)** 동기** (즉시 반환)
속도느림~30배 빠름
데이터 타입문자열만문자열, 숫자, Boolean
암호화없음지원
Expo 지원OEAS Build 필요
설치 난이도낮음중간

성능 차이가 큽니다. AsyncStorage는 Bridge를 통해 비동기로 동작하지만, MMKV는 JSI를 통해 동기적으로 네이티브 메모리에 직접 접근합니다. 토큰 확인처럼 앱 시작 시 빠르게 읽어야 하는 데이터에는 MMKV가 유리합니다.


Zustand + MMKV Persist

TSX
import { create } from 'zustand';
import { persist, createJSONStorage, StateStorage } from 'zustand/middleware';
import { MMKV } from 'react-native-mmkv';

const mmkvStorage = new MMKV();

// MMKV를 Zustand storage로 사용
const zustandMMKVStorage: StateStorage = {
  setItem: (name, value) => mmkvStorage.set(name, value),
  getItem: (name) => mmkvStorage.getString(name) ?? null,
  removeItem: (name) => mmkvStorage.delete(name),
};

const useSettingsStore = create(
  persist(
    (set) => ({
      isDarkMode: false,
      language: 'ko',
      toggleDarkMode: () => set((s) => ({ isDarkMode: !s.isDarkMode })),
    }),
    {
      name: 'settings',
      storage: createJSONStorage(() => zustandMMKVStorage),
    }
  )
);

어떤 걸 선택해야 하나?

  • **Expo 프로젝트, 간단한 저장 **: AsyncStorage
  • ** 성능이 중요한 경우 **: MMKV
  • ** 암호화가 필요한 민감 데이터 **: MMKV (암호화 옵션)
  • ** 앱 시작 시 동기적 읽기 필요 **: MMKV

정리

  • AsyncStorage는 ** 비동기 **, MMKV는 ** 동기 **로 동작합니다
  • MMKV는 JSI 기반으로 ** 약 30배 빠른** 성능을 제공합니다
  • 상태관리 라이브러리의 persist 미들웨어 와 함께 사용하면 앱 재시작 후에도 상태가 유지됩니다
  • 보안이 필요한 데이터(토큰 등)는 MMKV 암호화 또는 react-native-keychain 을 사용하세요
댓글 로딩 중...