폴더, 파일 구조

store 생성 로직, reducers 로직, 비동기 네트워크 통신 로직이 하나의 Store 폴더에 모아져 있었습니다. 안 그래도 리덕스의 단점 중 하나인 코드 길이가 길어져 가독성이 떨어진 경험이 있어 그것을 염두 해 store, reducers, sagas 폴더 3개로 구분했습니다.

1. store 폴더

store 관련 폴더입니다.

reduxToolkit의 configureStore를 이용해 store 생성, middleware 설정, devTools 관리를 한 번에 구현할 수 있습니다.

const store = configureStore({
  reducer: rootReducer,
  middleware: [sagaMiddleware],
  devTools: process.env.NODE_ENV !== 'production'
})

2. reducers 폴더

reducers 관련 폴더입니다.

index.js에서 combineReducers을 이용해 분리한 reducer를 모았습니다. books 하나 뿐이지만 확장성을 위해 작성했습니다.

const rootReducer = combineReducers({
  books
})

3. reducers/books.js 파일

기본적으로 redux toolkitcreateSlice를 이용해 굉장히 깔끔하고 짧게 redcuers를 구현됐다고 생각됩니다. 다만 initialState를 밖으로 꺼내는 걸 선호하기 때문에 밖으로 꺼내고 Status 관련된 변수들을 상수화 시켜 관리했습니다.

getItemsFailure 액션일 때 상태가 빠져있어 추가했습니다.

setBooks라는 함수는 state를 얻을 수 있는 함수입니다. 하지만 **useSelector hook을 이용해 바로 state를 가져올 수 있기 때문에** 삭제했습니다.

fetchBooks 함수를 이용해 비동기적으로 네트워크 통신을 할 수 있도록 잘 구현됐다고 생각합니다. 하지만 확장성을 위해 비동기 네트워크 통신 폴더에 분리할 필요성이 있어 분리했습니다. ( Saga 폴더에서 다시 )

// utils/constants
export const STATUS = {
  Idle: 'idle',
  Loading: 'loading',
  Success: 'success',
  Failure: 'failure'
}

// reducers/books
const initialState = {
  items: [],
  totalItems: 0,
  startIndex: 0,
  status: STATUS.Idle,
  error: null
}

const booksSlice = createSlice({
  name: 'books',
  initialState,
  reducers: {
		getItemsFailure(state, action) {
      state.status = STATUS.Failure
      state.error = action.payload
    }
	}
})

( 삭제 ) export const selectBooks = state => state.books

4. sagas 폴더

리덕스의 비동기 네트워크 처리를 위해 redux-saga를 이용했습니다. 사실 작성된 코드는 redux-thunk에 가까웠지만 다양한 effect와 확장성을 염두 해 redux-saga를 선택했습니다.

index.js에서 아직 하나의 saga지만 확장성을 염두 해 한 번에 처리했습니다.

export function* rootSaga() {
  yield all([booksSaga()])
}