Parallel Request

Eugenius1st·2025년 7월 13일
0

NextJS

목록 보기
8/11

Parallel Request

import { API_URL } from "../../../(home)/page";
async function getMovie(id: string) {
  console.log(`getMovie: ${Date.now()}`);
  await new Promise((resolve) => setTimeout(resolve, 5000));
  const response = await fetch(`${API_URL}/${id}`);
  const json = await response.json();
  return json;
}
async function getVideo(id: string) {
  console.log(`getVideo: ${Date.now()}`);
  await new Promise((resolve) => setTimeout(resolve, 5000));
  const response = await fetch(`${API_URL}/${id}/videos`);
  const json = await response.json();
  return json;
}
export default async function Text({ params }) {
  const { id } = await params;
  console.log("startfetching");
  const movie = await getMovie(id); // 이 함수 응답이 오래걸리면
  const video = await getVideo(id); // 첫줄 함수를 기다리다가 응답이 늦는다. 병렬로 실행해야 한다.
  console.log("endfetching");

  return (
    <div>
      testestest
      <h1>{movie.title}</h1>
    </div>
  );
}

이렇게 하면
start fetching 이 출력되고 end fetching이 순차적으로 출력되며
총 10초 넘게 걸리게 된다.

startfetching
getMovie: 1752386606417
getVideo: 1752386607056
endfetching

병렬 요청으로 바꿔줘야 한다.

promise.all을 사용하면 결과값이 동시에 나올 것이다.

const [movie, videos]=  await Promise.all([getMovie(id), getVideos(id)])

이렇게 요청을 보내면

startfetching
getMovie: 1752386815316
getVideo: 1752386815316 
endfetching

동시에 시작하게 된다.
순차적인 작업이 아니기 때문이다. 이것이 병렬 fetch 이다.

하지만 지금은 둘이 끝나야 보이므로, 둘을 분리하는 것이 좋다.
둘중 어느 한쪽도 기다릴 필요가 없게! 그것이 suspense이다.

Suspense

getMovie와 getVideos 만 렌더링 하는 컴포넌트를 만든다.
/components/movie-video.tsx
/components/movie-info.tsx

// 1. components.movie-video.tsx
import { API_URL } from "../app/(home)/page";
async function getVideos(id: string) {
  console.log(`getVideo: ${Date.now()}`);
  await new Promise((resolve) => setTimeout(resolve, 5000));
  const response = await fetch(`${API_URL}/${id}/videos`);
  const json = await response.json();
  return json;
}

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

//2. components/movie-video.tsx
import { API_URL } from "../app/(home)/page";
async function getMovie(id: string) {
  console.log(`getMovie: ${Date.now()}`);
  await new Promise((resolve) => setTimeout(resolve, 3000));
  const response = await fetch(`${API_URL}/${id}`);
  const json = await response.json();
  return json;
}

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

를 만들고 개별적으로 기다리게 한다.
그리고 page.tsx에서 MovieInfo 와 MovieVideos를 렌더링한다.

import { Suspense } from "react";
import MovieDetail from "../../../../components/movie-info";
import MovieVideos from "../../../../components/movie-video";
export default async function Text({ params }) {
  const { id } = await params;



  return (
    <div>
      <Suspense fallback={<h1>Loading Movie Info</h1>}>
        <MovieDetail id={id} />
      </Suspense>
      <Suspense fallback={<h1>Loading Movie Video</h1>}>
        <MovieVideos id={id} />
      </Suspense>
    </div>
  );
}

그럼 순차적으로 데이터가 준비되는 순간 사용자는 렌더링된 데이터를 볼 수 있다.
그리고 page.tsx 와 같은 위치에 있는 loading.tsx 는 최적화 한 덕분에 await 하는게 없으므로 전체 로딩하는 일이 없게 된다.

page 단위 로딩: loading.tsx
서버컴포넌트 단위 로딩: Suspense


Next15) 15버전 부터는 기본 캐싱이 안되기 때문에 캐싱을 확인하려면 fetch의 두번째 인자 cache옵션을 force-cache 로 바꿔줘야 한다고 한다.

profile
최강 프론트엔드 개발자가 되고싶은 안유진 입니다

0개의 댓글