[Redux] Redux Thunk

상현·2023년 11월 29일
2

React

목록 보기
12/24
post-thumbnail

미들웨어 (middleware)

Redux Thunk는 Redux 팀에서 만든 미들웨어이다. 따라서 미들웨어에 대해 먼저 간단하게 알아보자.

Redux

미들웨어가 없는 Redux는 다음과 같은 과정을 반복한다.

  1. view(화면)에서는 전역 데이터가 담겨있는 store의 값을 구독한다.
  2. view에서 사용자가 action을 취한다.
  3. action이 일어나면 dispatch에게 action을 전달한다.
  4. dipsatch는 어떤 action이 일어났는지 살펴보고 어떠한 동작을 해야하는지 reducer 에게 전달한다.
  5. reducer에서 일어나야 하는 동작들에 대해 처리한다.
  6. reducerstore에 바뀐 사항에 대해 알린다.
  7. storeview에게 바뀐 값이 있다고 알려준다.
  8. view는 바뀐 값을 다시 렌더링한다.

여기까지가 Redux가 하는 일반적인 일이다. 하지만 문제점이 하나 있다. reducer는 순수 함수 이어야 한다.
즉, 같은 Input에 대해서는 언제나 동일한 Output이 나와야 하고, 그렇기 때문에 reducer안에서는 네트워크 통신이나 랜덤 값 생성 등이 일어나서는 안된다.

middleware

이러한 점을 보완하기 위해 있는 것이 미들웨어다. 미들웨어는 reducer 이전에 위치하며, 미들웨어에서도 dispatch를 할 수 있다.

미들웨어를 사용하면 action이 dispatch 된 다음, reducer에서 해당 action을 받아와서 업데이트 하기 전에 추가적인 작업을 할 수 있다.

추가적인 작업이란 다음과 같다.

  • 특정 조건에 따라 action이 무시되게 만들 수 있다.
  • action을 콘솔에 출력하거나, 서버쪽에 로깅을 할 수 있다.
  • action이 dispatch 됐을 때 이를 수정해서 reducer에게 전달되도록 할 수 있다.
  • 특정 action이 발생했을 때 이에 기반하여 다른 action이 발생되도록 할 수 있다.
  • 특정 action이 발생했을 때 다른 함수를 실행 시킬 수 있다.

보통 리덕스에서 미들웨어를 사용하는 주된 사용 용도는 비동기 작업을 처리 할 때이다. 예를 들어 리액트 앱에서 우리가 백엔드 API를 연동해야 된다면, 리덕스 미들웨어를 사용하여 처리한다.

여러가지 미들웨어가 존재하지만, Redux Thunk는 비동기 작업을 처리할 수 있는 대표적인 미들웨어다.

Redux Thunk

우선 Thunk라는 용어가 뭔지 알아보자. 위키백과에서는 다음과 같이 설명하고 있다.

컴퓨터 프로그래밍에서 썽크(Thunk)는 기존의 서브루틴에 추가적인 연산을 삽입할 때 사용되는 서브루틴이다. 썽크는 주로 연산 결과가 필요할 때까지 연산을 지연시키는 용도로 사용되거나, 기존의 다른 서브루틴들의 시작과 끝 부분에 연산을 추가시키는 용도로 사용되는데, 컴파일러 코드 생성시와 모듈화 프로그래밍 방법론 등에서는 좀 더 다양한 형태로 활용되기도 한다.

위에서 설명한 용어에 빗대어서 말하자면 Redux Thunk도 어떠한 동작을 지연시켜주는 녀석 이라고 보면 되겠다.

createAsyncThunk

Redux에서 제공하는 createAsyncThunk 라는 함수로 쉽게 비동기 작업을 하는 Thunk를 만들 수 있다.

todos.js

export const __findAllTodos = createAsyncThunk("todos/findAll", async (payload, thunkAPI) => {
  try {
    const {data} = await instance.get(`todos`);
    // Promise -> resolve (네트워크 응답이 성공한 경우, dispatch 해주는 기능을 가진 API)
    return thunkAPI.fulfillWithValue(data);

  } catch (error) {
    console.error(error)
    return thunkAPI.rejectWithValue(error);
  }

});


const todosSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
  },
  extraReducers: {
    [__findAllTodos.pending]: (state, action) => {
      state.isLoading = true;
      state.isError = false;
    }, [__findAllTodos.fulfilled]: (state, action) => {
      console.log(state, action)
      state.isLoading = false;
      state.isError = false;
      state.todos = action.payload;
    }, [__findAllTodos.rejected]: (state, action) => {
      state.isLoading = false;
      state.isError = true;
    }
  }
})

App.jsx

useEffect(() => {
  // thunk를 dispatch한다!
  dispatch(__findAllTodos());
}, [dispatch]);

thunk 안에서 다른 thunk를 dispatch할 수도, reducer를 dispatch할 수도 있다.

export const __addTodo = createAsyncThunk("todos/add", async (payload, thunkAPI) => {
  try {
    const {data} = await instance.post(`todos`, payload);
    thunkAPI.dispatch(__findAllTodos());
    thunkAPI.dispatch(todosSlice.actions.log());
    return thunkAPI.fulfillWithValue(data)
  } catch (error) {
    return thunkAPI.rejectWithValue(error);
  }
})

RTK Query

Redux Toolkit을 설치한 경우 RTK Query라는 기능을 이용할 수 있다. 안에 createAsyncThunk가 내장되어 있어 createAsyncThunk를 사용하는 것은 같지만, 위에서 작성한 내용들을 더 쉽게 작성할 수 있게 도와준다.

  1. createApi 메서드를 사용해 아래 내용들을 채워준다.
export const todosApi = createApi({
  reducerPath: 'todosApi',
  baseQuery: fetchBaseQuery({
    baseUrl: process.env.REACT_APP_SEVER_URL // http:localhost:4000
  }),
  endpoints: (build) => ({
    findAllTodos: build.query({
      query: (arg) => 'todos'  // http:localhost:4000/todos
    }) 
  }),
})

// endpoint에 적은 메서드의 이름을 따른 hook이 자동 생성된다.
// use[endpoint 이름]Query
export const { useFindAllTodosQuery } = todosApi
  1. configureStore 설정
const store = configureStore({
  reducer: {
    [todosApi.reducerPath]: todosApi.reducer
  },
  middleware: getDefaultMiddleware => getDefaultMiddleware().concat(todosApi.middleware)
})
  1. 사용
const {data, error, isLoading} = useFindAllTodosQuery();

useEffect(() => {
  console.log('todos is loading... ', isLoading)
  if (!isLoading) {
    console.log('로딩 완료!')
    console.log('데이터는...', data)
  }
}, [isLoading]);

RTK가 자동으로 만들어준 hook을 사용하면 isLoading 여부와 error 그리고 data를 반환해준다.
이를 useEffect와 함께 사용하면 쉽게 데이터를 불러올 수 있다.

profile
프론트엔드 개발자

1개의 댓글

comment-user-thumbnail
2023년 11월 29일

오우 미들웨어 완벽한 설명 이해 쏙쏙됩니다!

답글 달기