HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
💌
JJong’s Archive
/
Data Fetching

Data Fetching

생성 일시
Nov 20, 2024 12:30 AM
갈수록 진화하는 data fetching 과정을 살펴보자~
 

1. Client에서 data fetching (기존 리액트 방법)

"use client"; import { useEffect, useState } from "react"; export default function Movies() { const [movies, setMovies] = useState(""); const [isLoading, setIsLoading] = useState(true); const getMovies = async () => { const response = await fetch( "https://nomad-movies.nomadcoders.workers.dev/movies" ); const json = await response.text(); setMovies(json); setIsLoading(false); }; useEffect(() => { getMovies(); }, []); return <div>{isLoading ? <div>Loading ...</div> : movies}</div>; }
  1. React에서는 컴포넌트를 async 함수로 선언할 수 없다. 즉, useState, useEffect를 사용해서 클라이언트 컴포넌트로 만들어줘야 함
  1. 즉, fetch가 클라이언트에서 일어나기 때문에, api 등이 브라우저에 노출이 됨(보안 bad) - (2에서 해결)
  1. isLoading을 일일히 체크해줘야 함 - (3에서 해결)
 

2. Server에서 data fetching + 로딩처리

const getMovies = async () => { const response = await fetch( "https://nomad-movies.nomadcoders.workers.dev/movies" ); return response.text(); }; export default async function Movies() { const movies = await getMovies(); return <div>{movies}</div>; }
/movies/page.tsx
  1. 컴포넌트를 async 함수로 선언할 수 있다!
  1. 서버에서 fetch 하므로, 브라우저는 api 등 아무것도 모른다.
  1. NextJS에서는 캐싱도 자동으로 해준다! >> 같은 요청이면 fetch를 수행하지 않고, 캐싱된 데이터를 보여줌
  1. but, 이 서버 컴포넌트의 작업이 완료될 때까지 이 페이지로 넘어오지 못한다. (3에서 해결)
 

3. 로딩 화면 보여주기(loading.tsx)

데이터 패칭이 로딩중 때, loading.tsx 파일로 화면을 보여줄 수 있다.
const loading = () => { return <div>loading Movies ...</div>; }; export default loading;
/movies/loading.tsx
  • 파일 위치는 page.tsx와 같은 level에 있어야 한다
  • 로딩중일 때, layout만 우선 보여준다
 

4. 병렬적으로 fetching하기(Promise.all)

-before-
두개 이상의 fetch를 할 때, 단순히 fetch 코드를 연달아 쓰면, 순차적으로 진행되어 시간이 오래걸림.
import { moviesURL } from "../page"; export const getMovie = async (id: string) => { const response = await fetch(`${moviesURL}/${id}`); return response.json(); }; export const getVideos = async (id: string) => { const response = await fetch(`${moviesURL}/${id}/videos`); return response.json(); }; export default async function Movies({ params: { id }, }: { params: { id: string }; }) { const movie = await getMovie(id); const videos = await getVideos(id); return ( <h1> {movie.title} {JSON.stringify(videos)} </h1> ); }
 
-after-
모든 Promise 동시에 시작해보자~ ⇒ Promise.all(in JS)
const [movie, videos] = await Promise.all([getMovie(id), getVideos(id)]);
 
but,, 이것은 두 promise가 모두 끝나야 결과값을 얻을 수 있다.
 

먼저 끝난 fetching 결과를 먼저 보여주기(Suspense)

<Suspense> in React
  • 비동기 작업이 완료될 때까지 대체 UI를 표시해주는 기능
  • fallback : component가 await되는 동안 표시할 컴포넌트를 render해줌
  • 이는 사용자 경험을 개선하고, 특히 데이터 로딩이나 지연된 컴포넌트 로드 시 유용하게 사용할 수 있
 
한 page에 fetch 함수가 두개 이상 있던 것을
각각 다른 컴포넌트로 분리 → 부모 컴포넌트에서 suspense로 각 컴포넌트를 감싸서 렌더링
 
import { moviesURL } from "../(movies)/movies/page"; export const getMovie = async (id: string) => { const response = await fetch(`${moviesURL}/${id}`); return response.json(); }; const MovieInfo = async ({ id }: { id: string }) => { const movie = await getMovie(id); return <div>{movie.title}</div>; }; export default MovieInfo;
components/movie-info.tsx
import { moviesURL } from "../(movies)/movies/page"; export const getVideos = async (id: string) => { const response = await fetch(`${moviesURL}/${id}/videos`); return response.json(); }; const MovieVideos = async ({ id }: { id: string }) => { const videos = await getVideos(id); return <ul>{videos.map((video) => video.name)}</ul>; }; export default MovieVideos;
components/movie-videos.tsx
import { Suspense } from "react"; import MovieInfo from "../../../components/movie-info"; import MovieVideos from "../../../components/movie-videos"; export default async function Movies({ params: { id }, }: { params: { id: string }; }) { return ( <div> <h3>Movie detail page</h3> <Suspense fallback={<div>movie info Loading...</div>}> <MovieInfo id={id} /> </Suspense> <Suspense fallback={<div>movie videos Loading...</div>}> <MovieVideos id={id} /> </Suspense> </div> ); }
/movies/[id]/page.tsx
이렇게 하면 부분적으로 로딩, fetching이 일어난다.
이 때, 부모 단에서 loading.tsx이 있다면, 로딩이 일어나지 않는 부분(여기선 h3)도 로딩을 기다린다. 때문에 없애는 것이 좋다.
cf) error는 컴포넌트에서 일어나면 page에도 일어나는 것으로 error.tsx는 쓰임
 
즉,
Page 단위 로딩 ⇒ loading.tsx
서버 컴포넌트 단위 로딩 ⇒ Suspense