로컬 스토리지(LocalStorage)에 내용을 저장하고, 전역 상태 관리 라이브러리(Redux)와 동기화 하는 방법
들어가며
- 리액트(React.js)에서 특정 변수를 로컬 스토리지(Local Storage)에 저장하고, 전역 상태 관리 라이브러리(Redux)와 동기화 시키는 방법을 정리해본다.
- 이렇게 함으로써, 사용자의 입력을 받아 특정 변수를 화면에 표시해줘야 할 경우 새로고침을 해도 동일한 내용이 표시되게 할 수 있다.

로컬 스토리지(Local Storage)
개념
- 브라우저에 데이터를 영구적으로 저장할 수 있는 방법
- 브라우저가 닫혀도 데이터가 유지되며, 도메인별로 구분되어 저장된다.
- 로컬 스토리지는 키-값(Key-Value) 형식으로 데이터를 저장하며, 데이터는 문자열 형식으로만 저장할 수 있다.
- 따라서 객체(Object)나 배열(Array) 같은 복잡한 데이터는 JSON으로 변환하여 저장하고, 가져올 때 다시 파싱(Parsing)해야 한다.
- JSON으로 변환하기 위해서는
JSON.stringfy(객체|배열)
메서드를 사용하고, JSON을 다시 객체/배열로 파싱하려면JSON.parse(JSON변수)
를 사용한다.
- JSON으로 변환하기 위해서는
- 따라서 객체(Object)나 배열(Array) 같은 복잡한 데이터는 JSON으로 변환하여 저장하고, 가져올 때 다시 파싱(Parsing)해야 한다.
- 저장된 데이터는 사용자가 명시적으로 삭제하거나, 브라우저의 캐시를 지우기 전까지 유지된다.
- 최대 저장 용량은 보통 5MB 정도이다.
- 문자열 형태로만 데이터를 저장한다. (객체 저장 시 JSON으로 변환)
사용 예제 : JSON 변환 & 파싱(Parsing)
객체
const user = { name: 'Peter', age: 2024, isMember: true }; // 객체 -> 문자열 형태로 변환 const userJson = JSON.stringfy(user); // 로컬 스토리지에 저장 localStorage.setItem('user', userJson); // 파싱 const storedUser = JSON.parse(localStorage.getItem('user'));
배열
const favoriteColors = ['red', 'blue', 'green']; // 객체 -> 문자열 형태로 변환 const favoriteColorsJson = JSON.stringfy(favoriteColors); // 로컬 스토리지에 저장 localStorage.setItem('colors', favoriteColorsJson); // 파싱 const storedColors = JSON.parse(localStorage.getItem('colors'));
객체 + 배열이 혼합된 형태
const settings = { theme: 'dark', shortcuts: ['Ctrl+S', 'Ctrl+C', 'Ctrl+V'], layout: { header: true, sidebar: false } }; // 객체+배열 -> 문자열 형태로 변환 const settingsJson = JSON.stringfy(settings); // 로컬 스토리지에 저장 localStorage.setItem('settings', settingsJson); // 파싱 const storedSettings = JSON.parse(localStorage.getItem('settings'));
주요 메서드
setItem(key, value)
: 로컬 스토리지에 데이터 저장하기getItem(key)
: 로컬 스토리지에서 데이터 가져오기removeItem(key)
: 특정 데이터를 삭제하기clear()
: 로컬 스토리지의 모든 데이터를 삭제하기length
: 로컬 스토리지에 저장된 데이터의 개수 반환
사용 예제 : 주요 메서드
데이터 저장
localStorage.setItem('username', 'peter'); const user = { name: 'peter', age: 2024 }; // 로컬 스토리지에 데이터 저장하기 localStorage.setItem('user', JSON.stringify(user));
데이터 가져오기
// 로컬 스토리지에서 데이터 가져오기 const username = localStorage.getItem('username'); console.log(username); // peter const storedUser = JSON.parse(localStorage.getItem('user')); console.log(storedUser); // { name: 'peter', age: 2024 }
데이터 삭제하기
// 특정 데이터 삭제하기 localStorage.removeItem('username'); // 전체 데이터 삭제하기 localStorage.clear();
(참고) 쿠키(Cookie) vs. 로컬 스토리지(Local Storage) vs. 세션(Session)
로컬 스토리지(Local Storage) | 쿠키(Cookie) | 세션(Session) | |
저장 위치 | 클라이언트 | 클라이언트 | 서버 |
저장 용량 | 5~10MB | 4KB | 서버 설정에 따라 다르다. |
유효 기간 | 영구적 (사용자가 명시적으로 삭제하지 않는 한 유지) |
설정 가능 (세션 쿠키는 브라우저 종료 시 삭제) |
브라우저 종료 시 삭제 (서버에서 설정 가능) |
데이터 전송 | 서버로 전송되지 않음. | 서버로 자동 전송 | 서버에서 관리, 클라이언트에는 세션 ID만 저장 |
보안 | 클라이언트 자바스크립트로 접근 가능, 보안 취약 | HttpOnly , Secure 옵션으로 보호 가능 |
서버에서 관리, 비교적 안전 |
사용 목적 | 클라이언트 측 영구 데이터 저장 | 세션 관리, 인증 정보 유지, 상태 저장 | 사용자 인증 정보 등 일시적 상태 저장 |
리덕스(Redux)와 로컬 스토리지의 값 동기화 하기
- 전역 상태 관리 라이브러리인 리덕스(Redux)와 로컬 스토리지의 값을 동기화하여 페이지가 새로고침 되더라도 값이 유지되도록 해보자.
- 여러개의 객체를 담고 있는 배열을 로컬 스토리지에 저장하는 경우를 생각해본다.
- 예:
[{id: *, text: *, translatedText: *}, ..., {id: *, text: *, translatedText: *}]
- 예:
const data = { id: Date.now(), text, translatedText, };
필요한 패키지 설치
- 우선, 필요한 패키지들을 설치해준다.
$ npm install redux-persist # 상태를 영구적으로 저장할 수 있게 해주는 도구 $ npm install @reduxjs/toolkit # 리덕스를 더 간편하고 효율적으로 사용하기 위해 만들어진 도구
/store/textSlice.js
- 여러 객체를 담고 있는 배열(Array)를 로컬 스토리지에 저장할 것이기 때문에
initialState
의textArrays
값을 배열([]
) 형태로 지정해준다.
import { createSlice } from '@reduxjs/toolkit'; const initialState = { textArrays: [], }; const textSlice = createSlice({ name: 'text', initialState, reducers: { // 상태를 업데이트 하는 리듀서 함수 saveText: (state, action) => { state.textArrays.push(action.payload); }, }, }); export const { saveText } = textSlice.actions; export default textSlice.reducer;
/store/store.js
store
변수 정의 시,middleware
부분은 리덕스 툴킷에서 미들웨어를 설정할 때,redux-persist
와 관련된 액션들이 직렬화 검사에서 무시되도록 하는 설정이다.- 이 부분을 설정하지 않으면 개발자 도구에서 경고 메시지가 출력된다.
- 리덕스 툴킷과
redux-persist
의 직렬화 방식에 차이가 있어서 발생하는 오류이다.- 리덕스(Redux)에서는 상태(state)나 액션(action)이 직렬화 가능한(serializable) 데이터여야 한다는 제약이 있다.
- 직렬화 가능한 데이터란, JSON으로 변환 가능한 데이터(객체, 배열, 문자열, 숫자 등)를 의미 한다.
- 리덕스에서 직렬화되지 않은 값(함수, DOM 요소 등)을 사용하면 예기치 않은 동작이 발생할 수 있기 때문에, 리덕스 툴킷은 기본적으로 직렬화 검사를 활성화한다.
- 그러나
redux-persist
에서 사용하는 액션(persist/PERSIST
,persist/REHYDRATE
)은 직렬화되지 않은 데이터를 포함할 수 있으므로, 이 액션들이 직렬화 검사에서 제외되도록 설정해야 한다.- 그렇지 않으면 경고나 오류가 발생할 수 있다.
- 리덕스(Redux)에서는 상태(state)나 액션(action)이 직렬화 가능한(serializable) 데이터여야 한다는 제약이 있다.
import { combineReducers } from 'redux'; import { persistReducer, persistStore } from 'redux-persist'; import { configureStore } from '@reduxjs/toolkit'; import storage from 'redux-persist/lib/storage'; // 기본적으로 로컬 스토리지 사용하기 import textReducer from './textSlice'; // redux-persist 설정 const persistConfig = { key: 'root', // 로컬 스토리지에 저장할 데이터의 이름 설정 storage, // 저장소로 로컬 스토리지 사용하기 }; // 여러 리듀서를 결합한 하나의 리듀서 객체 const rootReducer = combineReducers({ text: textReducer, }); // 퍼시스트 리듀서 생성 (기존의 rootReducer에 persistConfig를 적용한다.) const persistedReducer = persistReducer(persistConfig, rootReducer); export const store = configureStore({ reducer: persistedReducer, // 리덕스에서 미들웨어를 설정할 때, redux-persist 관련된 액션을 무시하도록 설정 middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { ignoredActions: ['persist/PERSIST', 'persist/REHYDRATE'], }, }), }); // 리덕스 스토어에 redux-persist를 적용하여 저장소와 리덕스 스토어 간의 동기화를 관리하는 객체 생성 // persistor는 저장된 상태를 복원하고, 리덕스 상태가 변할 때마다 자동으로 저장소에 상태를 동기화한다. export const persistor = persistStore(store);
- 비직렬화 관련 오류가 발생할 경우, 다음과 같이 간단하게
getDefaultMiddleware
의serializableCheck
를false
로 설정해준다.
// 스토어 설정 export const store = configureStore({ reducer: { global: persistedReducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: false, // 비직렬화 값 무시 }), });
/App.jsx
- 리덕스의
Provider
로 내부의 컴포넌트들을 감싸준다.
import { Provider } from 'react-redux'; import { store } from './store/store'; export const App = () => { return ( <Provider store={store}> ... </Provider> ); }; export default App;
/src/Test.jsx
saveText(data)
액션을 리덕스 스토어에 디스패치하여 상태를 변경시킨다.- 그리고 해당 값을 로컬 스토리지에 저장시켜준다.
import { useDispatch } from 'react-redux'; import { saveText } from '../store/textSlice'; export const App = () => { const dispatch = useDispatch(); // ... if (text && translatedText) { const data = { id: Date.now(), text, translatedText, }; // Redux에 저장 dispatch(saveText(data)); // LocalStorage에 저장 localStorage.setItem('savedTextPair', JSON.stringify(data)); } }
참고 사이트
HTTP 쿠키 - HTTP | MDN
HTTP 쿠키(웹 쿠키, 브라우저 쿠키)는 서버가 사용자의 웹 브라우저에 전송하는 작은 데이터 조각입니다. 브라우저는 그 데이터 조각들을 저장해 놓았다가, 동일한 서버에 재 요청 시 저장된 데이
developer.mozilla.org
Window.localStorage - Web API | MDN
localStorage 읽기 전용 속성을 사용하면 Document 출처의 Storage 객체에 접근할 수 있습니다. 저장한 데이터는 브라우저 세션 간에 공유됩니다. localStorage는 sessionStorage와 비슷하지만, localStorage의 데이
developer.mozilla.org
전형적인 HTTP 세션 - HTTP | MDN
HTTP와 같은 클라이언트-서버 프로토콜에서, 세션은 다음의 세 가지 과정으로 이루어집니다.
developer.mozilla.org
redux-persist
persist and rehydrate redux stores. Latest version: 6.0.0, last published: 5 years ago. Start using redux-persist in your project by running . There are 1425 other projects in the npm registry using redux-persist.
www.npmjs.com
Persist state with Redux Persist using Redux Toolkit in React - LogRocket Blog
With Redux Persist, developers can save the Redux store in persistent storage so even after refreshing the browser, the site state will be preserved.
blog.logrocket.com
'Programming > React' 카테고리의 다른 글
[React.js] React.memo() (0) | 2024.09.18 |
---|---|
[React.js] Context API를 이용하여 전역 상태 관리하기 (2) | 2024.09.11 |
[React.js] react-toastify 라이브러리 (0) | 2024.09.10 |
[React.js] uuid, nanoid 라이브러리 사용하기 (key) (0) | 2024.09.09 |
[React.js] 컴포넌트를 생성하는 방법 2가지 (JSX, React.createElement) (0) | 2024.08.26 |
[React.js] 리액트 스니펫(Snippet) 정리 (0) | 2024.08.26 |
[React.js] Create React App과 Vite (0) | 2024.08.26 |
[React.js] 함수형 업데이트와 직접 참조 업데이트 (0) | 2024.08.22 |