티스토리 뷰

✏️ 투두 다이어리

📝투두와 다이어리를 함께 관리해요✨

 

 

🔗 배포 링크 : https://project-todo-diary.web.app/

 

TodoDiary

 

project-todo-diary.web.app

 

🔗 Github 저장소: https://github.com/kijiwon/todo_diary

 

GitHub - kijiwon/todo_diary

Contribute to kijiwon/todo_diary development by creating an account on GitHub.

github.com


 

 

(프로젝트를 진행하면서 새로 알게 된 부분 정리하는 글..)

✏️ Redux - Toolkit 

이번 프로젝트에서 처음으로 Redux-Toolkit을 사용해 상태 관리를 진행했다!

 

📍 Redux-Toolkit이란..?

Redux의 일반적인 '세 가지의 문제'를 해결하기 위해 만들어진 Redux 로직을 작성하는 하나의 표준 방법

Redux의 일반적인 세 가지 문제란

  1. Redux 스토어 구성이 너무 복잡함
  2. Redux가 유용한 작업을 수행하도록 하려면 많은 패키지를 추가해야 함
  3. 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 메소드는 

  1. initialState
  2. reducer 함수들의 객체
  3. 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

 

Usage Guide | Redux Toolkit

 

redux-toolkit.js.org

https://velog.io/@yoonvelog/Redux-toolkit#-redux-toolkit-%EC%82%AC%EC%9A%A9%EB%AA%A9%EC%A0%81

 

Redux-toolkit

이번에 구글 설문지의 주요 기능을 구현해보면서 redux-toolkit을 사용해보게 되었다. [프로젝트 보기] 기존에는 redux와, thunk 미들웨어를 사용하여 상태관리를 했었는데 redux-toolkit의 장점들도 분명

velog.io

https://velog.io/@bcl0206/%EC%83%88%EB%A1%9C%EA%B3%A0%EC%B9%A8-%ED%9B%84%EC%97%90%EB%8F%84-store-state-%EC%9C%A0%EC%A7%80%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95-Redux-persist

 

새로고침 후에도 store state 유지하는 방법 ( Redux persist / 로컬스토리지)

Redux가 크게 필요하진 않은 서비스라 하더라도 절대 피해갈 수 없는 한 가지가 있다.바로 회원 인증 및 인가이다. 로그인을 요구하지 않는 페이지가 얼마나 되는가.모든 페이지가 회원 인증 및

velog.io

 

728x90