Redux #4 Redux Thunk
Redux Thunk로 비동기 작업 처리하기
Redux Thunk는 Redux에서 비동기 작업(예: HTTP 요청)을 처리할 때 사용하는 미들웨어다. Redux는 기본적으로 객체 형태의 액션만 처리할 수 있지만, Thunk를 사용하면 함수 형태의 액션도 디스패치할 수 있다. 이 글에서는 Redux Toolkit을 사용하여 Thunk로 HTTP 요청을 처리하는 방법을 알아보자.
Thunk란 무엇인가?
Thunk는 액션을 함수 형태로 만들어 디스패치할 수 있도록 도와주는 미들웨어다. 일반적으로 HTTP 요청과 같은 비동기 작업에서 많이 사용된다. Thunk를 통해 비동기 작업을 실행하고, 작업 결과에 따라 Redux 상태를 업데이트할 수 있다.
Redux Thunk로 HTTP 요청 처리하기
여기서는 JSONPlaceholder API를 사용해 게시물 데이터를 가져오는 간단한 예제를 작성해 보겠다.
1. Redux Slice 생성
우선 Redux Slice를 생성해 초기 상태와 동기 액션을 정의한다. 비동기 요청의 시작, 성공, 실패를 처리하기 위해 fetchPostsStart, fetchPostsSuccess, fetchPostsFailure 액션을 만든다.
import { createSlice } from "@reduxjs/toolkit";
const postsSlice = createSlice({
name: "posts",
initialState: {
posts: [],
status: "idle", // idle, loading, succeeded, failed
error: null,
},
reducers: {
fetchPostsStart(state) {
state.status = "loading";
},
fetchPostsSuccess(state, action) {
state.status = "succeeded";
state.posts = action.payload;
},
fetchPostsFailure(state, action) {
state.status = "failed";
state.error = action.payload;
},
},
});
export const { fetchPostsStart, fetchPostsSuccess, fetchPostsFailure } = postsSlice.actions;
export default postsSlice.reducer;
2. Thunk 액션 생성
Thunk를 사용해 비동기 HTTP 요청을 처리하는 액션을 작성한다. 요청 시작 시 로딩 상태로 전환하고, 성공하면 데이터를 상태에 저장하며, 실패 시 에러 메시지를 저장한다.
export const fetchPosts = () => async (dispatch) => {
dispatch(fetchPostsStart());
try {
const response = await fetch("https://jsonplaceholder.typicode.com/posts");
if (!response.ok) {
throw new Error("Failed to fetch posts.");
}
const data = await response.json();
dispatch(fetchPostsSuccess(data));
} catch (error) {
dispatch(fetchPostsFailure(error.message));
}
};
3. Redux Store 설정
Redux Store를 설정하고, 생성한 Slice를 연결한다. Redux Toolkit은 기본적으로 Thunk 미들웨어를 포함하고 있어 별도의 설정이 필요 없다.
import { configureStore } from "@reduxjs/toolkit";
import postsReducer from "./postsSlice";
const store = configureStore({
reducer: {
posts: postsReducer,
},
});
export default store;
4. React 컴포넌트에서 사용
React 컴포넌트에서 Thunk 액션을 디스패치하고 상태를 렌더링한다. useSelector를 통해 Redux 상태를 구독하고, useDispatch를 사용해 Thunk 액션을 호출한다.
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { fetchPosts } from "../store/postsThunk";
const Posts = () => {
const dispatch = useDispatch();
const { posts, status, error } = useSelector((state) => state.posts);
useEffect(() => {
if (status === "idle") {
dispatch(fetchPosts());
}
}, [status, dispatch]);
if (status === "loading") {
return <p>Loading...</p>;
}
if (status === "failed") {
return <p>Error: {error}</p>;
}
return (
<div>
<h1>Posts</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>
<h3>{post.title}</h3>
<p>{post.body}</p>
</li>
))}
</ul>
</div>
);
};
export default Posts;
Thunk의 동작 방식
Thunk의 동작은 다음과 같다:
- Thunk 액션(fetchPosts)을 디스패치하면 함수가 실행된다.
- 요청 시작 시 fetchPostsStart 액션을 디스패치해 상태를 로딩 상태로 전환한다.
- 요청 성공 시 fetchPostsSuccess 액션을 디스패치해 데이터를 상태에 저장한다.
- 요청 실패 시 fetchPostsFailure 액션을 디스패치해 에러 메시지를 상태에 저장한다.
Redux Thunk의 장점과 단점
- 장점:
- 비동기 작업을 간단히 처리할 수 있다.
- 상태 업데이트와 비동기 로직을 분리할 수 있어 유지보수가 쉬워진다.
- Redux Toolkit과 함께 사용하면 설정이 간단하다.
- 단점:
- 상태 관리와 비동기 로직이 많은 경우 코드가 복잡해질 수 있다.
- React Query나 SWR처럼 데이터 캐싱 기능이 내장되어 있지 않다.
결론
Redux Thunk는 Redux 상태 관리에서 비동기 작업을 처리하기 위한 강력한 도구다. 특히 Redux Toolkit과 함께 사용하면 설정이 간단하고 직관적으로 동작한다. 그러나 데이터 요청과 캐싱이 주요 작업인 프로젝트에서는 React Query나 SWR이 더 적합할 수 있다. 프로젝트의 요구사항에 따라 적절한 도구를 선택해 효율적인 개발 환경을 만들어 보자! 😊