Picture of the author
ROXIE.
DEV
Next.js로 나만의 블로그 만들기: Backend CMS 연결하기`mock data api`로 posts를 받아오는 방법에서 Backend CMS인 `Sanity`를 사용하여 api 통신 하는 방법에 대해 알아보자.
Next.js로 나만의 블로그 만들기: Backend CMS 연결하기

이번 포스팅에는 기존에 mock data api로 posts를 받아오는 방법에서 Backend CMS인 Sanity를 사용하여 api 통신 하는 방법에 대해 알아보겠다.

  • 기존 mock data 경로

    BASH
    1data 2├── posts 3│ └── example.md 4└── public
  • sanity client 설정 경로

    BASH
    1. 2├── app 3│ └── api 4│ └── posts 5│ └── route.ts 6├── service 7│ ├── posts.ts 8│ └── sanity.ts 9└── components

React에서 서비스 폴더를 별도로 분리하여 비즈니스 로직을 함수로 작성하는 패턴은 특정한 공식 명칭이 있는 것은 아니지만, 일반적으로 이런 구조를 "서비스 계층(Service Layer)" 또는 "서비스 오리엔테이션(Service-Oriented Architecture, SOA) 패턴"으로 참조할 수 있다. 이 패턴은 애플리케이션의 비즈니스 로직을 UI 컴포넌트로부터 분리하여 재사용성을 높이고, 유지 보수성을 개선하는 데 도움을 준다.

이렇듯 비즈니스 로직을 따로 함수로 사용하면 타입스크립트 사용시, 어떤 데이터인지 타입을 명확하게 정의 가능하며, 사용하는 곳에서도 타입을 안전하게 사용할 수 있어, 타입의 안전성이 전반적으로 높아진다.

비즈니스 로직 분리와 GROQ

Query Language (GROQ)는 Graph-Relational Object Queries의 약자로, Sanity에서 사용하는 오픈소스 쿼리 언어이다. 강력하고 직관적이라서 배우기 쉽고, 정확한 정보를 묘사하기 좋으며 조인쿼리가 가능하다.
기본적인 문법은 아래와 같다.

TYPESCRIPT
1// *[ 조건식 ] 2*[_type == 'movie' && releaseYear >= 1979] 3 4// *[ 조건식 ]{원하는 fields} 5*[_type == 'movie' && releaseYear >= 1979]{ _id, title, releaseYear } 6 7// *[ 조건식 | Sorting(기준) ]{원하는 fields} 8*[_type == 'movie' && releaseYear >= 1979] | order(releaseYear) { 9 _id, title, releaseYear 10} 11 12// *[ 조건식 | Sorting(기준) ]{원하는 fields}[시작 인덱스 번호...마지막 인덱스 번호] 13*[_type == 'movie' && releaseYear >= 1979] | order(releaseYear) { 14 _id, title, releaseYear 15}[0...100]

추가 필요한 쿼리를 찾는다면 공식문서 Query Cheat Sheet를 참고하면 된다.

TYPESCRIPT
1// service/posts.ts 2import { client } from "./sanity"; 3 4export type Post = { 5 id: string; 6 createdAt: Date; 7 updatedAt: Date; 8 category: string; 9 description: string; 10 featured: boolean; 11 path: string; 12 title: string; 13}; 14 15export async function getFeaturedPosts(): Promise<Post[]> { 16 // moc data api 17 // return getAllPosts() // 18 // .then((posts) => posts.filter((post) => post.featured)); 19 return client.fetch(` 20 *[_type =="post" && (featured == true || defined(featured))] | order(_createdAt desc){ "id": _id, "createdAt": _createdAt, 21 "updatedAt": _updatedAt, category, description, featured, path, title }`); 22}

지금까지 service폴더를 생성하여 비즈니스 로직을 따로 분리한 이유에 대해 알아보았다. 다음으로, sanity cloud 접속 정보 설정하는 방법에 대해 알아본다.

sanity client 설정

@sanity/client공식 문서를 참고하여 sanity client 설치 후 관련 정보를 설정한다.

BASH
1$ npm install @sanity/client
TYPESCRIPT
1// service/sanity.ts 2import { createClient } from "@sanity/client"; 3 4export const client = createClient({ 5 projectId: process.env.SANITY_PROJECT_ID, 6 dataset: process.env.SANITY_DATASET, 7 useCdn: false, 8 apiVersion: "2024-01-25", 9 token: process.env.SANITY_SECRET_TOKEN, 10});

projectId, dataset, tokensanity manage에서 확인 하여 .env파일에 반영 후 재 시작 한다.

설정이 끝났다면 본격적으로 Content Lake와 API 통신하여 데이터 .

SWR 네트워크 상태 라이브러리

SWR은 데이터 가져오기를 위한 React Hooks로 HTTP 캐시 무효 전략인 stale-while-revalidate에서 유래되었으며, 캐시(stale)로 부터 데이터를 반환한 후, fetch요청(revalidate)을 하고, 최종적으로 최신화된 데이터를 가져오는 전략이다.

주요 기능

  • 자동 재검증: SWR은 사용자가 애플리케이션에 상호작용할 때 자동으로 데이터를 재검증하여 최신 상태를 유지한다. 브라우저 포커스, 네트워크 재연결, 사용자 상호 작용 시 데이터를 새로고침할 수 있다.

  • 최적화된 UI 경험: SWR은 캐싱과 재검증을 통해 빠르고 일관된 사용자 경험을 제공한다. 데이터는 "만료되었지만 유효한(stale but valid)" 상태로 취급되어, 백그라운드에서 업데이트되는 동안에도 UI가 빠르게 로딩된다.

  • 간결한 API: SWR은 사용하기 쉬운 API를 제공하며, React Hooks와 자연스럽게 통합된다. 데이터 패칭, 캐싱, 재검증과 같은 복잡한 작업을 간단한 몇 줄의 코드로 처리할 수 있다.

  • 내장된 성능 최적화: SWR은 중복 요청을 방지하고, 데이터 패칭 작업을 효율적으로 관리하며, 사용자 경험을 개선하기 위한 여러 최적화 기능을 내장하고 있다.

  • 유연성: SWR은 REST, GraphQL 등 다양한 종류의 데이터 패칭 요구 사항을 지원한다. 또한, 전역 상태 관리, 요청 재시도, 페이징 및 무한 스크롤과 같은 고급 패턴을 구현하는 데에도 사용할 수 있다.

이러한 장점을 근거로 SWR 라이브러리를 사용하여 데이터 요청을 해보자.

SWR 사용방법

  • swr 설치

    BASH
    1$ npm i swr
  • 일반적으로 요청에는 "loading", "ready", "error"의 세 가지 상태가 있는데 data, error, isLoading 값을 사용하여 요청의 현재 상태를 확인하고 해당 UI를 반환한다.

  • fetcher은 fetch할때 어떤(fetch, Axios 등)걸 사용할지 명시할 수 있다.

    JAVASCRIPT
    1import useSWR from "swr"; 2 3function Profile() { 4 const { data, error, isLoading } = useSWR("/api/user/123", fetcher); 5 6 if (error) return <div>failed to load</div>; 7 if (isLoading) return <div>loading...</div>; 8 9 // 데이터 렌더링 10 return <div>hello {data.name}!</div>; 11}
  • useSWR을 사용할때마다 매번 fetcher을 호출하지 않아도 되게 context API를 생성한다. global-configuration 공식 사이트를 참고하여 설정한다.

    TYPESCRIPT
    1// context/SWRConfigContext.tsx 2"use client"; 3 4import { SWRConfig } from "swr"; 5 6type Props = { 7 children: React.ReactNode; 8}; 9 10export default function SWRConfigContext({ children }: Props) { 11 return ( 12 <SWRConfig 13 value={{ 14 // 원하는 fetcher 정의. 15 // url을 전달 받아 브라우저에서 제공해주는 fetch 사용하여 json으로 변환하여 반환함. 16 fetcher: (url: string) => fetch(url).then((res) => res.json()), 17 }} 18 > 19 {children} 20 </SWRConfig> 21 ); 22}
    TYPESCRIPT
    1// app/layout.tsx 2<main className="w-full mx-auto grow max-w-screen-2xl"> 3 <SWRConfigContext>{children}</SWRConfigContext> 4</main>
    TYPESCRIPT
    1"use client"; 2import useSWR from "swr"; 3import PostsGrid from "./PostsGrid"; 4 5export default function FeaturedPosts() { 6 const { data: posts, isLoading: loading } = useSWR("/api/posts"); 7 8 return ( 9 <section className="px-12 py-4 mt-[6rem]"> 10 <PostsGrid /> 11 </section> 12 ); 13}

마치며

지금까지 moc data에서 데이터를 가져오는 방식에서 sanity Content Lake API 통신으로 데이터를 가져오는 방식에 대해 알아보았다. 다음 포스팅에서는 마크다운 컨텐츠를 저장하는 방법에 대해 알아보겠다.

Next.js로 나만의 블로그 만들기: Sanity
Next.js로 나만의 블로그 만들기: Sanitysanity의 주요 특징과 사용방법에 대해 알아보자.
Vercel 배포 시 `.env` 설정
Vercel 배포 시 `.env` 설정Vercel 배포 시, .env 설정 누락으로 인한 에러 해결.
생성일: 2024.02.13수정일: 2024.02.13
목차