본문 바로가기
Front-End/React

[React] lambda-client request 취소하기

by SeanK 2023. 5. 15.

안녕하세요, Sean입니다.

 

최근 제작한 웹앱 프로젝트에서 한 가지 심각한 문제가 발생했는데요, 그건 바로 api request를 취소했지만 서버에서는 이를 알지 못해 뒤늦게 데이터가 랜더링 되는 문제가 있었습니다.

 

워낙 방대한 데이터에 쿼리를 하다보니 데이터 수신에 2분가량이 소요되는데 중간에 멈추더라도 서버에서 데이터를 보내면 클라이언트에서 이를 받아 랜더링 하는 게 주된 이유였죠.

 

백엔드팀에 물어보니, 안타깝게도 람다에 쿼리가 들어가면 리스폰스를 막지 못한다고 합니다.

(아마 가능은 할 겁니다. 안되는게 어디 있겠습니다 허허..)

 

여하튼 위 문제를 프런트에서 해결해야 하는 상황!

다행히 javascript에서는 "AbortController"라는 내장 api를 제공합니다.

 

사용방법은 간단합니다. AbortController 생성자를 이용해 인스턴스를 만들고 해당 인스턴스 안에 들어있는 signal 데이터를 api 요청에 같이 보내고, 이후 해당 api 요청을 취소하고 싶으면 AbortController.abort() 메서드를 동작시키면 됩니다.

 

하지만 인터넷상에 동일한 컴포넌트 안에서 useEffect를 이용한 예문은 많지만,

 떨어진컴포넌트에서 abort() 메서드를 사용하는 예시가 없어 기능 구현에 꽤나 애를 먹었습니다.

 

다른 분들에게 조금이나마 도움이 되었으면 하며 포스팅을 남겨봅니다.

1. lambda-client api 요청에 controller signal 보내기

우선 lambda-client api 요청에 controller signal을 보내도록 하겠습니다.

const res = await lambdaClient.invoke({
  FunctionName: '함수명',
  InvocationType: 'RequestResponse',
  Payload: new TextEncoder().encode(JSON.stringify(obj)),
}, {
  abortSignal: controller?.signal
})

signal은 위처럼 두 번째 인자로 abortSignal 키값에 담아 보내면 됩니다.

2. useRef를 이용해 controller 인스턴스 선언 할당

const useSubmit = (): [() => Promise<void>] => {

  const dispatch = useAppDispatch()
  const filters = useAppSelector((state) => state.filter.filters)
  const isLoading = useAppSelector((state) => state.data.status.isLoading)
  const statusKey = useAppSelector((state) => state.data.status.statusKey)
  const controller = useRef(new AbortController())

  const submit = async() => {
    ...
  }

  useEffect(() => {
    if (!isLoading && !controller.current.signal.aborted && statusKey.length > 0) {
      controller.current.abort()
      controller.current = new AbortController()
    }
  }, [isLoading])

  return [submit]
}

export default useSubmit;

그리고 떨어진 컴포넌트에서는 버튼을 누르면 isLoading의 값을 false로 바뀌게끔 해주었습니다.

 

여기서 controller.current = new AbortController()로 초기화해준 이유는

반복해서 람다 request를 보낼때 다시 보내는 request가 abort()되는 것을 막기 위함입니다.