본문 바로가기
학습 정리(공식문서,강의)/Next.js

10. 데이터 페칭과 캐싱

by 맨날개발 2024. 12. 16.
오늘 정리한 내용은 공식 문서 학습 후 정리한 내용입니다.

 

🎈 서버 데이터 페칭

데이터를 가져오기 위해서는 일반적으로 fetch API를 사용하거나 DB에 직접 접근해서 가져올 수 있다. 이 두가지 방법 모두 기본값은 캐싱을 사용하지 않는다.

 

아래와 같이 fetch API를 사용해서 데이터를 가져올 수 있다. 이때 next build를 통해 빌드를 하게 되면 아래 페이지는 정적 페이지로 프리렌더된다. 그래서 해당 페이지에 아무리 접근해도 더이상 fetch API가 호출되지 않기 때문에 매번 동일한 결과만 보인다.

 

export default async function Page() {
  const data = await fetch('http://localhost:3000/blog')
  const posts = await data.json()
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

 

그래서 이런 정적 페이지 생성을 막기 위해서 아래와 같은 코드를 해당 파일 내부에 추가해주면 된다.

export const dynamic = 'force-dynamic'

 

 

또다른 방법은 페이지 컴포넌트 내부에서 cookies, headers 함수를 호출하거나 페이지 props로부터 searchParams를 읽어들이면 동적 페이지를 만들 수 있다.

// cookies 함수 호출
import { cookies } from 'next/headers'

export default async function Home() {
  const cookieStore = await cookies();
  const date = new Date();
  return <div>{date.toISOString()}</div>
}

// searchParams 읽기
export default function Home({ searchParams }) {
  return <div>{searchParams.name}</div>
}

 

✨ 이때 cookies, headers 함수만 호출 하고 값은 사용하지 않아도 동적으로 페이지가 생성된다.

 

 

👓 클라이언트에서 데이터 페칭

클라이언트에서 데이터를 가져올 땐 useEffect 내부에서 fetch를 사용할 수 있다. 하지만 이 방법은 추천하지 않는다. SWR, React Query 등과 같은 리액트 라이브러리를 사용하는 것을 추천한다.

 

 

🛒 unstable_cache 사용하기

unstable_cache API를 사용해서 next build시 해당 페이지를 프리렌더할 수 있도록 캐싱할 수 있다.

import { unstable_cache } from "next/cache"

const getDate = unstable_cache(
  async () => {
    return new Date().toISOString();
  },
  ['photo'],
  { revalidate: 5, tags: ['photo'] }
)

export default async function Home({ searchParams }) {
  const date = await getDate();
  return <div>{date}</div>
}

 

revalidate(초)를 지정하여 일정 시간동안 캐싱할 수 있다. 태그를 활용해서 해당 캐시를 무효화할 수도 있다.

 

 

📯 fetch API에서 캐시

fetch API는 일반적인 자바스크립트에서 제공하는 fetch를 확장한 것이다. 그래서 nextjs에서는 캐싱을 사용할 수 있는 옵션을 제공한다.

 

아래와 같이 fetch의 옵션으로 cache를 사용해서 force-cache를 값으로 전달하면 캐시를 사용할 수 있다.

const res = await fetch(`http://localhost:3000/blog/${id}`, {
  cache: 'force-cache',
})

 

✨ 15버전부터 cache: no-store가 기본값이 되었다. 그 이전엔 cache: force-cache가 기본값이었다.

 

 

🚩 리액트 cache

리액트에서 제공하는 cache를 활용해볼 수 도 있다. fetch를 사용하지 않고 캐싱을 하고 싶다면 활용해볼 수 있다. 이는 단일요청에 대해서 중복으로 동일한 함수를 호출하는 경우 한번만 호출하도록 해준다.

import { cache } from 'react';

const customCache = cache(() => {
  return value;
});

 

 

 순차적 데이터 패칭

이름에서 알 수 있듯이 데이터를 순차적으로 가져오는 것을 의미한다. 이미 데이터 요청 진행중인경우에는 대기하였다가 완료 된 후 다음 요청을 진행하게 된다. 그래서 시간이 오래걸리는 문제가 존재한다.

 

하지만 이전 요청의 결과를 가지고 다음 요청을 해야하는 경우에는 해당방법을 사용해야 한다. 이 경우 모든 컨텐츠가 로드 되기까지 오래걸리기 때문에 loading.js와 Suspense를 활용해서 나머지 결과를 미리 보여줄 수 있다.

export default async function Page() {
  const post = await getPost()
 
  return (
    <>
      <h1>{post.name}</h1>
     
      <Suspense fallback={<div>Loading...</div>}>
        <Comments postId={post.id} />
      </Suspense>
    </>
  )
}
 
async function Comments({ postId }) {
  const comments = await getComments(postId);
 
  return (
    <ul>
      {comments.map((comment) => (
        <li key={comment.id}>{comment.content}</li>
      ))}
    </ul>
  )
}

 

 

💊 병렬 데이터 페칭

순차 데이터 페칭의 문제를 해소하여 데이터를 좀 더 빠르게 호출할 수 있다. 기본적으로 레이아웃과 페이지를 병렬로 가져오게 된다. 

일반적으로 병렬로 데이터를 가져오는 경우 await를 사용하지 않고 Promise.all을 통해 병렬 요청 후 두 요청이 완료 된 후 처리하는 방법을 사용한다.

async function getNotice() {
  const res = await fetch(`http://localhost:3000/notice`)
  return res.json()
}
 
async function getBoard() {
  const res = await fetch(`http://localhost:3000/board`)
  return res.json()
}
 
export default async function Page() {
  const noticeData = getNotice()
  const boardData = getBoard()
 
  const [notice, board] = await Promise.all([noticeData, boardData])
 
  return (
    <>
      //...
    </>
  )
}

 

위의 방법을 사용하면 먼저 데이터 요청이 완료 된 것을 보여줄 수 없다는 문제가 있다.

✨ Suspense를 활용할 수 있도록 컴포넌트를 분리, 재구성하는 방법이 있다.

 

 

🧵 데이터 프리로딩

현재 요청의 다음 요청을 미리 호출함으로써 프리로딩할 수 있다. 현재 요청이 await이 걸려 있어 대기해야하기 때문에 다음 요청을 비동기로 호출하여 데이터를 미리 캐싱한 후 실제 요청이 되었을 때 캐싱된 결과를 바로 사용하게 하는 방법이다.

export default async function Page() {
  preload();
  
  const blocking = await blockingFn();
  
  return <Item />
}


const preload = () => {
  void getItem();
}

async function Item() {
  const result = await getItem();
}

 

✨ preload 이름은 무엇으로 정하든 상관없다.

'학습 정리(공식문서,강의) > Next.js' 카테고리의 다른 글

12. ISR  (0) 2024.12.26
11. 서버 액션  (0) 2024.12.24
9. 미들웨어  (0) 2024.12.09
8. 리다이렉트와 라우트 핸들러  (0) 2024.12.06
7. Linking and Navigating  (0) 2024.12.03