728x90
728x90
로컬 스토리지(LocalStorage)에 내용을 저장하고, 전역 상태 관리 라이브러리(Redux)와 동기화 하는 방법
들어가며
- 리액트(React.js)에서 특정 변수를 로컬 스토리지(Local Storage)에 저장하고, 전역 상태 관리 라이브러리(Redux)와 동기화 시키는 방법을 정리해본다.
- 이렇게 함으로써, 사용자의 입력을 받아 특정 변수를 화면에 표시해줘야 할 경우 새로고침을 해도 동일한 내용이 표시되게 할 수 있다.
로컬 스토리지(Local Storage)
개념
- 브라우저에 데이터를 영구적으로 저장할 수 있는 방법
- 브라우저가 닫혀도 데이터가 유지되며, 도메인별로 구분되어 저장된다.
- 로컬 스토리지는 키-값(Key-Value) 형식으로 데이터를 저장하며, 데이터는 문자열 형식으로만 저장할 수 있다.
- 따라서 객체(Object)나 배열(Array) 같은 복잡한 데이터는 JSON으로 변환하여 저장하고, 가져올 때 다시 파싱(Parsing)해야 한다.
- JSON으로 변환하기 위해서는 @JSON.stringfy(객체|배열)@ 메서드를 사용하고, JSON을 다시 객체/배열로 파싱하려면 @JSON.parse(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));
}
}
참고 사이트
728x90
728x90
'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 |