[Next.js] Data Fetching

Miseon (MIMI)·2024년 4월 15일
post-thumbnail

🔖 개요

기존 React와 다르게, Next에서는 Server Side Data Fetching이 가능하다.

모든 것을 직접 관리해야 하는 React 방식에서, Next 프레임워크 방식으로 데이터를 편하게 받아보자.

🔖 Client Side Data Fetching

React에서의 Data Fetching 방식은 아래와 같다. 간단히 영화 목록을 불러오는 코드이다.

export default function HomePages() {
  const [isLoading, setIsLoading] = useState(true);
  const [movies, setMovies] = useState([]);

  const getMovies = async () => {
    const response = await fetch("https://nomad-movies.nomadcoders.workers.dev/movies");
    const data = await response.json();
    setMovies(data);
    setIsLoading(false);
  }

  useEffect(() => {
    getMovies();
  }, []);

  return (
    <div>
        { isLoading ? "Loading..." : JSON.stringify(movies) }
    </div>
  );
}

useEffect를 통해 최초 실행 시 데이터를 받고, useState를 통해 응답 데이터와 로딩 상태 등을 저장하여 관리하게 된다. 모든 것을 직접 관리해야 하는 방식이다.

또한 React 방식으로는, Data Fetching이 항상 Client에서 일어난다. 이는 브라우저 개발자도구에서 Network 탭의 API 요청 현황을 보면 알 수 있다. 따라서 API endpoint, password와 같은 secret key를 숨기기 어려워지기 때문에 직접 서버에 바로 요청하지 않고 API 서버를 거치는 구조가 보통이다.

React App <-> API <-> DataBase

🔖 Server Side Data Fetching

하지만 Next에서는 API를 거치지 않고 바로 DB와 통신할 수 있다. React의 Client Side가 아닌, Next의 Server Side로 데이터를 받아볼 수 있는 것이다.

아래 코드는 위 코드에서 Server Side Data Fetching을 적용한 것이다.

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

const URL = "https://nomad-movies.nomadcoders.workers.dev/movies";

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

export default async function HomePage() {
  const movies = await getMovies();

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

Client Side 방식과 다르게, useStateuseEffect를 통한 관리가 필요없어진다. 👍

또한, 브라우저가 Fetching하는 게 아니기 때문에 개발자도구의 Network 탭에서 확인할 수 없다. Next의 back-end에서 Fetching을 진행하기 때문이다.

다른 페이지에 있다가 해당 페이지로 돌아오더라도, 이미 캐싱된 데이터를 바로 보여주기 때문에 로딩 시간이 거의 없어진다.

📍 로딩 컴포넌트

Next의 프레임워크 기능을 통해 로딩 컴포넌트를 쉽게 제공할 수 있다.

Data Fetching이 존재하는 page.tsx 파일 옆에 loading.tsx 파일을 생성해주기만 하면 된다. (파일명은 반드시 'loading'이어야 한다.) 해당 로딩 파일을 통해 서버 컴포넌트로부터의 Data Fetching이 진행되는 동안 로딩 컴포넌트를 볼 수 있다.

브라우저는 Fetching이 완료될 때까지의 로딩을 진행하고 있지만, 유저는 Next에 의해 로딩 컴포넌트를 볼 수 있기 때문에 사용자 경험 측면에서 좋다.

📍 에러 컴포넌트

해당 파일이 존재하는 페이지에 에러가 발생하면 보여줄 컴포넌트를 지정할 수도 있다.

파일명은 반드시 'error'여야 한다.

export default function ErrorOMG() {
  return <div>OMG... Error!</div>;
}

🔖 Suspense

📍 Parallel Requests

const movie = await getMovie(id);
const videos = await getVideos(id);

위 코드는 movievideos를 가져올 때 순차 요청이 진행되므로 동기 문제가 발생한다.

이를 해결하기 위해서는 아래와 같이 Promise를 이용할 수 있다.

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

💡 아래 Promise 관련 포스팅을 통해 Promise를 알아보자! 😉

Promise.all을 이용하여 작업을 동시 요청할 수 있지만, 모든 Promise가 다 끝날 때까지 UI가 렌더링되지 않는 문제가 발생한다.

📍 Suspense

하나의 Promise라도 로딩이 끝난다면, 해당하는 UI가 바로 보이도록 해주는 것이 좋을 것이다. 이를 시각적으로 표현해주기 위해 Suspense 태그를 사용할 수 있다.

💡 Suspense는 Next가 아닌 React 내장 기능이다.

<div>
  <Suspense fallback={<div>Loading MovieInfo...</div>}>
    <MovieInfo id={id} />
  </Suspense>
  <Suspense fallback={<div>Loading MovieVideos...</div>}>
    <MovieVideos id={id} />
  </Suspense>
</div>

이렇게 Suspense를 통해 쉽게 로딩 상태를 분리할 수 있다. 또한, fallback 속성을 통해 로딩 중 띄워줄 컴포넌트를 지정할 수도 있다.

병렬적 요청을 선언적으로 할 수 있고, 하나의 요청이 완료되면 즉시 컴포넌트가 렌더링된다. 모든 요청이 완료되는 것을 기다릴 필요가 없어지기 때문에 전체 페이지가 로딩 상태가 아니게 된다.

Fetching을 진행하는 컴포넌트를 Suspense로 감싸줌에 따라서, 유저는 페이지 전체를 바로 볼 수 있지만, 각 Fetching 상태에 대해서도 알 수 있게 된다.

📍 Next에서의 Suspense

Suspense는 React가 제공하는 기능인데, Next에서 사용할 때의 장점은 무엇이 있을까?

SSR 환경에서 Suspense를 스트리밍 방식으로 사용할 수 있다는 점은 Next가 제공하는 기능이다. 또한, 서버 컴포넌트로 Fetching 최적화를 할 수 있다는 점이다.

profile
방황하는 개발자

0개의 댓글