React Native 0.79 전환 중 Recoil을 Zustand로 교체한 과정
React Native 0.79 마이그레이션과 함께 Recoil 전역 상태를 Zustand 5 store로 옮기고 초기화 책임을 단순화한 과정을 정리합니다.
React Native 0.79 마이그레이션은 네이티브 빌드 설정만 바꾸는 작업이 아니었다. React 19와 주변 라이브러리 버전을 맞추는 과정에서 전역 상태 관리도 Recoil 0.7에서 Zustand 5로 교체했다. 초기 프로젝트부터 Zustand를 설계한 것이 아니라 기존 atom 사용처를 실제 store API로 옮긴 마이그레이션이었다.
Recoil atom을 상태와 동작이 있는 store로 바꿨다
기존 사용자 상태는 값과 기본값만 가진 atom이었다.
아래 코드 예시는 설명을 위해 식별자와 운영 설정값을 일반화했습니다.
export const accountState = atom<Account | null>({
key: "accountState",
default: null,
});
Zustand 전환 후에는 상태, 설정 동작, 초기화 동작을 한 인터페이스에 넣었다.
interface AccountState {
account: Account | null;
setAccount: (account: Account | null) => void;
resetAccount: () => void;
}
export const useAccountStore = create<AccountState>((set) => ({
account: null,
setAccount: (account) => set({ account }),
resetAccount: () => set({ account: null }),
}));
같은 패턴으로 사용자, 지점, 관리자, 결제, 로딩·기기 상태를 옮겼다. 단순 값을 읽는 컴포넌트는 selector를 사용하고 여러 동작이 필요한 흐름은 store에서 필요한 함수를 꺼냈다.
const organization = useOrganizationStore((state) => state.organization);
const { setAccount, resetAccount } = useAccountStore();
Provider 의존성을 제거하고 초기화 규칙을 명시했다
Recoil은 앱 루트의 RecoilRoot가 필요했다. Zustand 전환으로 루트 Provider를 제거했고, 화면 이동 시 사용하던 useResetRecoilState는 각 store의 reset... 동작으로 바꿨다.
지점 선택 화면을 다시 열 때 사용자·지점·부가 기능·현재 입장 상태를 초기화하는 코드가 대표적이다. reset 함수가 store에 이름으로 드러나면서 “어떤 상태를 언제 비우는지”를 호출부에서 읽기 쉬워졌다.
하나의 거대한 store로 합치지 않았다
이번 코드는 흔히 말하는 하나의 대형 slice store를 만들지 않았다. 사용자, 결제, 지점, 유틸리티처럼 기존 atom의 책임 경계를 유지한 채 각각 Zustand store로 옮겼다. 각 store가 맡는 상태와 초기화 규칙을 유지하면서 전환 범위를 관리했다.
마이그레이션의 장점은 다음 범위에서 확인된다.
- Recoil 의존성과
RecoilRoot제거 - atom별 읽기·쓰기 Hook을 Zustand store API로 통일
- 상태 초기화를 명시적인 reset 함수로 표현
- selector로 필요한 상태만 구독할 수 있는 기반 마련
이 변경은 React Native 0.79 Android 전환과 함께 진행됐고 이후 iOS 마이그레이션이 이어졌다. 상태 관리 전환의 성과는 Recoil 의존성 제거와 상태 소유권·초기화 규칙의 명확화에 있다.
상태 관리 라이브러리 교체에서 중요한 것은 이름이 아니라 기존 상태의 책임과 초기화 시점을 잃지 않는 것이었다. atom을 store로 바꾸는 동안 호출부의 읽기·쓰기·reset 규칙을 함께 옮겨야 안전한 마이그레이션이 된다.