Next.js

Next.js14 정리(2)

minsun309 2024. 8. 28. 08:58

Next.js14 정리(1) 이어서…

Parallel Requests

import { Link } from "next/link";

export const metadata = {
  title: "Home",
};

export const API_URL = "https://.../movies";

async function getMovies() {
  const response = await fetch(API_URL);
  const json = await response.json();
  return json;
}

export default async function HomePage() {
  const movies = await getMovies();
  return (
    <div>
      {movies.map((movie) => (
        <li key={movie.id}>
          <a href={`movies/${movie.id}`}>{movie.title}</a>
        </li>
      ))}
    </div>
  );
}

 

 

movies/[id]

여러 api 불러올 때 병렬 처리 Promise.all 함수로 불러올 수 있다.

단점 : 모든 api가 불러와져야 화면을 보여준다.

import { API_URL } from "../../../(home)/page";

async function getMovie(id) {
  await new Promise((resolve) => setTimeout(resolve, 1000));
  const res = await fetch(`${API_URL}/${id}`);
  return res.json();
}

async function getVideos(id) {
  await new Promise((resolve) => setTimeout(resolve, 1000));
  const res = await fetch(`${API_URL}/${id}/videos`);
  return res.json();
}

export default async function Movie({ params: { id } }) {
  const [movie, videos] = await Promise.all([getMovie(id), getVideos(id)]);

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

 

 

Suspense

두 api가 동시에 불러오지만 준비가 먼저 끝난 거는 먼저 보여준다.

⇒ 병렬적으로 2가지를 동시에 fetch할 수 있는데 하지만 하나의 요청이 완료되면 즉시 component가 render 된다.

⇒ 각각 api를 불러오는 컴포넌트로 만들어 개별적으로 기다리게 한다.

 

Suspense 가 데이터를 fetch 하기 위해 안의 컴포넌트를 await한다.

Suspense 의 fallback 컴포넌트가 await 되는 동안 (fetch 중에)표시할 메세지를 render할 수 있게 해준다.

⭐여기서 이제 데이터 패치를 하지 않기 때문에 ( 각 컴포넌트에서 데이터 패치 ) 형제 페이지인 loading.jsx는 활동하지 않음

//movies/[id]
import { Suspense } from "react";
import MovieInfo from "../../../../components/movie-info";
import MovieVideo from "../../../../components/movie-videos";

export default async function Movie({ params: { id } }) {
  return (
    <div>
      <Suspense fallback={<h1>Loading Info</h1>}>
        <MovieInfo id={id} />
      </Suspense>
      <Suspense fallback={<h1>Loading video</h1>}>
        <MovieVideo id={id} />
      </Suspense>
    </div>
  );
}

 

 

components/movie-info.jsx

import { API_URL } from "../app/(home)/page";

async function getMovie(id) {
  await new Promise((resolve) => setTimeout(resolve, 1000));
  const res = await fetch(`${API_URL}/${id}`);
  return res.json();
}
export default async function MovieInfo({ id }) {
  const movie = await getMovie(id);

  return (
    <div>
      <h6>{JSON.stringify(movie)}</h6>
    </div>
  );
}

 

 

components/movie-videos.jsx

import { API_URL } from "../app/(home)/page";

async function getVideos(id) {
  await new Promise((resolve) => setTimeout(resolve, 1000));
  const res = await fetch(`${API_URL}/${id}/videos`);
  return res.json();
}

export default async function MovieVideo({ id }) {
  const videos = await getVideos(id);

  return (
    <div>
      <h6>{JSON.stringify(videos)}</h6>
    </div>
  );
}

 

 

Error Handling

에러가 발생할 경우(ex, 데이터 패치 오류) 보여줄 페이지

movies/[id] 폴더에 error.jsx 파일 생성 ⇒ 해당하는 페이지 옆에 만들어야 한다.

error 컴포넌트에는 "use client" 넣어야 한다.

"use client";
export default function Error() {
  return (
    <div>
      <h1>Error</h1>
    </div>
  );
}

 

 

Dynamic Metadata

async function 처럼 generateMetadata에 params를 받아 올 수 있어 동적 메타데이터를 적용 할 수 있다.

import { Suspense } from "react";
import MovieInfo, { getMovie } from "../../../../components/movie-info";
import MovieVideo from "../../../../components/movie-videos";

export async function generateMetadata({ params: { id } }) {
  const movie = await getMovie(id);
  return {
    title: movie.title,
  };
}

export default async function Movie({ params: { id } }) {
  return (
    <div>
      <h3>Detail</h3>
      <Suspense fallback={<h1>Loading Info</h1>}>
        <MovieInfo id={id} />
      </Suspense>
      <Suspense fallback={<h1>Loading video</h1>}>
        <MovieVideo id={id} />
      </Suspense>
    </div>
  );
}