Dev Diary

useEffect 내에서 비동기 작업 처리하기 본문

Trouble Shooting

useEffect 내에서 비동기 작업 처리하기

sik9252 2023. 11. 27. 05:25
SMALL

api 요청에 react-query v4를 사용하고 있는데 useQuery를 이용한 api 요청이 계속 실행되는것을 방지하기 위해 refetch 함수를 사용하여 특정 상황에만 쿼리가 요청될 수 있도록 구현하는 도중, useEffect 안에 비동기 요청 함수를 넣어 전역상태에 값을 세팅해줘야 하는 상황이 생겼다.

 

그래서 매번 사용했던대로 useEffect 안에서 api 요청 함수를 수행하고 응답에 특정 프로젝트에 참가하고 있는 프리랜서들과 클라이언트(발주처)의 uid, name, role이 담긴 배열이 있었는데 클라이언트(발주처)인 경우에는 clientInfo 라는 전역상태에, 프리랜서인경우에는 freelancersInfo 전역상태에 각각 저장해주었다. 그런데 프리랜서의 경우 1명일수도 있지만 2명 이상일수도 있기 때문에 배열에 저장해주고 있었는데 프리랜서가 3명인경우 3명만 freelancersInfo에 저장되어야하는데 2배인 6개의 요소가 저장되는 현상이 발생했다.

 

freelancersInfo = [
  {
    id: 1,
    name: '프리랜서1',
    role: '개발'
  },
  {
    id: 2,
    name: '프리랜서2',
    role: '디자인'
  },
  {
    id: 3,
    name: '프리랜서3',
    role: '기획'
  }, // 여기까지만 담겨야하는데 아래처럼 똑같은게 1번 더 담김
  {
    id: 1,
    name: '프리랜서1',
    role: '개발'
  },
  {
    id: 2,
    name: '프리랜서2',
    role: '디자인'
  },
  {
    id: 3,
    name: '프리랜서3',
    role: '기획'
  }
 ]

 

왜 이런 현상이 발생하는 것인지 고민해보았는데 clientInfo와 freelancersInfo가 아직 세팅되지 않았을때만 refetch를 수행하여 이들 값을 세팅하기 위해 useEffect의 종속성 배열에 clientInfo, freelancerInfo를 넣어두었었는데 useEffect 안에 적어둔 api 요청이 실행되면서 두 전역상태의 값이 변경되기때문에 useEffect가 변경을 감지하여 다시 실행되기 때문에 항상 값이 1개로 고정되어있는 클라이언트(발주처)의 경우에는 2개가 저장된다던가 하는 상황이 발생하지 않았지만, 2명 이상이 존재할 수 있어서 배열로 저장되는 freelancersInfo와 같은 경우에는 응답 요소의 2배에 해당하는 요소들이 저장되고 있던 것이였다.

 

그래서 해결법에 대해 고민하다가 useEffect의 정리 함수를 사용해 한번 실행된 이후 종속성 배열에 의해 재실행되려고 하는 api 요청에 대한 응답값의 세팅은 무시하도록 하는 방법을 적용하기로 하였다.

 

아래는 해당 방법을 적용하며 정리한 내용들이다.


React의 useEffect Hook은 컴포넌트가 렌더링된 후 특정 작업을 수행하도록 설정하는데 사용된다. 그러나 useEffect 내에서 비동기 작업을 수행할 때는 몇 가지 주의사항이 있다.

 

1. useEffect의 종속성 배열

위의 내 경우처럼 특정 값이 변경될때마다 특정 로직을 수행하도록 하기 위해 useEffect의 두 번째 인자(종속성 배열)로 배열을 전달하는 경우가 있는데 이렇게 하면 배열 내의 값들이 변경될 때마다 useEffect가 재실행된다.

useEffect(() => {
  // Some effects...
}, [dependency1, dependency2]); // dependency1, dependency2가 변경될 때마다 useEffect가 재실행됨

 

그러나 제대로 사용하지 않으면 상태가 변경될 때마다 useEffect가 재실행되므로 상태가 무한히 변경되는 문제가 발생할 수 있다.

 

2. useEffect와 비동기 작업

이게 바로 내가 겪은 문제인데, useEffect 내에서 비동기 작업을 수행할 때는 비동기 작업이 완료되기 전에 useEffect가 다시 실행되거나 컴포넌트가 언마운트되는 경우를 고려해야 한다. 이런 경우 useEffect의 정리 함수를 사용하여 비동기 작업의 결과를 무시할 수 있다.

useEffect(() => {
  let isCancelled = false;

  asyncFunction()
    .then(result => {
      if (!isCancelled) {
        // Handle result
      }
    });

  return () => {
    isCancelled = true;
  };
}, []);

 

위처럼 isCancelled라는 취소 플래그를 사용하여 useEffect가 다시 실행되거나 컴포넌트가 언마운트될 때 비동기 작업의 결과를 무시하도록 처리하였다. 이렇게 하면 useEffect가 다시 실행되어도 이전의 비동기 작업이 완료되지 않은 상태에서 새로운 비동기 작업이 시작되는 것을 방지할 수 있다.

 

이제 이렇게 중복 없이 한번만 담기게 되었다!

freelancersInfo = [
  {
    id: 1,
    name: '프리랜서1',
    role: '개발'
  },
  {
    id: 2,
    name: '프리랜서2',
    role: '디자인'
  },
  {
    id: 3,
    name: '프리랜서3',
    role: '기획'
  }
 ]

 

LIST