import { API_URL } from "../../../(home)/page";
async function getMovie(id: string) {
console.log(`Fetching movies: ${Date.now()}`);
const response = await fetch(`${API_URL}/${id}`);
console.log(`Finished movies: ${Date.now()}`);
return response.json();
}
async function getVideos(id: string) {
console.log(`Fetching videos: ${Date.now()}`);
const response = await fetch(`${API_URL}/${id}/videos`);
console.log(`Finished videos: ${Date.now()}`);
return response.json();
}
export default async function MovieDetail({ params: { id } }: { params: { id: string } }) {
const movie = await getMovie(id);
const videos = await getVideos(id);
return <h1>{movie.title}</h1>;
}
이전에 서버에서 fetching하는 것을 이용하여 여러 요청을 보내보았다.
이 방법은 데이터 요청 2개를 직렬으로 연결하여 최대시간이 걸리게 된다.
import { API_URL } from "../../../(home)/page";
async function getMovie(id: string) {
console.time("Fetching movies");
await new Promise((resolve) => setTimeout(resolve, 5000));
const response = await fetch(`${API_URL}/${id}`);
console.timeEnd("Fetching movies");
return response.json();
}
async function getVideos(id: string) {
console.time("Fetching videos");
await new Promise((resolve) => setTimeout(resolve, 1000));
const response = await fetch(`${API_URL}/${id}/videos`);
console.timeEnd("Fetching videos");
return response.json();
}
export default async function MovieDetail({ params: { id } }: { params: { id: string } }) {
const movie = await getMovie(id);
const videos = await getVideos(id);
return <h1>{movie.title}</h1>;
}
극단적인 예로 영화데이터는 5초가 걸리고 영상데이터는 1초가 걸리는 요청이라고 가정해보자.
두가지 요청을 완수하고 사용자에게 정상적인 페이지를 보여주기까지 6초가 걸리는 것이다.
따라서 우리는 병렬적으로 fetching하기위해 Promise.all을 사용해보자.
import { API_URL } from "../../../(home)/page";
async function getMovie(id: string) {
console.time("Fetching movies");
await new Promise((resolve) => setTimeout(resolve, 5000));
const response = await fetch(`${API_URL}/${id}`);
console.timeEnd("Fetching movies");
return response.json();
}
async function getVideos(id: string) {
console.time("Fetching videos");
await new Promise((resolve) => setTimeout(resolve, 1000));
const response = await fetch(`${API_URL}/${id}/videos`);
console.timeEnd("Fetching videos");
return response.json();
}
export default async function MovieDetail({ params: { id } }: { params: { id: string } }) {
console.log("-------");
console.log("start fetching");
const [movie, videos] = await Promise.all([getMovie(id), getVideos(id)]);
console.log("finished fetching");
return <h1>{movie.title}</h1>;
}
콘솔로그만 본다면 6초가 걸렸을 것이라고 생각이 들 수도 있지만, 두 요청을 한번에 보내 5.013초가 최종적으로 걸린 것이다.
import { API_URL } from "../app/(home)/page";
async function getMovie(id: string) {
console.time("Fetching movies");
await new Promise((resolve) => setTimeout(resolve, 5000));
const response = await fetch(`${API_URL}/${id}`);
console.timeEnd("Fetching movies");
return response.json();
}
export default async function MovieInfo({ id }: { id: string }) {
const movie = await getMovie(id);
return <h6>{JSON.stringify(movie)}</h6>;
}
import { API_URL } from "../app/(home)/page";
async function getVideos(id: string) {
console.time("Fetching videos");
await new Promise((resolve) => setTimeout(resolve, 3000));
const response = await fetch(`${API_URL}/${id}/videos`);
console.timeEnd("Fetching videos");
return response.json();
}
export default async function MovieVideos({ id }: { id: string }) {
const videos = await getVideos(id);
return <h6>{JSON.stringify(videos)}</h6>;
}
이렇게 요청하고 데이터를 보여주는 async 컴포넌트를 만들어준다.
import { Suspense } from "react";
import MovieInfo from "../../../../components/movie-info";
import MovieVideos from "../../../../components/movie-videos";
export default async function MovieDetail({ params: { id } }: { params: { id: string } }) {
return (
<div>
<Suspense fallback={<h1>Loading Movie Info</h1>}>
<MovieInfo id={id} />
</Suspense>
<Suspense fallback={<h1>Loading Movie Video</h1>}>
<MovieVideos id={id} />
</Suspense>
</div>
);
}
그 컴포넌트들을 각각 react의 Suspense로 감싸주고, fallback 속성에 해당 컴포넌트가 로딩중일 때 보여줄 컴포넌트를 넣어준다.
Promise.all을 사용하면 가장 긴 요청이 끝나고서야 loading.tsx 컴포넌트가 사라지고, 원하는 page컴포넌트가 화면에 출려되었는데, 이렇게 Suspense를 사용하면 각각의 요청이 끝날때마다 데이터를 화면에 보여줄 수 있어 TTV를 빠르게 만들 수 있다.
import { API_URL } from "../app/(home)/page";
async function getVideos(id: string) {
await new Promise((resolve) => setTimeout(resolve, 3000));
throw new Error("somthing Broken!");
// const response = await fetch(`${API_URL}/${id}/videos`);
// return response.json();
}
export default async function MovieVideos({ id }: { id: string }) {
const videos = await getVideos(id);
return <h6>{JSON.stringify(videos)}</h6>;
}
영화 영상을 받아오는 함수에서 에러가 난다고 가정해보자.
유저에게 이런 화면을 보여줄 필요는 없지 않을까?
물론 저 화면은 dev환경에서 보이는 것이고, 유저에겐 흰 화면만 보일 것이다.
따라서 우리는 에러상황의 예외처리를 해야한다.
error이라는 파일을 해당 페이지 폴더에 만들어주자.
그리고 use client는 꼭 작성해야한다.
'2차 공부 > TIL' 카테고리의 다른 글
24.08.26 챌린지반 아티클 -3 / 자바스크립트의 History API (0) | 2024.08.27 |
---|---|
24.08.26 Next JS CSS (0) | 2024.08.26 |
24.08.26 Next JS Data Fetching / loading (0) | 2024.08.26 |
24.08.23 NextJS 동적라우팅 (1) | 2024.08.23 |
24.08.22 Next.js Layout / metadata (0) | 2024.08.22 |