Persistence
아톰(atom)을 영구적으로 저장하는 방법에 대해 알아봅니다. 아톰은 상태 관리 라이브러리에서 사용되는 기본 단위로, 애플리케이션의 상태를 나타냅니다. 아톰을 영구적으로 저장하면, 페이지를 새로 고침하거나 애플리케이션을 다시 시작해도 이전 상태를 유지할 수 있습니다.
아톰을 영구적으로 저장하려면, atom
함수에 persist
옵션을 추가해야 합니다. 이 옵션은 아톰의 상태를 로컬 스토리지나 세션 스토리지와 같은 브라우저 저장소에 저장하도록 설정합니다.
import { atom } from 'recoil';const counterState = atom({key: 'counterState',default: 0,persist: true,});
위 예제에서 counterState
아톰은 persist: true
옵션을 통해 영구적으로 저장됩니다. 이제 이 아톰의 상태는 브라우저의 로컬 스토리지에 저장되며, 페이지를 새로 고침해도 이전 상태가 유지됩니다.
또한, persist
옵션에 저장소를 직접 지정할 수도 있습니다. 예를 들어, 세션 스토리지를 사용하려면 다음과 같이 설정할 수 있습니다.
import { atom } from 'recoil';const counterState = atom({key: 'counterState',default: 0,persist: {type: 'session',},});
이렇게 하면 counterState
아톰의 상태는 세션 스토리지에 저장됩니다. 세션 스토리지는 브라우저를 닫을 때까지 데이터를 유지하지만, 브라우저를 닫으면 데이터가 삭제됩니다.
아톰을 영구적으로 저장하는 기능은 사용자 설정이나 폼 데이터와 같이 중요한 상태를 유지해야 할 때 매우 유용합니다. 이를 통해 사용자 경험을 향상시키고, 데이터 손실을 방지할 수 있습니다.
Jotai는 sessionStorage
, localStorage
, AsyncStorage
, 또는 URL 해시에 상태를 저장할 수 있는 utils 번들에 있는 atomWithStorage 함수를 제공합니다.
(참고: 이 가이드는 약간 오래되었고, 일부 수정이 필요합니다.)
여기에는 몇 가지 대체 구현도 있습니다:
localStorage를 활용한 간단한 패턴
const strAtom = atom(localStorage.getItem('myKey') ?? 'foo')const strAtomWithPersistence = atom((get) => get(strAtom),(get, set, newStr) => {set(strAtom, newStr)localStorage.setItem('myKey', newStr)},)
localStorage와 JSON 파싱을 활용한 헬퍼 함수
const atomWithLocalStorage = (key, initialValue) => {const getInitialValue = () => {const item = localStorage.getItem(key)if (item !== null) {return JSON.parse(item)}return initialValue}const baseAtom = atom(getInitialValue())const derivedAtom = atom((get) => get(baseAtom),(get, set, update) => {const nextValue =typeof update === 'function' ? update(get(baseAtom)) : updateset(baseAtom, nextValue)localStorage.setItem(key, JSON.stringify(nextValue))},)return derivedAtom}
(에러 처리가 추가되어야 합니다.)
AsyncStorage와 JSON 파싱을 사용한 헬퍼 함수
이 기능은 onMount가 필요 합니다.
const atomWithAsyncStorage = (key, initialValue) => {const baseAtom = atom(initialValue)baseAtom.onMount = (setValue) => {;(async () => {const item = await AsyncStorage.getItem(key)setValue(JSON.parse(item))})()}const derivedAtom = atom((get) => get(baseAtom),(get, set, update) => {const nextValue =typeof update === 'function' ? update(get(baseAtom)) : updateset(baseAtom, nextValue)AsyncStorage.setItem(key, JSON.stringify(nextValue))},)return derivedAtom}
비동기 아톰을 사용하는 방법에 대한 자세한 내용은 Async 문서를 확인하세요.
sessionStorage 예제
AsyncStorage와 동일하게 atomWithStorage
유틸리티를 사용하고, 기본 스토리지를 sessionStorage
로 재정의하면 됩니다.
import { atomWithStorage, createJSONStorage } from 'jotai/utils'const storage = createJSONStorage(() => sessionStorage)const someAtom = atomWithStorage('some-key', someInitialValue, storage)
직렬화 아톰 패턴
type Actions =| { type: 'serialize'; callback: (value: string) => void }| { type: 'deserialize'; value: string }const serializeAtom = atom(null, (get, set, action: Actions) => {if (action.type === 'serialize') {const obj = {todos: get(todosAtom).map(get),}action.callback(JSON.stringify(obj))} else if (action.type === 'deserialize') {const obj = JSON.parse(action.value)// 에러 처리와 타입 검사가 필요함set(todosAtom,obj.todos.map((todo: Todo) => atom(todo)),)}})const Persist = () => {const [, dispatch] = useAtom(serializeAtom)const save = () => {dispatch({type: 'serialize',callback: (value) => {localStorage.setItem('serializedTodos', value)},})}const load = () => {const value = localStorage.getItem('serializedTodos')if (value) {dispatch({ type: 'deserialize', value })}}return (<div><button onClick={save}>localStorage에 저장</button><button onClick={load}>localStorage에서 불러오기</button></div>)}
예제
atomFamily를 사용한 패턴
type Actions =| { type: 'serialize'; callback: (value: string) => void }| { type: 'deserialize'; value: string }const serializeAtom = atom(null, (get, set, action: Actions) => {if (action.type === 'serialize') {const todos = get(todosAtom)const todoMap: Record<string, { title: string; completed: boolean }> = {}todos.forEach((id) => {todoMap[id] = get(todoAtomFamily({ id }))})const obj = {todos,todoMap,filter: get(filterAtom),}action.callback(JSON.stringify(obj))} else if (action.type === 'deserialize') {const obj = JSON.parse(action.value)// 에러 처리와 타입 검사가 필요함set(filterAtom, obj.filter)obj.todos.forEach((id: string) => {const todo = obj.todoMap[id]set(todoAtomFamily({ id, ...todo }), todo)})set(todosAtom, obj.todos)}})const Persist = () => {const [, dispatch] = useAtom(serializeAtom)const save = () => {dispatch({type: 'serialize',callback: (value) => {localStorage.setItem('serializedTodos', value)},})}const load = () => {const value = localStorage.getItem('serializedTodos')if (value) {dispatch({ type: 'deserialize', value })}}return (<div><button onClick={save}>Save to localStorage</button><button onClick={load}>Load from localStorage</button></div>)}