[다독다독] Redux 적용해보기

KINA KIM·2022년 6월 6일
0

사용 이유

요가나라와 개인프로젝트를 진행하면서 props 지옥에 빠졌다. 목표 컴포넌트에서 state를 사용하려면 부모 컴포넌트부터 목표 컴포넌트까지 props를 계속 전달해야하고, 하위 컴포넌트에서 부모 컴포넌트를 조작하려면 부모 컴포넌트에 도달할 때까지 콜백함수를 무한정 호출해야 한다.

그래서 state를 전역으로 관리할 수 있는 Redux를 진행하고 있는 프로젝트에 적용해보고자 한다. Redux를 사용하면 state 관리가 엄청나게 쉬워지는 건 물론이고 한 곳에서 관리하다 보니 에러 발생 위험을 낮출 수 있고 어디서 에러가 발생했는지 추적도 쉬워지는데다 중복되는 코드 사용도 줄일 수 있을 것이다.

요가나라는 프로젝트 규모가 큰데다 컴포넌트도 엄청나게 많아서 함께 협업하는 프론트엔드 개발자 분과 의논 후에 천천히 적용해야 할 것 같고, 일단 혼자 진행하고 있는 독서 기록 웹 애플리케이션에 먼저 사용해봤다.

설치

yarn add redeux react-redux

적용한 부분

1) 내 서재의 책 관리

  • 저장된 책의 state 및 CRUD 부분

2) 토글 기능

  • 모달 토글
  • 수정모드 토글

폴더 구조

1) src/modules/action

  • initActions.js : 필요한 action 상수 정의
  • index.js : 액션 함수 정의

2) src/modules/reducers

  • bookStore.js : 내 서재 책관리 관련 store 정의
  • toggleStore.js : 토글 기능 관련 store 정의
  • index.js : 서브리듀서 병합

코드

1) src/modules/action

// initActions.js: 필요한 action 상수 정의
export const initBookActions = {
    GET: 'GET',
    UPDATE_OR_ADD: 'UPDATE_OR_ADD',
    DELETE: 'DELETE',
}
export const initToggle = {
    MODIFY_TOGGLE: 'MODIFY_TOGGLE',
    MODAL_TOGGLE: 'MODAL_TOGGLE',
}

// index.js: 액션 함수 정의
import {initBookActions, initToggle} from "./initActions"

export const bookActions = {
    getSavedBooks: (savedBooks) =>{
        return{
            type: initBookActions.GET,
            savedBooks,
        }
    },
    onClickBookUpdateOrAdd: (userId, newBook) =>{
        return{
            type: initBookActions.UPDATE_OR_ADD,
            userId,
            newBookId: newBook.isbn,
            newBook
        }
    },
    onClickBookDelete: (bookId, userId) =>{
        return{
            type: initBookActions.DELETE,
            bookId,
            userId,
        }
    }
}

export const toggleActions = {
    toggleModifyMode: (bool) =>{
        return{
            type:initToggle.MODIFY_TOGGLE,
            bool,
        }
    },
    toggleModal: (bool) =>{
        return{
            type:initToggle.MODAL_TOGGLE,
            bool,
        }
    }
}

2) src/modules/reducers

// bookStore.js: 내 서재 책관리 관련 store 정의
import {initBookActions} from "../actions/initActions";
import BookService from '../../service/book_service'

const bookService = new BookService()

const initBookState = {
    savedBooks: []
};

export const bookStore = (state = initBookState, action) => {
    switch (action.type) {
        case initBookActions.GET:
            {
                return { ...state, savedBooks: action.savedBooks }
            }

        case initBookActions.DELETE:
            {
                const update = { ...state.savedBooks }
                const id = Object.keys(update).filter(key => update[key].isbn === action.bookId)
                delete update[id]
                bookService.deleteBook(action.userId, action.bookId)
                return { ...state, savedBooks: update }
            }

        case initBookActions.UPDATE_OR_ADD:
            {
                const update = { ...state.savedBooks }
                const id = Object.keys(update).filter(key => update[key].isbn === action.newBookId)
                update[id] = action.newBook
                bookService.saveBook(action.userId, action.newBookId, action.newBook)
                return { ...state, savedBooks: update }
            }

        default:
            return state;
    }
}

// toggleStore.js: 토글 기능 관련 store 정의
import { initToggle } from "../actions/initActions"

const initToggleState = {
    modifyToggle : false,
    modalToggle : false,
}

export const toggleStore = (state=initToggleState, action) =>{
    switch(action.type){
        case initToggle.MODIFY_TOGGLE:
            {
                return {...state, modifyToggle:action.bool}
            }
        case initToggle.MODAL_TOGGLE:
            {
                return {...state, modalToggle:action.bool}
            }
        default:
            return state
    }
}

// index.js: 서브리듀서 병합
import { combineReducers } from "redux";
import {bookStore} from "./bookStore";
import { toggleStore } from "./toggleStore";

const reducers = combineReducers({
    bookStore,
    toggleStore
});

export default reducers

사용법

0) App 감싸기

import { createStore } from 'redux';
import reducers from './modules/reducers/'; //내 store가 있는 경로
import { Provider } from 'react-redux';
const store = createStore(reducers)
ReactDOM.createRoot(rootNode).render(
  <React.StrictMode>
    <Provider store={store}>
    <App/>
    </Provider>
  </React.StrictMode>,
);

1) action 사용

import { useDispatch } from 'react-redux
const dispatch = useDispatch()
dispatch(액션 함수 객체명, 사용할 액션 함수명, params)
//dispatch(bookActions.onClickBookUpdateOrAdd(props.userInfo.userId, newBook))

2) state 불러오기

import { useSelector } from 'react-redux
const 변수명 = useSelector(store=>store.스토어명.스토어에 있는 state명)
//const savedBooks = useSelector(store => store.bookStore.savedBooks)

적용 결과 예시

적용 전

//savedBooksByCategory.jsx
//앞부분은 모두 생략하였음
<Modal isToggle={isToggle}
	setIsToggle={setIsToggle}
	setModifyMode={setModifyMode}>
	<BookBasicInfo selectedBook={selectedBook} />
    <SavedBookContents
		selectedBook={selectedBook}
		setSelectedBook={setSelectedBook}
		savedBooks={props.savedBooks}
		userInfo={props.userInfo}
        onClickUpdateOrAdd={props.onClickUpdateOrAdd}
		onClickDelete={onClickDelete}
		bookRepository={props.bookRepository}
		setModifyMode={setModifyMode}
		modifyMode={modifyMode}/>
</Modal>

적용 후

//savedBooksByCategory.jsx
//앞부분은 모두 생략하였음
<Modal>
	<BookBasicInfo selectedBook={selectedBook} />
	<SavedBookContents
		selectedBook={selectedBook}
		setSelectedBook={setSelectedBook}
		userInfo={props.userInfo}/>
</Modal>

//savedBooksCategory.jsx에서 적용된 부분 일부
function SavedBookContents(props) {
    const dispatch = useDispatch()
    const isModifyMode = useSelector(store=>store.toggleStore.modifyToggle)
    
    const onClickDelete = (e) => {
        if (window.confirm('정말 삭제하시겠어요?')) {
          dispatch(bookActions.onClickBookDelete(e.target.id, props.userInfo.userId))
          alert('삭제가 완료되었습니다.')
          dispatch(toggleActions.toggleModal(false))
        }
      }
...
...
...
}
export default SavedBookContents

적용 결과 예시로 일부 코드만 발췌해 업로드하지만 프로젝트 전체에 모두 적용했다. savedBooks와 savedBooks의 CRUD 함수들, 모달과 수정모드 토글 함수들을 props으로 넘겨주지 않고 reducer를 통해 관리할 수 있게 만들어 props 지옥에서 빠져나올 수 있었다. 코드가 한결 깔끔해지고 일관성있게 작동하는 것이 느껴진다.

0개의 댓글