
우리는 Sanity CMS를 통해 4개의 웹사이트와 10개의 언어로 400개 이상의 콘텐츠를 발행했습니다. 스키마 설계부터 자동화된 다국어 발행까지, 우리가 배운 것들을 공유합니다.
Sanity CMS는 구조화된 콘텐츠, 실시간 Content Lake, 그리고 Sanity Studio라는 커스터마이징 가능한 React 기반 에디터를 중심으로 구축된 헤드리스 콘텐츠 플랫폼입니다. GROQ를 쿼리 언어로, Portable Text를 리치 콘텐츠로, 스키마-코드(schema-as-code)를 콘텐츠 모델링으로 사용합니다. 이 가이드는 설정, 스키마 설계, GROQ, Portable Text, 다국어 아키텍처, 그리고 가격 책정을 다룹니다.
Sanity CMS란?
Sanity는 구조화된 콘텐츠 플랫폼으로, Sanity.io 팀이 '콘텐츠 운영 체제'라고 부르는 것입니다. HTML 덩어리를 데이터베이스에 저장하는 전통적인 CMS와 달리, Sanity는 모든 콘텐츠를 Content Lake라는 관리형 백엔드에 구조화된 JSON으로 저장합니다. GROQ 또는 GraphQL로 쿼리하고, 원하는 모든 프론트엔드에서 콘텐츠를 렌더링할 수 있습니다: Next.js, React Native, Svelte, 모바일 앱, CLI 도구 -- 무엇이든 가능합니다.
이를 사용하는 회사들은 범위가 매우 넓습니다. Nike, Figma, Puma, 그리고 Cloudflare는 엔터프라이즈 규모로 Sanity를 운영합니다. 스타트업들은 무료 티어가 실제로 사용 가능하기 때문에 사용합니다(가격 책정은 나중에 다룰 것입니다). 우리가 사용하는 이유는 완전히 자동화된 10개 언어 발행 파이프라인을 구축할 수 있는 유연성을 주는 다른 것이 없었기 때문입니다.
Content Lake 아키텍처
Content Lake는 Sanity의 관리형 백엔드입니다. 모든 연결된 클라이언트에서 실시간으로 동기화되는 호스팅된 문서 저장소라고 생각하면 됩니다. Sanity Studio에서 편집자가 단락을 변경하면, 다른 편집자가 즉시 이를 볼 수 있습니다 -- 저장 버튼도, 병합 충돌도, 데이터베이스 마이그레이션도 없습니다.
내부적으로, 문서는 타입이 지정된 필드가 있는 구조화된 JSON으로 저장됩니다. 모든 변경은 트랜잭션 로그를 통해 추적되므로, 기본적으로 전체 버전 기록을 얻을 수 있습니다. 실시간 동기화는 리스너 기반 아키텍처(Sanity의 GitHub 아키텍처 문서에 설명됨)를 사용하여 RxJS observables를 통해 모든 구독자에게 변경 사항을 푸시합니다.
이것이 예를 들어 REST API가 있는 PostgreSQL 데이터베이스와 다른 점은 무엇일까요? Content Lake는 콘텐츠 모델링, 접근 제어, CDN 캐싱, 이미지 변환, 그리고 실시간 협업을 하나의 관리형 서비스로 처리합니다. 마이그레이션을 실행하지 않습니다. 복제본을 관리하지 않습니다. 스키마를 정의하고 콘텐츠를 쿼리하기만 하면 됩니다.
Sanity Studio: 커스터마이징 가능한 에디터
Sanity Studio는 편집 인터페이스로 사용되는 오픈소스 React 애플리케이션입니다. 호스팅된 관리자 패널이 아니라, 코드베이스에 있는 React 앱입니다. 커스텀 입력 컴포넌트, 조건부 필드, 문서 동작, 구조 빌더 패턴, 그리고 플러그인 등 모든 측면을 커스터마이징할 수 있습니다.
실시간 협업이 내장되어 있습니다. 여러 편집자가 현재 상태 표시기와 실시간 업데이트와 함께 동일한 문서에서 동시에 작업할 수 있습니다. Google Docs를 사용해봤다면, 경험이 비슷합니다 -- 다른 사람들의 커서와 변경 사항을 실시간으로 볼 수 있습니다.
우리는 npx sanity deploy로 Studio를 배포하며, 이는 Sanity의 CDN에 커스텀 서브도메인에서 호스팅됩니다. 단순히 React 앱이므로 자체 호스팅할 수도 있습니다. 우리가 헤드리스 CMS 비교에서 Sanity를 높게 평가한 이유는 Studio의 유연성 때문입니다.
Sanity 프로젝트 설정 방법
Sanity CMS를 설정하려면, npm create sanity@latest로 CLI를 설치하고, 프로젝트 템플릿을 선택하고, 스키마 파일을 구성하고, npx sanity dev를 실행하여 Studio를 로컬로 실행합니다. 전체 프로세스는 5분 이내에 완료됩니다.
사전 요구사항 및 설치
Node.js 18+ 그리고 npm (또는 pnpm)이 필요합니다. 그게 다입니다. init 명령을 실행하세요:
CLI는 필요한 모든 것으로 프로젝트를 스캐폴드합니다. 프로젝트 구조가 어떻게 보이는지는 다음과 같습니다:
프로젝트 구조 설명
sanity.config.ts 파일이 엔트리 포인트입니다. 최소한의 구성은 다음과 같습니다:
visionTool() 플러그인은 Studio 내 GROQ 플레이그라운드를 제공합니다 -- 개발 중에 지속적으로 사용하게 될 것입니다.
Studio 배포
npx sanity dev로 로컬에서 실행합니다 (localhost:3333에서 실행됩니다). 편집자와 공유할 준비가 되면, Sanity의 CDN에 배포합니다:
프로 팁: 스키마 변경 후 npx sanity@latest schema deploy를 실행하세요. 이는 스키마를 Sanity의 API에 업로드하며, GraphQL API 및 스키마 인식 도구(나중에 다룰 MCP 서버 포함)와 같은 기능을 활성화합니다.
Sanity CMS의 스키마 설계
Sanity 스키마는 코드베이스의 JavaScript 또는 TypeScript 객체로 정의됩니다. 각 스키마는 필드, 검증 규칙, 그리고 커스텀 입력 컴포넌트가 있는 문서 타입을 지정합니다. 스키마 변경은 즉시 반영됩니다 -- 데이터베이스 마이그레이션이 필요 없습니다. 이것이 '스키마-코드(schema-as-code)' 접근 방식이며, 이것이 우리를 Contentful이 아닌 Sanity로 결정하게 한 것입니다.
필드 타입 및 검증
Sanity는 풍부한 필드 타입 세트를 제공합니다. 우리가 가장 많이 사용하는 것들은 다음과 같습니다:
모든 필드는 검증 콜백을 통한 검증을 지원합니다. 필수 필드, 최소/최대값, 정규 표현식 패턴, 그리고 커스텀 규칙을 강제할 수 있습니다:
커스텀 블록 타입 (우리의 프로덕션 사례)
여기서 Sanity가 흥미로워집니다 -- 그리고 6개의 경쟁사 가이드 중 0개가 코드를 보여주는 곳입니다. 우리의 프로덕션 스키마에서, 우리는 body 배열 내에 5개의 커스텀 블록 타입을 정의합니다: block (표준 텍스트), table, codeBlock, chartBlock, 그리고 inlineImage.
우리의 codeBlock 정의는 다음과 같습니다:
그리고 body 필드가 우리의 모든 커스텀 타입을 함께 참조하는 방식은 다음과 같습니다:
이는 편집자들에게 풍부한 콘텐츠 도구 모음을 제공하면서 모든 요소를 타입이 지정되고 쿼리 가능하게 유지합니다. chartBlock은 단순한 불투명한 HTML 임베드가 아닙니다 -- chartType, title, dataPoints, 그리고 dataLabels 필드가 있는 구조화된 데이터입니다. 이는 동일한 콘텐츠를 웹, 이메일, 그리고 모바일에서 렌더링하려고 할 때 중요합니다.
스키마 구성 모범 사례
스키마를 모듈식으로 유지하세요. 우리는 타입별로 파일 간에 분할합니다: schemas/documents/post.ts, schemas/objects/codeBlock.ts, schemas/objects/chartBlock.ts. schemas/index.ts에서 모두 임포트합니다:
구조화된 콘텐츠로 작업하면서 얻은 핵심 통찰력: 당신의 스키마가 곧 당신의 콘텐츠 모델입니다. 이를 콘텐츠 팀을 위한 컨텍스트 엔지니어링으로 생각하면, 더 나은 설계 결정을 내릴 것입니다. 추가하는 모든 필드는 목적을 제공해야 합니다 -- 편집자를 위해, 렌더링을 위해, 또는 쿼리를 위해.
GROQ: Sanity의 쿼리 언어
GROQ (Graph-Relational Object Queries)는 JSON 문서를 필터링, 조인, 그리고 프로젝션하기 위한 Sanity의 오픈소스 쿼리 언어입니다. 기본 문법은 *[filter]{projection}입니다 -- 필터와 일치하는 모든 문서를 선택한 다음 출력을 형태로 정합니다. Sanity 특정 쿼리에 대해서는 GraphQL보다 더 간결하며, 우리의 경험상 배우기 빠릅니다.
기본 쿼리: 필터링 및 프로젝션
가장 간단한 쿼리는 한 타입의 모든 문서를 가져옵니다:
-> 연산자는 참조를 따릅니다. author->name은 "author 참조를 따르고 name 필드를 반환한다"는 의미입니다. 별도의 쿼리도 없고, N+1 문제도 없고, JOIN도 없습니다 -- 모든 것이 하나의 표현식입니다.
조인, 정렬, 그리고 페이지네이션
우리의 블로그 인덱스 페이지의 경우, 정렬되고 페이지화된 게시물과 확장된 참조가 필요합니다:
[0...10]은 처음 10개의 결과를 제공합니다 (0 인덱싱, 배타적 끝). | order(publishedAt desc)는 최신순으로 정렬합니다. 프로젝션은 출력을 형태로 정하여 프론트엔드가 필요로 하는 정확히 그것만 포함합니다 -- 더 이상 없습니다.
Sanity Studio 내의 Vision 플러그인을 사용하여 이러한 모든 쿼리를 대화식으로 테스트할 수 있습니다. 개발 중에 매우 유용합니다. 더 많은 패턴은 GROQ 치트 시트를 확인하세요.
GROQ와 GraphQL의 비교
Sanity는 GROQ와 GraphQL을 모두 지원합니다. 어떤 것을 언제 사용해야 할까요?
GROQ는 Sanity의 기본 언어입니다. 단일 쿼리 문자열에서 조인, 프로젝션, 그리고 계산된 필드를 처리합니다. 이것이 Content Lake가 최적화된 것입니다.
GraphQL은 스키마를 배포한 후 사용할 수 있습니다 (npx sanity@latest schema deploy). 표준화된 도구가 필요할 때 사용하세요 -- 예를 들어, 프론트엔드가 이미 Apollo Client를 사용하거나 팀이 GraphQL은 알지만 GROQ는 모르는 경우입니다.
...