Frontend

Redux Thunk 제대로 알기

mechaniccoder 2023. 6. 21. 00:32

안녕하세요. 이번 포스팅에서는 Redux Thunk에 대해 알아보려 합니다. 일반적으로 thunk는 asynchronous한 로직에 사용한다고 알려져 있는데요. Redux state와 dispatch에 접근할 수 있기 때문에 다양하게 활용할 수 있습니다.

 

이 포스팅을 읽고 난 후에는 아래의 내용들을 얻어가실 수 있습니다.

 

  • synchronous한 로직에 thunk를 활용하기
  • 여러 action을 위해 thunk에서 dispatch

Synchronous complex logic

복잡한 로직은 reducer에서 처리하는 것이 일반적이 케이스이기 합니다. 그렇지만 만약 state에 접근이 필요한 경우 Thunk에서 로직을 작성할 수도 있습니다.

const addTodoThunk = createAsyncThunk("todos/addTodo", (payload, thunkAPI) => {
  const { getState } = thunkAPI
  const state = getState()

  // some complex logic with state...
})

하지만 테스팅의 관점에서 complex logic이 thunk에 있다면 이는 테스트하기 어려운 코드입니다. thunk는 root state에 접근하기 때문에 side effect가 발생할 수 있죠. 이와는 다르게 reducer는 순수함수이므로 테스트하기가 용이합니다.

reducer를 테스트하는게 implementation detail을 테스트하는게 아닌가? 라는 생각이 들 수도 있을 것 같은데요. 비지니스 로직이 많지 않은 앱의 경우는 integration testing을 하는 방향이 맞겠지만 복잡한 비지니스 로직이 많은 앱의 경우는 integration testing과 함께 unit testing도 작성해야 하는 경우가 많을 것 같네요.

 

그래서 필요한 state를 thunk를 통해 가져오고 이를 reducer로 넘겨서 로직을 실행하는게 좋은 방향이라고 생각합니다. 그냥 컴포넌트에서 인자로 넘기면 되지 않을까? 라고도 싶은데요. 단순히 인자로 넘기기 위해 useSelector를 사용해 컴포넌트에서 state를 가져오게 되면 불필요한 리렌더링이 발생할 수 있습니다.

 

const SomeComponent = () => {
  // 이 컴포넌트에서는 불필요한 상태
  const userInfo = useAppSelector(selectUserInfo) 
  const dispatch = useDispatch()

  const handleClick = () => {
    dispatch(addTodoThunk(userInfo))
  }

  return <Button onClick={handleClick}>Add Todo</Button>
}

Multiple dispatch

여러 액션을 호출하기 위해 thunk를 사용할 수도 있습니다.

const addTodoThunk = createAsyncThunk("todos/addTodo", (payload, thunkAPI) => {
  const { dispatch } = thunkAPI
  
  dispatch(updateUserInfo(...))
  dispatch(saveTodos(...)
})

 


마치며

이렇게 thunk는 다양한 쓰임새로 사용될 수 있습니다. 실제로 회사에서는 thunk를 사용해 redux root state에 접근 후 복잡한 로직을 실행하는 것에 사용하고 있습니다. 평소에 thunk를 asynchronous한 로직에만 사용하셨다면 이번 기회를 통해 다양하게 활용해보는건 어떨까요? 물론 상황에 맞게 사용해야겠지만요.