Suspense를 적용하려는 이유

  1. jotai, react-query를 사용했음에도 불구하고 삼항 연산자로 로딩 컴포넌트를 보여주고 있었기 때문에 해당 기술 스택들을 제대로 사용하지 못하는 것 같았다. 이 기술들을 사용하여 로딩 기능을 구현하고 싶었다.
  2. 이전에는 삼항 연산자로 로딩 페이지를 렌더링했다. DOM이 아예 사라졌다가 다시 나타나는 것이기 때문에 Layout 비용이 많이 들 수 있다.
  3. MainPage를 렌더링하고, useEffect 로 데이터를 가져온 후, 재렌더링하는 것은 비동기 요청이 병렬적으로 수행될 수 있다. 이 때문에 경쟁 상태(race conditions)에 취약할 수 있다.
    1. 현재 MainPage에서는 useEffectuseState 로 비동기 통신의 순서를 정해놓은 효과를 주고 있지만, 후에 비동기 통신이 추가 된다면 우리가 생각한 순서대로 데이터가 응답된다는 보장이 없어질 수도 있기 때문에 싱크가 맞지 않는 데이터를 제공할 수도 있다.
    2. Promise.all을 통해 해결할 수 있지만 React에서 Suspense라는 더 좋은 방식을 제공한다.
  4. 데이터 로딩과 UI 렌더링이라는 두 가지 역할이 MainPage 컴포넌트 안에서 복잡하게 얽혀있다.
    1. 나중에 코드가 더 많아지면 읽기 어려워지고 테스트 코드를 작성하기 어려울 것이다.

적용 전 상태

로딩 컴포넌트 렌더링

// MainPage.tsx

...

return (
	{isLoading ? (
    <MapLoading />
  ) : (
    <Map
      latitude={coordinates!.latitude}
      longitude={coordinates!.longitude}
    />

    ...

);

비동기 처리

// MainPage.tsx
useEffect(() => {
  // 사용자가 위치 정보 제공에 동의했을 때
  const success = (geolocationPosition: GeolocationPosition) => {

    ...

    setMapCenter(
      geolocationPosition.coords.latitude,
      geolocationPosition.coords.longitude
    );
  };

  // 사용자가 위치 정보 제공에 동의하지 않았을 때
  const error = () => {
    setCoordinates({ ...DEFAULT_COORDINATES });
  };

  navigator.geolocation.watchPosition(success, error);
}, []);
// MainPage.tsx
const setMapCenter = async (latitude: number, longitude: number) => {
  // 위도, 경도가 있는 경우 네이버 API에 주소 요청
  const usersLocationResponse: UsersLocationResponseTypes =
    await apis.getUsersLocation(latitude, longitude);
  const userLocation = usersLocationResponse.results[0].region.area1.name;

  ...

  setCoordinates({ ...DEFAULT_COORDINATES });
};

useEffect(() => {
  if (!coordinates) {
    return;
  }
  // 서울 중심 여부 혹은 사용자 위치 정보 허용 여부에 따라 다른 초기 위치 랜더링
  setIsLoading(false);
}, [coordinates]);
// App.tsx
...
function App() {
  return (
    <>
      <QueryClientProvider client={queryClient}>
        <GlobalStyle />
        <MainPage />
      </QueryClientProvider>
    </>
  );
}