[ Next.js 14 ] 08.parallel Requests Suspense

sohyun·2024년 2월 7일
0

Next.js14

목록 보기
8/9
post-thumbnail

두개의 api를 await 하고 있을 때
첫번째가 20초걸리고 두번째가 0.5초가 걸린다하더라도 총20.5초가
지난뒤에야 데이터를 볼 수 있음 (직렬적,순차적)

아래 이미지처럼 각각의 데이처패치 함수에 await를 걸고 log에 시간을 찍어
확인하면 각각 순차적으로 실행됨을 볼 수 있다.

이때, 병렬적으로 promise.all을 사용하여 원하는 함수를 동시에 await할 수 있음
promise.all이 끝나면 결과값으로 배열을 반환하기때문에
array 구조분해할당으로 값을 가져온다

이렇게 Promise.all을 사용하여 await를 병렬적으로 할 수 있다는 것을
위 이미지에서 동시에 찍힌 시각으로 확인할 수 있다.

차이를 보면 각각의 함수에서 5초뒤에 실행되므로 총 최소 10초가 걸렸는데
동시에 하기때문에 총 5초가 걸린다는것을 확인할 수 있다.
순차적인 방법이 아닌 병렬적으로 fetch하는 한 방법이다.

그런데 위의 방법으로 할 때 두개의 함수가 끝나야지만
각각의 데이터를 보여주는 UI를 볼 수 있다

그래서 두개의 함수가 완료되어야 UI를 한번에 볼 수 잇는게 아닌
각각의 함수가 완료되는대로 먼저 보여줄 UI들은 보여주도록 분리하는 작업이 필요!

데이터 소스가 여러개라면 Suspense를 사용하면 되는데

우선 비디오 데이터에 관한 컴포넌트를 만들어서 함수를 분리시켜준다.


// /components/movie-videos.tsx
import { API_URL } from "../app/(home)/page";

const getMovieDetaiVideo = async (id: string) => {
  console.log(`fetch Data Video ${Date.now()}`);
  await new Promise((resolve) => setTimeout(resolve, 3000));

  return await fetch(`${API_URL}/${id}/video`)
    .then((res) => res.json())
    .catch((err) => err);
};

const MovieVideos = async ({ id }: { id: string }) => {
  const videos = await getMovieDetaiVideo(id);
  return (
    <>
      <h6>{JSON.stringify(videos)}</h6>
    </>
  );
};

export default MovieVideos;

또 다른 데이터 패치 함수 movie info 데이터에 관한 컴포넌트를 만들어서 함수를 분리시켜준다.


// /components/movie-info.tsx
import { API_URL } from "../app/(home)/page";

const getMovieDetail = async (id: string) => {
  console.log(`fetch Data MovieDetail ${Date.now()}`);
  await new Promise((resolve) => setTimeout(resolve, 5000));

  return await fetch(`${API_URL}/${id}`)
    .then((res) => res.json())
    .catch((err) => err);
};

const MovieInfo = async ({ id }: { id: string }) => {
  const movie = await getMovieDetail(id);
  return (
    <>
      <h6>{JSON.stringify(movie)}</h6>
    </>
  );
};

export default MovieInfo;

페이지에서 두가지를 render할건데 개별적으로 기다리게 할 수 있다는 것

리액트에서오는 서스펜스로 감싸주면됨

//movieDetail

import { Metadata } from "next";
import MovieVideos from "../../../components/movie-viedos";
import MovieInfo from "../../../components/movie-info";
import { Suspense } from "react";

export const metadata: Metadata = {
  title: "movie Detail",
};

const MovieDetail = ({ params: { id } }: { params: { id: string } }) => {
  return (
    <>
      <h1>movie detail</h1>
      <Suspense fallback={<h3>movie info loading</h3>}>
        <MovieInfo id={id} />
      </Suspense>
      <Suspense fallback={<h3>movie video loading</h3>}>
        <MovieVideos id={id} />
      </Suspense>
    </>
  );
};
export default MovieDetail;

1.movieDetail페이지에서 두가지 데이터를 fetch해야했고
2.각각의 함수에 await를 걸면 순차적으로 데이터를 fetch하기 때문에
3.Promise.all을 사용하여 병렬적으로 데이터를 fetch했다.
4.그러나 동시에 실행되더라도 여전히 두 fetch가 준비되기를 기다려야만 UI를 볼 수 있었다.
5. 이를 각 데이터 fetch 함수를 컴포넌트로 분리했고
6. mocieDetail페이지에서 두 컴포넌트를 import하여 id를 내려준뒤
7. 각 컴포넌트를 Suspense를 사용하여 분리된 loading과 UI를 보여줄 수 있게됨
8. 이는 Suspense가 데이터를 fetch하기 위에 감싼 component를 await하는 거라고 보면됨
9.fetch 중에는 fallback을 보여주고 끝나면 UI를 렌더함
10. 이렇게하면 페이지가 즉시 load할 수 있다는 장점인것이다 ~~!!
11. 만약 페이지에서 두가지 데이터를 모두 fetch하고 있었더라면 loading.tsx를 사용하여 모든게 다 되어야 UI를 보여주었을 것
12. 페이지의 UI는 바로 보여주되, 구체적으로 페이지의 어느부분이 로딩상태인지 명시 가능해짐
13. 또한 최초의 한번은 로딩하여 보여주지만 또 다시 들어갈때 데이터가 캐싱되어 바로 보여줄 수도 있음

느낀점

개발자경험에 좋다는 것이 무엇인지 체감되는 프레임워크
isLoading에 setIsLoading 등 직접 하나하나 수동적으로 넣어줄 필요가 없고
관리하기 좋음

profile
냠소현 개발일지

0개의 댓글