[RTK] 공식 문서만 보고 Redux Toolkit 적용해 보기(2)

찐새·2023년 10월 7일
0

공식 문서만 보고

목록 보기
2/9
post-thumbnail

공식 문서를 돌같이 보는 버릇을 고치자!

[RTK] 공식 문서만 보고 Redux Toolkit 적용해 보기(1)에 이어 작성하는 글이다.

5) todoReducer

todo의 로직도 auth와 큰 차이는 없다. 다만, CRUD 요청이 모두 비동기로 발생해 createAsyncThunk를 4번 사용했다. extraReducers에서도 콜백 함수의 builder를 4번 호출했다. .addCase로 연결하여 호출할 수도 있지만, 개인적으로 가독성이 떨어져 api 호출별로 builder.addCase를 나눠 작성했다.

extraReducers: (builder) => {
  // get todo list
  builder
    .addCase(fetchTodoList.pending, (state) => {
    state.isLoading = true;
  })
    .addCase(fetchTodoList.fulfilled, (state, action) => {
    state.isLoading = false;
    state.todoList = action.payload;
  })
    .addCase(fetchTodoList.rejected, (state) => {
    state.isLoading = false;
  });
  // create todo
  builder
    .addCase(fetchCreateTodo.pending, (state) => {
    state.isLoading = true;
  })
    .addCase(fetchCreateTodo.fulfilled, (state, action) => {
    state.isLoading = false;
    state.todoList.todos = [
      ...state.todoList.todos,
      action.payload.newToDoData,
    ];
  })
    .addCase(fetchCreateTodo.rejected, (state) => {
    state.isLoading = false;
  });
  // update todo
  builder
    .addCase(fetchUpdateTodo.pending, (state) => {
    state.isLoading = true;
  })
    .addCase(fetchUpdateTodo.fulfilled, (state, action) => {
    state.isLoading = false;
    state.todoList.todos = state.todoList.todos
      .map((todo) => todo.id === action.payload.updateTodo.id
           ? action.payload.updateTodo
           : todo
          );
  })
    .addCase(fetchUpdateTodo.rejected, (state) => {
    state.isLoading = false;
  });
  // delete todo
  builder
    .addCase(fetchDeleteTodo.pending, (state) => {
    state.isLoading = true;
  })
    .addCase(fetchDeleteTodo.fulfilled, (state, action) => {
    state.isLoading = false;
    state.todoList.todos = state.todoList.todos.filter(
      (todo) => todo.id !== action.payload.id
    );
  })
    .addCase(fetchDeleteTodo.rejected, (state) => {
    state.isLoading = false;
  });
},

주석으로 구분하긴 했지만, 그래도 보기가 좋지 않았다. 호출하는 함수만 다른 같은 로직이 수두룩 빽빽했기 때문이다. 이걸 어떻게 해결해야 하나 진짜 오래 고민했다. 그러다 참지 못하고 검색 찬스를 사용하고 말았다...!

RTK extrareducers builder combine을 검색하니 가장 위에 나와 비슷한 고민을 한 사람의 스택오버플로 질문이 있었다. 이 사람도 에러 로직이 같은데 어떻게 DRY하게 만드냐고 질문했다. 그 중 addMatcher를 사용한 답변을 참고했다.

공식 문서를 살펴 보니 addMatcher들어오는 작업을 action.type 속성 대신 자체 필터 함수와 일치시킬 수 있는 메서드이다.(역시 공식 문서는 답을 알고 있다, 두둥!)

첫 번째 인자로 matcher 함수를 받고, 두 번째 인자로 reducer를 받는다.

내게 필요한 과정은 pending일 때 isLoading = true로, fulfilled or rejected일 때는 isLoading = false로 변화시키는 것이다.

enum AsyncThunkTypes {
  pending = "pending",
  fulfilled = "fulfilled",
  rejected = "rejected",
}

const todoReducer = createSlice({
  // (...)
  extraReducers: (builder) => {
    // ...builder.addCase(...)

    builder
      .addMatcher(
      (action: PayloadAction) => action.type.endsWith(AsyncThunkTypes.pending),
      (state) => {
        state.isLoading = true;
      }
    )
      .addMatcher(
      (action: PayloadAction) =>
      action.type.endsWith(AsyncThunkTypes.fulfilled || AsyncThunkTypes.rejected),
      (state) => {
        state.isLoading = false;
      }
    );
  },
}

pending일 때의 addMatcherfulfilled 또는 rejected일 때의 addMatcher로 나눠 적용했다. 어쨌든 반복되는 부분을 줄여 만족했다.

6) 커스텀 훅 내용 대체

auth 때와 마찬가지로 todouseTodoList 커스텀 훅의 내용을 수정했다.

function useTodoList() {
  const { createTodo, getTodos, updateTodo, deleteTodo } = useToDoContext();
  const [todoList, setTodoList] = useState<ResponseToDoType[]>([]);
  const { state, loading, onFetching } = useFetch(getTodos);

  useEffect(() => {
    if (state?.ok) {
      setTodoList(state.todos);
  	}
  }, [loading, state]);
  return { ... }
}

상태 관리와 별개로 useFetch 커스텀 훅으로 받아온 데이터를 다시 useState에 저장하는 쓸데없는 코드와 context를 제거했다.

function useTodoList() {
  const {
    todoList: { todos: todoList },
    isLoading: loading,
  } = useAppSelector(selectTodoState);
  const dispatch = useAppDispatch();

  useEffect(() => {
    dispatch(fetchTodoList());
  }, [dispatch]);
  return { ... }
}

배운점

  1. Redux를 적용하고자 공식 문서를 보면서 열심히 달렸다. 충격적인 사실은 내가 본 것은 Redux 공식 문서였고, 내가 사용한 Redux Toolkit의 공식 문서는 따로 있었다는 사실이다! 어쩐지 API를 아무리 봐도 스펙 설명이 없더라니... 조금 더 꼼꼼히 살펴야겠다. - Redux Toolkit Docs
  2. RTK를 처음 사용해봤지만, 개발하는데 필요한 정보는 공식 문서에 다 있었다. 평소 라이브러리를 사용할 때 강의나 서적으로 접해 써 보고, 에러가 발생하면 구글링부터 했다. 그러나 이번에 공식 문서만 보면서 강의 및 구글링의 유혹을 떨쳐냈고, 목표한 바를 구현했다. 중간에 참지 못하고 검색했지만, 그 문제의 해결책 역시 이미 공식 문서에 있었다. 앞으로는 공식 문서로 먼저 공부해야겠다.
  3. 개인적으로 상태 관리 라이브러리를 사용하니 로직을 보기 편했다. 중요한 수정은 reducer만 건들면 되니까. 적절한 라이브러리의 사용은 삶의 질을 향상시키는 것 같다.
  4. 아쉬운 점은 여전히 보는 게 익숙하지 않아 전체를 살피지는 못했다는 점이다. 분명 더 나은 코드를 작성할 수 있고, 더 좋은 기능이 있을 텐데. 앞으로도 차차 살피면서 익혀야 할 문제다.
profile
프론트엔드 개발자가 되고 싶다

0개의 댓글