티스토리 뷰
✏️ 투두 다이어리
📝투두와 다이어리를 함께 관리해요✨
🔗 배포 링크 : https://project-todo-diary.web.app/
🔗 Github 저장소: https://github.com/kijiwon/todo_diary
(프로젝트를 진행하면서 새로 알게 된 부분 정리하는 글..)
✏️ Redux - Toolkit
이번 프로젝트에서 처음으로 Redux-Toolkit을 사용해 상태 관리를 진행했다!
📍 Redux-Toolkit이란..?
Redux의 일반적인 '세 가지의 문제'를 해결하기 위해 만들어진 Redux 로직을 작성하는 하나의 표준 방법
Redux의 일반적인 세 가지 문제란
- Redux 스토어 구성이 너무 복잡함
- Redux가 유용한 작업을 수행하도록 하려면 많은 패키지를 추가해야 함
- Redux에는 너무 많은 보일러 플레이트 코드(어떤 일을 하기 위해 작성해야 하는 상용구 코드)가 필요함
따라서 Redux-Toolkit의 사용은 코드를 단순화하고 많은 일반적인 Redux 실수와 버그를 제거할 수 있다.
✏️ Redux-Toolkit 유틸리티
1. configureStore( )
스토어를 구성하는 함수
여러개의 리듀서를 자동으로 합쳐주고, 기본으로 제공되는 redux-thunk를 포함한 지정된 미들웨어를 더해준다.
import { configureStore } from '@reduxjs/toolkit'
import rootReducer from './rootReducers'
const store = configureStore({
reducer: rootReducer,
})
export default store
redux-thunk를 디폴트로 제공하고 Redux Dev Tools 확장자를 활성화할 수 있음
store를 생성하고 난 다음 Provider 컴포넌트를 사용해 프로젝트에 store를 연동시킨다.
import { Provider } from 'react-redux';
import { store } from './redux/store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<GlobalStyle />
<App />
</Provider>,
);
2. createReducer( )
상태에 변화를 일으키는 리듀서 함수를 생성하는 유틸함수
액션 타입과 리듀서 함수를 연결해주는 목록을 작성한다.
immer 라이브러리를 사용해 mutative(변경 가능한) 한 형태로 작성해도 immutability(불변성)을 유지할 수 있다.
const todosReducer = createReducer([], (builder) => {
builder
.addCase('ADD_TODO', (state, action) => {
// "mutate" the array by calling push()
state.push(action.payload)
})
.addCase('TOGGLE_TODO', (state, action) => {
const todo = state[action.payload.index]
// "mutate" the object by overwriting a field
todo.completed = !todo.completed
})
.addCase('REMOVE_TODO', (state, action) => {
// Can still return an immutably-updated value if we want to
return state.filter((todo, i) => i !== action.payload.index)
})
})
builder callback 과 map object 2가지 표기법이 존재
주로 builder callback을 사용해 액션을 처리
3. createAction( )
기존 Redux에서는 액션을 정의할 때 액션 타입 상수와 액션 생성자 함수를 분리하여 선언했다.
Redux-Toolkit에서는 createAction 함수를 제공해 액션 타입 상수와 액션 생성자 함수를 결합해 추상화 한다.
const addTodo = createAction('ADD_TODO')
addTodo({ text: 'Buy milk' })
// {type : "ADD_TODO", payload : {text : "Buy milk"}})
4. createSlice( )
먼저, Redux state는 일반적으로 combineReducer에 전달되는 리듀서에 의해 정의된 "slice"로 구성된다.
프로젝트에서는 todoReducer와 diaryReducer를 함께 사용하기 때문에 combineReducer( )를 사용해 두 리듀서를 묶어준다.
import { combineReducers } from 'redux';
import todoReducer from './todoSlice';
import diaryReducer from './diarySlice';
const rootReducer = combineReducers({
todo: todoReducer,
diary: diaryReducer,
});
export default rootReducer;
createSlice 메소드는
- initialState
- reducer 함수들의 객체
- slice 이름
을 받아 이에 상응하는 액션 생성자와 액션 타입을 자동으로 생성한다.
따라서 createSlice를 사용하면 createReducer와 createAction 를 따로 작성할 필요가 없다!
(이번 프로젝트에서는 createSlice를 사용했다)
// todoSlice
import { createSlice } from '@reduxjs/toolkit';
const todoSlice = createSlice({
name: 'todos',
initialState: {
data: [],
},
reducers: {
addTodo: (state, action) => {
const { id, date, text, importance } = action.payload;
state.data.unshift({ id, date, text, importance, complete: false });
},
deleteTodo: (state, action) => {
const selectedId = action.payload;
state.data = state.data.filter((todo) => todo.id !== selectedId);
},
completeTodo: (state, action) => {
const selectedId = action.payload;
state.data = state.data.map((todo) =>
todo.id === selectedId ? { ...todo, complete: !todo.complete } : todo,
);
},
},
});
export const { addTodo, deleteTodo, completeTodo } = todoSlice.actions;
export default todoSlice.reducer;
✏️ Redux persist
Redux state를 지속적으로 저장하고 복원하기 위한 라이브러리
웹을 새로고침 하거나 세션을 종료한 후에도 사용자의 상태를 유지할 수 있다.
1. redux persist 설치하기
> npm install redux-persist
2. Redux persist 구성 객체 생성하기
state를 저장하는 위치에 따라 storage를 가져온다.
- localStorage에 저장하는 경우
import storage from 'redux-persist/lib/storage
- sessionStorage에 저장하는 경우
import storageSession from 'redux-persist/lib/storage/session
객체에 들어가는 옵션을 설정한다.
- key
- 저장된 데이터를 식별하는데 사용되는 고유 키
- storage
- Redux state를 저장할 방법을 지정
import storage from 'redux-persist/lib/storage'; // localStorage에 저장하기 위한 storage을 불러옴
const persistConfig = {
key: 'root',
storage, // localStorage 사용
};
3. persistReducer로 Redux reduer 랩핑(wrapping)하기
persistReducer 함수를 사용해 rootReducer를 persistConfig에 연결한다.
import {persistReducer} from 'redux-persist';
const persistedReducer = persistReducer(persistConfig, rootReducer);
4. Redux store 설정하기
persistStore 함수를 이용해 스토어를 구성하고 state를 지속시킨다.
import {
persistStore,
persistReducer,
} from 'redux-persist';
export const persistor = persistStore(store);
5. PersistGate 컴포넌트로 최상위 컴포넌트를 Redux persist로 감싸기
PersistGate 컴포넌트는 애플리케이션이 로딩 중에 redux state가 복원될 때까지 대기하도록 설정할 수 있다.
import { store, persistor } from './redux/store';
import { PersistGate } from 'redux-persist/integration/react';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<GlobalStyle />
<App />
</PersistGate>
</Provider>,
);
📍 non-serializable value 오류 해결하기
설정을 완료하고 저장을 하니 오류 메세지가 떴다.
이 오류는 직렬화 할 수 없는 값을 넣어 발생한 것으로 redux-toolkit 공식 문서에서 해결 방법을 찾을 수 있었다.
Redux의 핵심 사용 원칙 중 하나는 직렬화할 수 없는 값을 state 또는 actions 에 넣어서는 안 된다는 것입니다 .
그러나 대부분의 규칙과 마찬가지로 예외도 있습니다. 직렬화할 수 없는 데이터를 허용해야 하는 작업을 처리해야 하는 경우가 있을 수 있습니다. 이 작업은 매우 드물게, 필요한 경우에만 수행되어야 하며 이러한 직렬화 가능하지 않은 페이로드는 리듀서를 통해 애플리케이션 상태가 되어서는 안 됩니다.
직렬성 개발 확인 미들웨어는 작업 이나 상태에서 직렬화할 수 없는 값을 감지할 때마다 자동으로 경고합니다. 실수로 실수하는 것을 방지하려면 이 미들웨어를 활성 상태로 두는 것이 좋습니다. 그러나 해당 경고를 꺼야 하는 경우 특정 작업 유형이나 작업 및 상태의 필드를 무시하도록 미들웨어를 구성하여 사용자 정의할 수 있습니다.
.....
Redux-Persist를 사용하는 경우, 전달되는 모든 작업 유형을 구체적으로 무시해야 합니다.
해결 방법은 store에 middleware를 추가해 무시할 작업 유형을 명시하는 것이였다.
import {
persistStore,
persistReducer,
FLUSH,
REHYDRATE,
PAUSE,
PERSIST,
PURGE,
REGISTER,
} from 'redux-persist';
export const store = configureStore({
reducer: persistedReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
}),
});
위의 설정까지 마치니 오류가 없어지고 정상적으로 동작했다!!
🔗 참고 링크
https://redux-toolkit.js.org/usage/usage-guide
https://velog.io/@yoonvelog/Redux-toolkit#-redux-toolkit-%EC%82%AC%EC%9A%A9%EB%AA%A9%EC%A0%81
'코딩 > 프로젝트' 카테고리의 다른 글
[개인 프로젝트] 풀 페이지 스크롤링 구현하기(+ 네비게이션바) (0) | 2024.01.05 |
---|---|
[개인 프로젝트] 투두 다이어리(feat.투두 date 관리하기) (0) | 2023.12.12 |
[개인 프로젝트] - Holiday Calendar 리팩토링 (React Query) (0) | 2023.06.02 |
[협업 프로젝트] - 공용 Github 연결하기 (0) | 2023.03.29 |
[API활용 프로젝트] - Holiday Calendar (0) | 2023.02.19 |