24.09.17

강연주·2024년 9월 16일

📚 TIL

목록 보기
42/186

💜 2024년 3/4분기 목표

  • ☑️ 9/9 ~ 9/20 커리어톤 이력서 완성반
  • 또는 11월 한 달 인턴(7기)
  • 12월 생일 전에 취업
  • 2025년 1월 1일까지 59kg

🍂 추석 이후 시작

  • 최종 프로젝트 리팩토링
  • ☑️ 사이드 프로젝트 고민
  • ☑️ 알고리즘/기술면접 루틴 짜기

useEffect안에서 데이터 fetching은 안티패턴

공식문서에 다 나와있는데 읽지도 않고 무지성으로 코드 짠 나...
친절하게 알려주신 현업 개발자님의 자비...💫

https://ko.react.dev/reference/react/useEffect

Effect에서 직접 데이터 페칭 로직을 작성하면
나중에 캐싱 기능이나 서버 렌더링과 같은 최적화 추가가 어렵다.
자체 제작된 커스텀 Hook이나,
커뮤니티에 의해 유지보수되는 Hook 사용이 더 간단!

1. Effect에서 데이터를 페칭하는 좋은 대안은 무엇인가요?

Effect 내부에서 fetch 호출을 작성하는 것은 클라이언트 사이드 앱에서 데이터를 페칭하는 가장 인기 있는 방법입니다. 하지만 이것은 매우 수동적인 접근 방식이며 큰 단점이 있습니다.

Effect는 서버에서는 실행되지 않습니다.
이는 초기 서버 렌더링 된 HTML이 데이터가 없는 state만을 포함한다는 것을 의미합니다.
클라이언트 컴퓨터는 모든 자바스크립트를 다운로드 받고
앱을 렌더링한 다음 데이터를 로드합니다.
이는 효율적이지 않을 수 있습니다.

Effect 내부에서 직접 페칭을 하는 것은
네트워크 폭포(network waterfalls)가 생성되기 쉽게 합니다.
부모 컴포넌트 렌더링 후 일부 데이터를 페칭하고 나서
자식 컴포넌트가 렌더링 됩니다. 이후 자식 컴포넌트가 자신의 데이터를 페칭하기 시작합니다. 네트워크의 속도가 빠르지 않다면 이 방법은 모든 데이터를 병렬로 페칭하는 것보다 훨씬 느립니다.

Effect 내부에서 직접 데이터를 페칭하는 것은 일반적으로
데이터를 미리 로드하거나 캐싱하지 않음을 의미합니다.
예를 들어 컴포넌트가 마운트 해제되고 다시 마운트되었을 때 데이터를 다시 가져와야 합니다.

사용하기 매우 불편한 방법입니다.
경쟁 조건과 같은 버그를 발생시키지 않도록 fetch 호출을 작성할 때 상당한 양의 보일러 플레이트 코드가 필요합니다.

이러한 단점은 리액트만 해당되는 것이 아닙니다. 다른 라이브러리를 사용하여 데이터를 페칭할 때도 해당됩니다.

2. 라우팅과 마찬가지로 데이터 페칭은 세부적인 사항이 많으므로 다음과 같은 접근 방식을 권장합니다.

  • 프레임워크를 사용하는 경우,
    해당 프레임워크에 내장된 데이터 페칭 메커니즘을 활용.
    현대 리액트 프레임워크는 매우 효율적이며 상기의 문제점이 없는 통합된 데이터 페칭 기능을 가지고 있습니다.
  • 그렇지 않은 경우,
    클라이언트 측 캐시를 사용하거나 직접 개발을 고려해 보세요. 인기 있는 오픈소스 솔루션으로는,
    React Query, useSWR, 그리고 React Router 6.4+.가 있습니다.
    물론 직접 솔루션을 개발할수도 있으며
    이 경우에는 이펙트를 내부적으로 사용하면서도
    데이터 사전로드 또는 데이터 요구사항을 라우트로 호이스팅하는 방법을 통해 중복 요청 방지, 응답 캐싱 및 네트워크 폭포 효과 방지를 구현할 수 있습니다.

만약 이러한 접근 방식이 적합하지 않다면 Effect 내부에서 데이터를 페칭하는 것을 계속 진행할 수 있습니다.

3. 언제 커스텀 Hook을 사용해야 하는지?

모든 자잘한 중복되는 코드들까지 커스텀 Hook으로 분리할 필요가 없습니다. 어떤 중복된 코드는 괜찮습니다.
예를 들어, 앞선 예시처럼
하나의 useState를 감싸기 위한 useFormInput을 분리하는 것은 불필요합니다.

하지만 Effect를 사용하든 사용하지 않든,
커스텀 Hook 안에 그것을 감싸는 게 좋은지 아닌지 고려하세요.
Effect를 자주 쓸 필요가 없을지 모릅니다.

만약 Effect를 사용한다면, 그건 외부 시스템과 동기화한다든가
React가 내장하지 않은 API를 위해 무언가를 하는 등
“React에서 벗어나기” 위함일 겁니다.
커스텀 Hook으로 감싸는 것은 목적을 정확하게 전달하고
어떻게 데이터가 그것을 통해 흐르는지 알 수 있게 해줍니다.

예를 들어 두 가지 목록을 보여주는 ShippingForm 컴포넌트를 살펴봅시다. 하나는 도시의 목록을 보여주고, 다른 하나는 선택된 도시의 구역 목록을 보여줍니다. 아마 코드를 다음과 같이 작성하기 시작할 겁니다.

function ShippingForm({ country }) {
  const [cities, setCities] = useState(null);
  // 이 Effect는 나라별 도시를 불러옵니다.
  useEffect(() => {
    let ignore = false;
    fetch(`/api/cities?country=${country}`)
      .then(response => response.json())
      .then(json => {
        if (!ignore) {
          setCities(json);
        }
      });
    return () => {
      ignore = true;
    };
  }, [country]);

  const [city, setCity] = useState(null);
  const [areas, setAreas] = useState(null);
  // 이 Effect 선택된 도시의 구역을 불러옵니다.
  useEffect(() => {
    if (city) {
      let ignore = false;
      fetch(`/api/areas?city=${city}`)
        .then(response => response.json())
        .then(json => {
          if (!ignore) {
            setAreas(json);
          }
        });
      return () => {
        ignore = true;
      };
    }
  }, [city]);

  // ...

이 코드들이 반복됨에도, Effect들을 따로 분리하는 것이 옳습니다.
그들은 다른 두 가지(도시, 구역)를 동기화합니다.
따라서 하나의 Effect로 통합시킬 필요가 없습니다.

대신 ShippingForm 컴포넌트를
useData라는 커스텀 Hook을 통해 공통된 로직을 추출할 수 있습니다.

function useData(url) {
  const [data, setData] = useState(null);
  useEffect(() => {
    if (url) {
      let ignore = false;
      fetch(url)
        .then(response => response.json())
        .then(json => {
          if (!ignore) {
            setData(json);
          }
        });
      return () => {
        ignore = true;
      };
    }
  }, [url]);
  return data;
}

이제 ShippingForm 컴포넌트 내부의 Effect들을 useData로 교체할 수 있습니다.

function ShippingForm({ country }) {
  const cities = useData(`/api/cities?country=${country}`);
  const [city, setCity] = useState(null);
  const areas = useData(city ? `/api/areas?city=${city}` : null);
  // ...

커스텀 Hook을 추출하는 것은 데이터의 흐름을 명확하게 해줍니다. url을 입력하고 data를 받습니다.

useData안의 Effect를 “숨김으로써” 다른 사람이 ShippingForm 컴포넌트에
불필요한 의존성을 추가하는 것을 막을 수 있습니다.
시간이 지나면 앱의 대부분 Effect들은 커스텀 Hook 안에 있을 겁니다.

profile
아무튼, 개발자

0개의 댓글