리덕스 공부 #4

min Mt·2020년 5월 14일
0
post-thumbnail

Redux 공식 튜토리얼
Advanced Tutorial 정리

Async Actions

앞에서 배운 ToDo 앱은 동기적이었다. 즉, 액션을 dispatch 하는 순간 스테이트가 바로 업데이트 된다. 하지만 우리가 사용하고 만들 앱들은 다 네트워크를 사용하는 앱들이다. 이러한 앱들은 비동기적 방식을 많이 사용한다.
비동기 액션을 어떻게 처리하는지 알아보자.

With Asynchronous API

비동기적 API를 호출할 때, 다음 3가지 종류의 액션이 필요하다.
1. An action informing the reducers that the request began.
2. An action informing the reducers that the request finished successfully.
3. An action informing the reducers that request failed.

튜토리얼에 있는 예제는 reddit의 subreddit을 요청하는 상황이다.
예제를 봐보자

import fetch from 'cross-fetch'

export const REQUEST_POSTS = 'REQUEST_POSTS'
function requestPosts(subreddit) {
  return {
    type: REQUEST_POSTS,
    subreddit
  }
}

export const RECEIVE_POSTS = 'RECEIVE_POSTS'
function receivePosts(subreddit, json) {
  return {
    type: RECEIVE_POSTS,
    subreddit,
    posts: json.data.children.map(child => child.data),
    receivedAt: Date.now()
  }
}

export const INVALIDATE_SUBREDDIT = 'INVALIDATE_SUBREDDIT'
export function invalidateSubreddit(subreddit) {
  return {
    type: INVALIDATE_SUBREDDIT,
    subreddit
  }
}

// Meet our first thunk action creator!
// Though its insides are different, you would use it just like any other action creator:
// store.dispatch(fetchPosts('reactjs'))

export function fetchPosts(subreddit) {
  // Thunk middleware knows how to handle functions.
  // It passes the dispatch method as an argument to the function,
  // thus making it able to dispatch actions itself.

  return function (dispatch) {
    // First dispatch: the app state is updated to inform
    // that the API call is starting.

    dispatch(requestPosts(subreddit))

    // The function called by the thunk middleware can return a value,
    // that is passed on as the return value of the dispatch method.

    // In this case, we return a promise to wait for.
    // This is not required by thunk middleware, but it is convenient for us.

    return fetch(`https://www.reddit.com/r/${subreddit}.json`)
      .then(
        response => response.json(),
        // Do not use catch, because that will also catch
        // any errors in the dispatch and resulting render,
        // causing a loop of 'Unexpected batch number' errors.
        // https://github.com/facebook/react/issues/6895
        error => console.log('An error occurred.', error)
      )
      .then(json =>
        // We can dispatch many times!
        // Here, we update the app state with the results of the API call.

        dispatch(receivePosts(subreddit, json))
      )
  }
}

코드가주석이 엄청 긴데 일단 한번 생각해보자. dispatch(action)는 action 을 인자로 받는다. action 은 object이다. 그런데 // store.dispatch(fetchPosts('reactjs')) 이런식으로 dispatch에다가 function을 리턴하는 인자를 넣어놨다. 이게 가능한 이유가 미들웨어 덕분이다. 액션이 객체 형태가 아닌 함수 형태일 때, 전달받은 인자를 액션에 넣어 다시 리턴해 주는 미들웨어를 사용하면 된다. 이게 Redux-Thunk이다. 코드 일부를 보면 다음과 같다.

if (typeof action === 'function') {
  //dispatch를 전달해야 조건에 따라 dispatch를 할수 있고, 또 getState를 넣어줘서
  //현재 state를 조건에 사용할 수 있도록 만들어졌다.
  return action(dispatch, getState, extraArgument); 
}

이런식으로 하면 비동기적 처리가 가능하다. 함수를 액션으로 넣었으니, 조건에 따라 dispatch를 하던 그냥 return을 해버리던 하면 된다.
예제 코드를 봐보면 먼저 api 요청을 하는 액션을 dispatch 한다.

dispatch(requestPosts(subreddit))

그 다음 cross-fetch 라이브러리를 사용하여 Promise를 반환하고, 요청이 실패하거나 성공하거나 할 경우에 맞추어 다시 dispatch를 한다. 흠 axios를 사용해도 된다.

return fetch(`https://www.reddit.com/r/${subreddit}.json`)
      .then(
        response => response.json(),
        error => console.log('An error occurred.', error)
      )
      .then(json =>
        // We can dispatch many times!
        // Here, we update the app state with the results of the API call.
        dispatch(receivePosts(subreddit, json))
      )

요약

내가 이해한 대로 정리.
1. 비동기 처리를 위해서는 store에 dispatch 하기 직전에 주어진 조건에 따라 어떠한 처리들을 해야함.
2. action은 단순 object라서 이러한 비동기 처리를 해결할 수 없음.
3. 따라서 redux-thunk, redux-promise 같은 미들웨어들을 사용하여 dispatch가 함수형태의 인자도 받을 수 있도록 함.
4. 이렇게 object대신 들어간 함수 안에서 axios나 cross-fetch 따위의 라이브러리를 사용하여 http/s 통신을 하거나 그 외 원하는 입맛대로 비동기적 처리를 함.

profile
안녕하세요!

0개의 댓글