
리덕스 환경에서 비동기 작업을 처리해 보자.
지난번 리덕스 툴킷 실습에서 사용했던 카트 예제를 그대로 사용할 것이다.
기존에 로컬 데이터로 가지고 있었던 카트의 데이터를 서버로 옮기고서
카트에 아이템을 추가하거나 제거해서 업데이트할 때 서버로 데이터를 보내고
처음 페이지를 로딩할 때는 서버에서 데이터를 가져오도록 만들어 보자.
리덕스에서 비동기 작업을 처리하는 법
여기서 카트는 상태로 존재하고, 이 상태를 업데이트하는 함수들은 전부 리듀서 함수다.
리듀서 함수는 순수함수이기 때문에 그 내부에서 사이드이펙트를 처리할 수 없다.
즉, 카트 상태를 서버에서 가져오는 작업을 지금 가진 리듀서 함수 내에 넣어둘 수 없다.
그러면 서버와 통신하는 작업을 처리할 코드를 어디에 둬야 할까?
더 많은 방법이 있지만 일단은 두 가지 방법을 살펴보자.
하나는 컴포넌트 내에 코드를 두는 것이고, 다른 하나는 thunk를 사용하는 것이다.
두 가지 방법을 각각 살펴보면서 어떻게 비동기 작업을 처리하고 있는지 알아보자.
1. 컴포넌트 내에서 비동기 작업 처리하기
컴포넌트 내에서 사이드이펙트를 수행하려면 사이드이펙트를 처리하는 useEffect 훅을 사용한다.
App.js 내에서 useEffect를 사용해 카트 상태가 변경됐을 때 데이터를 서버로 보내 보자.
useEffect(() => {
const sendCartData = async () => {
dispatch(
uiActions.showNotification({
//pending 메시지
})
);
const response = await fetch(
//서버 주소,
{
method: "PUT",
body: JSON.stringify(cart),
}
);
if (!response.ok) {
throw new Error("Sending cart data failed.");
}
dispatch(
uiActions.showNotification({
//성공 메시지
})
);
};
//초기 로딩을 할 때는 데이터를 전송하지 않는다.
if (isInitial) {
isInitial = false;
return;
}
sendCartData().catch((error) => {
dispatch(
uiActions.showNotification({
//실패 메시지
})
);
});
}, [cart, dispatch]);
async await를 이용해 비동기 작업을 처리하는 코드를 useEffect에 넣어 주었다.
각각의 작업 상태마다 notification ui를 설정해 화면에 작업 처리 상태를 보여주는 기능도 추가했다.
이렇게 하면 App 컴포넌트 내에서 서버와 데이터를 주고받도록 만들 수 있고,
서버와의 통신 상태에 따라서 ui를 업데이트해 사용자에게 진행 과정을 알려줄 수 있다.
그렇지만 비동기 처리 코드를 컴포넌트 내에 두면 코드가 상당히 길어지고
사이드이펙트 처리와 여러 상태를 업데이트하는 함수가 렌더링 로직과 섞여 있어
애플리케이션이 더 커지면 유지보수가 어려워질 수 있다는 약점이 있어 보인다.
2. thunk 사용하기
thunk를 활용하면 로직을 분리해서 첫번째 방법보다 코드를 더 깔끔하게 유지할 수 있다.
리덕스 툴킷은 내부적으로 redux-thunk라는 미들웨어를 가지고 있다.
이것을 활용하면 일반 액션 생성자와는 다르게 비동기적으로 작업을 처리할 액션 생성자를 만들 수 있다.
thunk를 비동기 작업에 사용할 때 기본적인 포맷은 다음과 같다.
// fetchTodoById is the "thunk action creator"
export function fetchTodoById(todoId) {
// fetchTodoByIdThunk is the "thunk function"
return async function fetchTodoByIdThunk(dispatch, getState) {
const response = await client.get(`/fakeApi/todo/${todoId}`)
dispatch(todosLoaded(response.todos))
}
}
thunk 액션 생성자 함수를 만들고 그 내부에서 thunk 함수를 리턴해 주는데, 이 함수는 비동기 함수다.
thunk함수에는 액션을 보낼 수 있는 dispatch와 현재 상태 트리를 반환하는 getState를 받는다.
이 thunk 함수에서 비동기 작업을 해 주고 dispatch로 액션을 보내 스토어에 새로운 상태를 저장할 수 있다.
카트 예제에서 thunk를 사용해 보자.
export const sendCartData = (cart) => {
return async (dispatch) => {
dispatch(
uiActions.showNotification({
//pending 메시지
})
);
const sendRequest = async () => {
const response = await fetch(
//서버 주소,
{
method: "PUT",
body: JSON.stringify(cart),
}
);
if (!response.ok) {
throw new Error("Sending cart data failed.");
}
};
/* 실행 부분 */
try {
await sendRequest();
dispatch(
uiActions.showNotification({
//성공 메시지
})
);
} catch (error) {
dispatch(
toggleActions.showNotification({
//에러 메시지
})
);
}
};
};
이렇게 만든 액션 생성자를 별도의 파일에 넣고 export해 다른 파일에서 쓸 수 있도록 한 뒤
App 컴포넌트에서는 필요할 때 dispatch를 하기만 하면 App 컴포넌트를 깔끔하게 유지할 수 있고
액션 생성자만 별로의 파일로 보관할 수 있어 유지보수가 쉽고 코드의 가독성이 올라가는 것을 알 수 있다.
그러나 이것이 100% 만족스러운 해결책은 아니므로.. 다음에 이어서 다른 대안들에 대해 살펴보기로 하자.
'React' 카테고리의 다른 글
[React] 리액트 앱에 인증 추가하기 (2) | 2023.05.01 |
---|---|
[React] 리액트 라우터 data api 사용하기 (0) | 2023.04.26 |
[React] 리액트 성능 최적화: React.Memo, useCallback, useMemo (0) | 2023.04.18 |
[Redux] 리덕스 툴킷 사용하기 (2) | 2023.04.14 |
[Redux] 리덕스 기초 (1) | 2023.04.13 |

리덕스 환경에서 비동기 작업을 처리해 보자.
지난번 리덕스 툴킷 실습에서 사용했던 카트 예제를 그대로 사용할 것이다.
기존에 로컬 데이터로 가지고 있었던 카트의 데이터를 서버로 옮기고서
카트에 아이템을 추가하거나 제거해서 업데이트할 때 서버로 데이터를 보내고
처음 페이지를 로딩할 때는 서버에서 데이터를 가져오도록 만들어 보자.
리덕스에서 비동기 작업을 처리하는 법
여기서 카트는 상태로 존재하고, 이 상태를 업데이트하는 함수들은 전부 리듀서 함수다.
리듀서 함수는 순수함수이기 때문에 그 내부에서 사이드이펙트를 처리할 수 없다.
즉, 카트 상태를 서버에서 가져오는 작업을 지금 가진 리듀서 함수 내에 넣어둘 수 없다.
그러면 서버와 통신하는 작업을 처리할 코드를 어디에 둬야 할까?
더 많은 방법이 있지만 일단은 두 가지 방법을 살펴보자.
하나는 컴포넌트 내에 코드를 두는 것이고, 다른 하나는 thunk를 사용하는 것이다.
두 가지 방법을 각각 살펴보면서 어떻게 비동기 작업을 처리하고 있는지 알아보자.
1. 컴포넌트 내에서 비동기 작업 처리하기
컴포넌트 내에서 사이드이펙트를 수행하려면 사이드이펙트를 처리하는 useEffect 훅을 사용한다.
App.js 내에서 useEffect를 사용해 카트 상태가 변경됐을 때 데이터를 서버로 보내 보자.
useEffect(() => {
const sendCartData = async () => {
dispatch(
uiActions.showNotification({
//pending 메시지
})
);
const response = await fetch(
//서버 주소,
{
method: "PUT",
body: JSON.stringify(cart),
}
);
if (!response.ok) {
throw new Error("Sending cart data failed.");
}
dispatch(
uiActions.showNotification({
//성공 메시지
})
);
};
//초기 로딩을 할 때는 데이터를 전송하지 않는다.
if (isInitial) {
isInitial = false;
return;
}
sendCartData().catch((error) => {
dispatch(
uiActions.showNotification({
//실패 메시지
})
);
});
}, [cart, dispatch]);
async await를 이용해 비동기 작업을 처리하는 코드를 useEffect에 넣어 주었다.
각각의 작업 상태마다 notification ui를 설정해 화면에 작업 처리 상태를 보여주는 기능도 추가했다.
이렇게 하면 App 컴포넌트 내에서 서버와 데이터를 주고받도록 만들 수 있고,
서버와의 통신 상태에 따라서 ui를 업데이트해 사용자에게 진행 과정을 알려줄 수 있다.
그렇지만 비동기 처리 코드를 컴포넌트 내에 두면 코드가 상당히 길어지고
사이드이펙트 처리와 여러 상태를 업데이트하는 함수가 렌더링 로직과 섞여 있어
애플리케이션이 더 커지면 유지보수가 어려워질 수 있다는 약점이 있어 보인다.
2. thunk 사용하기
thunk를 활용하면 로직을 분리해서 첫번째 방법보다 코드를 더 깔끔하게 유지할 수 있다.
리덕스 툴킷은 내부적으로 redux-thunk라는 미들웨어를 가지고 있다.
이것을 활용하면 일반 액션 생성자와는 다르게 비동기적으로 작업을 처리할 액션 생성자를 만들 수 있다.
thunk를 비동기 작업에 사용할 때 기본적인 포맷은 다음과 같다.
// fetchTodoById is the "thunk action creator"
export function fetchTodoById(todoId) {
// fetchTodoByIdThunk is the "thunk function"
return async function fetchTodoByIdThunk(dispatch, getState) {
const response = await client.get(`/fakeApi/todo/${todoId}`)
dispatch(todosLoaded(response.todos))
}
}
thunk 액션 생성자 함수를 만들고 그 내부에서 thunk 함수를 리턴해 주는데, 이 함수는 비동기 함수다.
thunk함수에는 액션을 보낼 수 있는 dispatch와 현재 상태 트리를 반환하는 getState를 받는다.
이 thunk 함수에서 비동기 작업을 해 주고 dispatch로 액션을 보내 스토어에 새로운 상태를 저장할 수 있다.
카트 예제에서 thunk를 사용해 보자.
export const sendCartData = (cart) => {
return async (dispatch) => {
dispatch(
uiActions.showNotification({
//pending 메시지
})
);
const sendRequest = async () => {
const response = await fetch(
//서버 주소,
{
method: "PUT",
body: JSON.stringify(cart),
}
);
if (!response.ok) {
throw new Error("Sending cart data failed.");
}
};
/* 실행 부분 */
try {
await sendRequest();
dispatch(
uiActions.showNotification({
//성공 메시지
})
);
} catch (error) {
dispatch(
toggleActions.showNotification({
//에러 메시지
})
);
}
};
};
이렇게 만든 액션 생성자를 별도의 파일에 넣고 export해 다른 파일에서 쓸 수 있도록 한 뒤
App 컴포넌트에서는 필요할 때 dispatch를 하기만 하면 App 컴포넌트를 깔끔하게 유지할 수 있고
액션 생성자만 별로의 파일로 보관할 수 있어 유지보수가 쉽고 코드의 가독성이 올라가는 것을 알 수 있다.
그러나 이것이 100% 만족스러운 해결책은 아니므로.. 다음에 이어서 다른 대안들에 대해 살펴보기로 하자.
'React' 카테고리의 다른 글
[React] 리액트 앱에 인증 추가하기 (2) | 2023.05.01 |
---|---|
[React] 리액트 라우터 data api 사용하기 (0) | 2023.04.26 |
[React] 리액트 성능 최적화: React.Memo, useCallback, useMemo (0) | 2023.04.18 |
[Redux] 리덕스 툴킷 사용하기 (2) | 2023.04.14 |
[Redux] 리덕스 기초 (1) | 2023.04.13 |