Programming/Client
여러 번 호출 가능한 abortController 만들기 (feat. useRef)
Jiwoo
2024. 6. 7. 17:12
문제 상황
대량 청구서를 발송 중에 취소하는 로직이 필요했습니다.
그래서 fetch 비동기 통신을 취소하는 abortController를 사용했습니다.
🚨 주의사항
abortController는 프론트의 요청만 취소하는 것임을 알아야 합니다.
요청 이후에 백엔드에서 일어나는 일은 제어할 수 없습니다.
예를 들어 청구서 발송을 클릭하고, 곧바로 취소를 눌러서 요청을 취소했다하더라도 백엔드에서는 발송이 시작된 상태이고 이것을 취소할 수는 없습니다.
저는 위와 같은 이유로 해당 로직을 작성했지만, 사용하지는 못했습니다 :(
해결 방법
메일을 발송하는 로직을 훅으로 만들어서 사용하는 컴포넌트에서 호출하도록 했습니다.
import AbortController from "abort-controller";
export function useSendInvoice(emails: React.Key[]) {
// sendInvoice 함수 실행 시 새로 생성하나 리렌더링은 막기 위해 useRef 사용
// 새로 생성하는 이유는 한 번 취소하면 계속 {aborted: true} (취소상태)로 호출되기 때문임
const abortControllerRef: MutableRefObject<AbortController | null> = useRef(null);
const url = `${process.env.NEXT_PUBLIC_API_ENDPOINT}/billing/invoice/send/mail`;
async function sendInvoice() {
abortControllerRef.current = new AbortController();
try {
const res = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${localStorage.getItem("loginToken")}`,
},
body: JSON.stringify({ emails }),
signal: abortControllerRef.current.signal as AbortSignal,
});
const { resultCode, result } = await res.json();
if (res.status !== 200 || resultCode !== "SUCCESS") {
return { success: false, errorType: "unknown error" };
}
return { success: true, data: result };
} catch (err) {
const errorObj = err as Error;
if (errorObj.name === "AbortError") {
return { success: false, errorType: "aborted" };
}
return { success: false, errorType: "unknown error" };
}
}
// fetch 요청만 중간에 취소되고 메일은 다 발송되서 사용 안 함
function cancelSend() {
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
}
return { sendInvoice, cancelSend };
}
useRef를 사용한 이유
취소한 이후에 sendInvoice
를 호출하면 {aborted: true}
(취소상태)로 호출되는 문제가 발생했습니다.
useRef를 사용하여 sendInvoice를 호출할 때마다 abortControllerRef.current sendInvoice
를 새로 생성하나, 리렌더링은 되지 않도록 처리했습니다.