[Next.js App Router] DB 생성, 데이터 불러오기, 렌더링 전략

이희령·2023년 10월 30일
1

[Chapter 6] Setting Up Your Database

레파지토리를 Vercel에 연결해서 배포하기

  • Vercel에 새로운 프로젝트를 생성해서 Github 레파지토리 연결 후 배포한다.

DB 생성하기

  • Vercel Storage 탭에서 Postgres database를 생성한다.
  • 이때 Region을 내 위치와 가까운 국가로 설정하면 데이터 호출 시 latency를 줄일 수 있다. (Settings 탭에서 Functions > Function Region까지 설정하는 것을 추천한다.)
  • Postgres database 관련 코드를 루트 디렉토리의 .env 파일에 추가한다.

DB에 초기 데이터 넣기

"scripts": {
  "build": "next build",
  "dev": "next dev",
  "start": "next start",
  "seed": "node -r dotenv/config ./scripts/seed.js"
},
  • package.json 파일에 seed 명령어를 추가한다.
  • npm run seed를 실행하면 SQL를 이용해 테이블들을 생성하고 placeholder-data.js의 데이터를 이 테이블들에 초기 데이터로 채워넣는다.

[Chapter 7] Fetching Data

데이터를 불러오는 방법

  • 데이터를 클라이언트에서 불러온다면 DB 관련 민감한 코드가 노출되는 것을 막기 위해 서버에서 실행되어 애플리케이션과 DB 사이를 중계하는 API layer를 사용하는 것이 좋다.
  • Postgres와 같은 관계형 데이터베이스의 경우 SQL이나 Prisma와 같은 ORM을 사용하여 Database queries를 수행할 수 있다.
  • 만약 React Server Components를 사용한다면 API layer를 사용하지 않더라도 코드가 노출될 위험 없이 DB를 직접적으로 query 할 수 있다. (Database queries)

서버 컴포넌트로 데이터를 불러오는 것의 장점

  • 서버 컴포넌트는 서버에서 실행되기 때문에 값 비싼 데이터 fetching 등의 로직은 서버에 맡기고 클라이언트에는 결과만을 전송할 수 있다.
  • 서버 컴포넌트는 promises를 지원하기 때문에 비동기적으로 데이터를 fetching 할 수 있다. useEffect, useState, 혹은 다른 라이브러리를 사용하지 않고도 async/await 문법을 사용할 수 있다.
  • 서버 컴포넌트는 서버에서 실행되기 때문에 추가적인 API layer 없이 직접적으로 DB를 query 할 수 있다.

Dashboard 페이지에 데이터 불러오기

import RevenueChart from '@/app/ui/dashboard/revenue-chart';
import { fetchRevenue } from '../lib/data';
 
export default async function Page() {
  const revenue = await fetchRevenue();
  
  return (
    // ...
    <RevenueChart revenue={revenue}  />
	// ...
  );
}
  • Pageasync 컴포넌트이기 때문에 await를 이용해서 데이터를 불러올 수 있다.
  • 하지만 현재 코드에서는 데이터 요청들이 의도치 않게 서로를 막고 있기 때문에 request waterfall이 발생한다.

request waterfalls란?

  • waterfall은 이전 요청의 완료에 따라 결정되는 일련의 네트워크 요청을 의미한다
  • 데이터를 불러오는 경우 이전의 리퀘스트가 데이터를 return 했을 때만 다음 리퀘스트를 시작할 수 있다.
const revenue = await fetchRevenue();
const latestInvoices = await fetchLatestInvoices(); // wait for fetchRevenue() to finish
  • 예를 들어 상단의 코드에서 fetchRevenue()가 완료될 때까지 fetchLatestInvoices()를 실행할 수 없다.

병렬적으로 데이터 불러오기

export async function fetchCardData() {
  try {
    const invoiceCountPromise = sql`SELECT COUNT(*) FROM invoices`;
    const customerCountPromise = sql`SELECT COUNT(*) FROM customers`;
    const invoiceStatusPromise = sql`SELECT
         SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS "paid",
         SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) AS "pending"
         FROM invoices`;
 
    const data = await Promise.all([
      invoiceCountPromise,
      customerCountPromise,
      invoiceStatusPromise,
    ]);
    // ...
  }
}
  • waterfalls를 방지할 수 있는 가장 흔한 방법은 모든 데이터를 병렬적으로, 즉 동시에 요청하는 것이다.
  • 자바스크립트에서는 모든 promises를 동시에 시작하기 위해 Promise.all() 혹은 Promise.allSettled()를 사용할 수 있다.
  • 모든 데이터를 동시에 불러옴으로써 성능을 향상시킬 수 있다.

[Chapter 8] Static and Dynamic Rendering

Static Rendering이란?

  • Static Rendering을 사용하면 빌드 타임(배포 시) 혹은 revalidation 하는 동안 서버에서 데이터를 fetching하고 렌더링한다.
  • 결과물은 CDN(Content Delivery Network)에 분배되고 캐싱된다.
  • Static Rendering블로그 글, 제품 페이지처럼 모든 사용자에게 공유되는 데이터를 제공할 때 유용하지만, 데이터가 정기적으로 업데이트되는 대시보드에는 적합하지 않다.
  • Next.js에서는 default 렌더링 방식으로 Static Rendering을 사용한다.

Static Rendering의 장점

  1. 웹사이트 속도 향상: 미리 렌더링된 콘텐츠는 캐싱되기 때문에 전세계의 사용자들은 웹 사이트의 콘텐츠에 보다 빠르고 안정적으로 접근할 수 있다.
  2. 서버 부담 감소: 콘텐츠가 캐싱되기 때문에 서버는 모든 사용자 요청에 콘텐츠를 동적으로 생성할 필요가 없다.
  3. SEO: 미리 렌더링된 콘텐츠는 페이지가 로드될 때 이미 사용 가능하기 때문에 검색 엔진 크롤러가 색인하기 더 쉽다.

Dynamic Rendering이란?

  • Dynamic Rendering을 사용하면 사용자가 요청할 때마다(사용자가 페이지에 방문할 때마다) 콘텐츠가 서버에서 렌더링된다.
  • Dynamic Rendering은 데이터가 자주 업데이트되는 애플리케이션에 적합하다.
import { unstable_noStore as noStore } from 'next/cache';
 
export async function fetchRevenue() {
  // Add noStore() here to prevent the response from being cached.
  // This is equivalent to in fetch(..., {cache: 'no-store'}).
  // This request should be refetched on every request.
  noStore();
  // ...
}
  • data fetching 시에 {cache: 'no-store'}를 설정하면 모든 리퀘스트마다 데이터를 refetch하는 방식으로 Dynamic Rendering를 사용할 수 있다.

Dynamic Rendering의 장점

  1. 실시간 데이터: Dynamic rendering을 사용하면 실시간 혹은 자주 업데이트되는 데이터를 제공할 수 있다.
  2. 사용자별 데이터 제공: 데이터가 사용자의 상호작용에 따라 업데이트되기 때문에 개인별 대시보드나 사용자 프로필 정보와 같이 사용자별로 데이터를 제공하기에 좋다.
  3. 리퀘스트 시 정보 제공: 쿠키나 URL search params와 같이 리퀘스트 시에만 알 수 있는 정보에 접근할 수 있다.
profile
Small Steps make a Big Difference.🚶🏻‍♀️

0개의 댓글