두개의 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 등 직접 하나하나 수동적으로 넣어줄 필요가 없고
관리하기 좋음