
Sanity CMS 가이드: 10개 언어로 콘텐츠를 발행하는 방법
우리는 Sanity CMS를 통해 4개 웹사이트와 10개 언어로 400개 이상의 콘텐츠를 발행해 왔습니다. 스키마 설계부터 자동화된 다국어 발행에 이르기까지 우리가 배운 점을 공유합니다.
Sanity CMS는 구조화된 콘텐츠, 실시간 콘텐츠 레이크(Content Lake), 그리고 Sanity Studio라고 불리는 React 기반의 맞춤형 편집기를 중심으로 구축된 헤드리스 콘텐츠 플랫폼입니다. 쿼리를 위해 GROQ를 사용하고, 풍부한 콘텐츠를 위해 포터블 텍스트(Portable Text)를 사용하며, 콘텐츠 모델링을 위해 스키마 애즈 코드(schema-as-code) 방식을 채택합니다. 이 가이드에서는 설정, 스키마 설계, GROQ, 포터블 텍스트, 다국어 아키텍처 및 가격 정책을 다룹니다.
Sanity CMS란 무엇인가?
Sanity는 구조화된 콘텐츠 플랫폼으로, Sanity.io 팀은 이를 "콘텐츠 운영 체제"라고 부릅니다. 데이터베이스에 HTML 덩어리를 저장하는 기존 CMS와 달리, Sanity는 콘텐츠 레이크라는 관리형 백엔드에 모든 콘텐츠를 구조화된 JSON으로 저장합니다. GROQ 또는 GraphQL로 쿼리하고, 원하는 어떤 프론트엔드(Next.js, React Native, Svelte, 모바일 앱, CLI 도구 등)에서도 콘텐츠를 렌더링할 수 있습니다.
이를 사용하는 기업들은 다양합니다. Nike, Figma, Puma, Cloudflare는 엔터프라이즈 규모로 Sanity를 운영합니다. 스타트업은 무료 티어가 실제로 사용하기 충분하기 때문에(가격 정책은 나중에 자세히 설명) 사용합니다. 우리는 완전히 자동화된 10개 언어 발행 파이프라인을 구축할 수 있는 유연성을 제공한 다른 대안이 없었기 때문에 Sanity를 선택했습니다.
콘텐츠 레이크 아키텍처
콘텐츠 레이크는 Sanity의 관리형 백엔드입니다. 연결된 모든 클라이언트에서 실시간으로 동기화되는 호스팅 문서 저장소라고 생각하면 됩니다. 편집자가 Sanity Studio에서 단락을 변경하면 다른 편집자는 즉시 이를 볼 수 있습니다. 저장 버튼도, 병합 충돌도, 데이터베이스 마이그레이션도 필요 없습니다.
내부적으로 문서는 타입이 지정된 필드를 가진 구조화된 JSON으로 저장됩니다. 모든 변경 사항은 트랜잭션 로그를 통해 추적되므로 기본적으로 전체 버전 기록을 얻을 수 있습니다. 실시간 동기화는 Sanity의 GitHub 아키텍처 문서에 설명된 리스너 기반 아키텍처를 사용하며, RxJS 옵저버블을 통해 모든 구독자에게 변경 사항을 푸시합니다.
이것이 REST API를 갖춘 PostgreSQL 데이터베이스와 다른 점은 무엇일까요? 콘텐츠 레이크는 콘텐츠 모델링, 접근 제어, CDN 캐싱, 이미지 변환 및 실시간 협업을 단일 관리형 서비스로 처리합니다. 마이그레이션을 실행할 필요가 없고, 복제본을 관리할 필요도 없습니다. 스키마를 정의하고 콘텐츠를 쿼리하기만 하면 됩니다.
Sanity Studio: 맞춤형 편집기
Sanity Studio는 편집 인터페이스 역할을 하는 오픈 소스 React 애플리케이션입니다. 호스팅되는 관리자 패널이 아니라 코드베이스에 상주하는 React 앱입니다. 커스텀 입력 컴포넌트, 조건부 필드, 문서 작업, 구조 빌더 패턴 및 플러그인 등 모든 측면을 customization할 수 있습니다.
실시간 협업 기능이 내장되어 있습니다. 여러 편집자가 존재 표시기(presence indicators)와 실시간 업데이트를 통해 동일한 문서에서 동시에 작업할 수 있습니다. Google Docs를 사용해 본 적이 있다면, 다른 사람의 커서와 변경 사항을 실시간으로 볼 수 있는 것과 유사한 경험입니다.
우리는 npx sanity deploy를 사용하여 스튜디오를 배포하며, 이는 커스텀 서브도메인의 Sanity CDN에서 호스팅됩니다. 단순히 React 앱이기 때문에 직접 호스팅할 수도 있습니다. 우리는 스튜디오의 유연성 덕분에 헤드리스 CMS 비교에서 Sanity를 높게 평가했습니다.
Sanity 프로젝트 설정 방법
Sanity CMS를 설정하려면 npm create sanity@latest로 CLI를 설치하고, 프로젝트 템플릿을 선택한 후, 스키마 파일을 구성하고 npx sanity dev를 실행하여 로컬에서 스튜디오를 실행하면 됩니다. 전체 과정은 5분도 걸리지 않습니다.
필수 조건 및 설치
Node.js 18+와 npm(또는 pnpm)만 필요합니다. 초기화 명령어를 실행하세요:
npm create sanity@latest
# You'll be prompted for:
# - Login method (Google, GitHub, email)
# - Project name
# - Dataset name (default: "production")
# - Project template (blog, ecommerce, clean)
# - TypeScript? (recommended: yes)CLI가 필요한 모든 것을 포함한 프로젝트를 스캐폴딩합니다. 프로젝트 구조는 다음과 같습니다:
프로젝트 구조 설명
my-sanity-project/
├── schemas/ # Your content schemas (this is where you'll spend time)
│ ├── index.ts # Schema registry -- imports and exports all types
│ ├── post.ts # Document type definitions
│ └── blockContent.ts # Rich text / Portable Text config
├── sanity.config.ts # Main config -- plugins, Studio structure, dataset
├── sanity.cli.ts # CLI config -- project ID, dataset
├── package.json
└── tsconfig.jsonsanity.config.ts 파일은 진입점입니다. 최소한의 설정은 다음과 같습니다:
// sanity.config.ts
import { defineConfig } from 'sanity'
import { structureTool } from 'sanity/structure'
import { visionTool } from '@sanity/vision'
import { schemaTypes } from './schemas'
export default defineConfig({
name: 'default',
title: 'My Blog',
projectId: 'your-project-id',
dataset: 'production',
plugins: [structureTool(), visionTool()],
schema: { types: schemaTypes },
})visionTool() 플러그인은 스튜디오 내에서 GROQ 플레이그라운드를 제공하므로, 개발 중에 자주 사용하게 될 것입니다.
스튜디오 배포
npx sanity dev로 로컬에서 실행합니다(localhost:3333에서 실행). 편집자와 공유할 준비가 되면 Sanity의 CDN에 배포합니다:
npx sanity deploy
# Prompts for a hostname, e.g., "my-blog"
# Deploys to https://my-blog.sanity.studio전문가 팁: 스키마를 변경할 때마다 npx sanity@latest schema deploy를 실행하세요. 이렇게 하면 스키마가 Sanity의 API에 업로드되어 GraphQL API 및 스키마 인식 도구(나중에 다룰 MCP 서버 포함)와 같은 기능이 활성화됩니다.
Sanity CMS의 스키마 설계
Sanity 스키마는 코드베이스 내에서 JavaScript 또는 TypeScript 객체로 정의됩니다. 각 스키마는 필드, 유효성 검사 규칙 및 커스텀 입력 컴포넌트가 있는 문서 유형을 지정합니다. 스키마 변경은 즉각적이며 데이터베이스 마이그레이션이 필요하지 않습니다. 이것이 바로 "스키마 애즈 코드" 접근 방식이며, Contentful보다 Sanity를 선택하게 만든 결정적인 요소였습니다.
필드 유형 및 유효성 검사
Sanity는 다양한 필드 유형을 제공합니다. 우리가 가장 많이 사용하는 유형은 다음과 같습니다:
| 필드 유형 | 사용 사례 | 예시 |
|---|---|---|
string | 짧은 텍스트, 제목, 슬러그 | 게시글 제목, 저자 이름 |
text | 여러 줄의 일반 텍스트 | 요약, 설명 |
number | 정수, 실수 | 읽기 시간, 정렬 순서 |
boolean | 토글 | 추천 여부, 초안 상태 |
array | 목록, 풍부한 텍스트(포터블 텍스트) | 본문 콘텐츠, 태그 |
reference | 다른 문서 링크 | 저자, 카테고리 |
image | 메타데이터가 포함된 이미지 | 대체 텍스트가 있는 커버 이미지 |
slug | URL 친화적인 문자열 | 제목에서 자동 생성 |
object | 중첩된 필드 그룹 | SEO 필드(metaTitle + metaDescription) |
date / datetime | 날짜 | 발행일 |
모든 필드는 validation 콜백을 통해 유효성 검사를 지원합니다. 필수 필드, 최소/최대 값, 정규식 패턴 및 사용자 정의 규칙을 강제할 수 있습니다:
defineField({
name: 'seoDescription',
title: 'Meta Description',
type: 'string',
validation: (Rule) =>
Rule.required()
.min(145)
.max(160)
.warning('Meta description should be 145-160 characters'),
})커스텀 블록 유형(프로덕션 예시)
Sanity가 흥미로워지는 지점이자, 경쟁 가이드 6개 중 0개가 코드를 보여주는 부분입니다. 우리의 프로덕션 스키마에서는 body 배열 내에 5가지 커스텀 블록 유형을 정의합니다: block(표준 텍스트), table, codeBlock, chartBlock, inlineImage.
codeBlock 정의는 다음과 같습니다:
// schemas/objects/codeBlock.ts
import { defineType } from 'sanity'
export const codeBlock = defineType({
name: 'codeBlock',
title: 'Code Block',
type: 'object',
fields: [
{
name: 'language',
title: 'Language',
type: 'string',
options: {
list: [
{ title: 'JavaScript', value: 'javascript' },
{ title: 'TypeScript', value: 'typescript' },
{ title: 'Python', value: 'python' },
{ title: 'Bash', value: 'bash' },
{ title: 'JSON', value: 'json' },
{ title: 'GROQ', value: 'groq' },
],
},
},
{
name: 'code',
title: 'Code',
type: 'text',
},
],
})body 필드가 모든 커스텀 유형을 참조하는 방법은 다음과 같습니다:
// schemas/fields/body.ts
defineField({
name: 'body',
title: 'Body',
type: 'array',
of: [
{ type: 'block' }, // Standard Portable Text (paragraphs, headings, lists)
{ type: 'table' }, // @sanity/table plugin
{ type: 'codeBlock' }, // Our custom code block
{ type: 'chartBlock' }, // Data visualization (bar, line, pie)
{ type: 'inlineImage' }, // Images with alt text and captions
],
})이는 편집자에게 풍부한 콘텐츠 도구를 제공하면서도 모든 요소를 타입화되고 쿼리 가능하게 유지합니다. chartBlock은 불투명한 HTML 임베드가 아니라 chartType, title, dataPoints, dataLabels 필드를 가진 구조화된 데이터입니다. 웹, 이메일, 모바일에서 동일한 콘텐츠를 렌더링하려고 할 때 이는 매우 중요합니다.
스키마 조직 모범 사례
스키마를 모듈식으로 유지하세요. 우리는 유형별로 파일을 분할합니다: schemas/documents/post.ts, schemas/objects/codeBlock.ts, schemas/objects/chartBlock.ts. schemas/index.ts에서 모두 가져옵니다:
// schemas/index.ts
import { post } from './documents/post'
import { codeBlock } from './objects/codeBlock'
import { chartBlock } from './objects/chartBlock'
import { inlineImage } from './objects/inlineImage'
export const schemaTypes = [post, codeBlock, chartBlock, inlineImage]구조화된 콘텐츠 작업을 통해 얻은 핵심 통찰력: 스키마가 곧 콘텐츠 모델입니다. 콘텐츠 팀을 위한 컨텍스트 엔지니어링이라고 생각하면 더 나은 설계 결정을 내릴 수 있습니다. 추가하는 모든 필드는 편집자, 렌더링 또는 쿼리를 위해 목적을 가져야 합니다.
GROQ: Sanity의 쿼리 언어
GROQ(Graph-Relational Object Queries)는 JSON 문서를 필터링, 조인 및 프로젝션하기 위한 Sanity의 오픈 소스 쿼리 언어입니다. 기본 구문은 *[filter]{projection}으로, 필터와 일치하는 모든 문서를 선택한 다음 출력을 형성합니다. Sanity 특정 쿼리의 경우 GraphQL보다 간결하며, 우리의 경험상 학습 속도가 더 빠릅니다.
기본 쿼리: 필터 및 프로젝션
가장 간단한 쿼리는 특정 유형의 모든 문서를 가져옵니다:
// Fetch all posts -- just title and slug
*[_type == "post"]{
title,
"slug": slug.current
}
// Filter by language, expand author reference
*[_type == "post" && language == "en"]{
title,
"slug": slug.current,
"authorName": author->name,
"authorImage": author->image,
"categoryTitle": category->title,
publishedAt
}-> 연산자는 참조를 따라갑니다. author->name은 "저자 참조를 따라가서 name 필드를 반환한다"는 의미입니다. 별도의 쿼리도, N+1 문제도, JOIN도 필요 없으며, 모든 것이 하나의 표현식으로 처리됩니다.
조인, 정렬 및 페이지네이션
블로그 인덱스 페이지에는 확장된 참조가 포함된 정렬된 페이지네이션 게시글이 필요합니다:
// Paginated posts with full metadata
*[_type == "post" && language == "en"] | order(publishedAt desc) [0...10] {
title,
"slug": slug.current,
excerpt,
publishedAt,
readTime,
"author": author->{name, image},
"category": category->{title, "slug": slug.current},
"coverImage": coverImage{
"src": asset->url,
alt
}
}[0...10]은 처음 10개의 결과를 제공합니다(0부터 시작, 끝은 제외). | order(publishedAt desc)는 최신순으로 정렬합니다. 프로젝션은 프론트엔드에 정확히 필요한 내용만 포함하도록 출력을 형성합니다.
Sanity Studio 내부의 Vision 플러그인을 사용하여 이러한 쿼리를 모두 상호작용적으로 테스트할 수 있습니다. 개발 중에 매우 유용합니다. 더 많은 패턴은 GROQ 치트 시트를 확인하세요.
GROQ vs GraphQL
Sanity는 GROQ와 GraphQL을 모두 지원합니다. 언제 무엇을 사용해야 할까요?
GROQ는 Sanity의 네이티브 언어입니다. 단일 쿼리 문자열 내에서 조인, 프로젝션 및 계산된 필드를 처리합니다. 콘텐츠 레이크가 최적화된 대상입니다.
GraphQL은 스키마를 배포(npx sanity@latest schema deploy)한 후에 사용할 수 있습니다. 표준화된 도구가 필요할 때 사용하세요. 예를 들어, 프론트엔드가 이미 Apollo Client를 사용하거나 팀이 GROQ보다 GraphQL에 익숙한 경우입니다.
우리는 exclusively GROQ를 사용합니다. Sanity 데이터에 대해 더 표현력이 풍부하며, Vision 플러그인으로 쿼리 디버깅이 매우 쉽습니다.
포터블 텍스트: 올바른 방식으로 구현된 풍부한 콘텐츠
포터블 텍스트는 구조화된 풍부한 텍스트를 위한 Sanity의 사양입니다. 콘텐츠를 HTML 문자열로 저장하는 대신, typed blocks, 단락, 제목, 이미지, 코드 스니펫, 테이블 등을 각각 JSON 객체로 저장합니다.这使得 콘텐츠를 어떤 프레임워크, 어떤 플랫폼, 어떤 형식에서도 렌더링할 수 있게 합니다.
데이터 구조
단락과 코드 블록이 포터블 텍스트 JSON으로 어떻게 보이는지 살펴보세요:
[
{
"_type": "block",
"_key": "a1b2c3",
"style": "normal",
"markDefs": [],
"children": [
{
"_type": "span",
"_key": "d4e5f6",
"text": "Here's an example of our pipeline config:",
"marks": []
}
]
},
{
"_type": "codeBlock",
"_key": "g7h8i9",
"language": "typescript",
"code": "export default defineConfig({ ... })"
}
]모든 블록에는 _type과 _key가 있습니다. 표준 텍스트 블록은 자식 span(굵게, 기울임꼴, 링크와 같은 마크 지원)을 가진 "block"을 사용합니다. codeBlock, chartBlock, table, inlineImage와 같은 커스텀 블록은 고유한 _type을 사용하고 구조화된 필드를携带합니다.
왜 중요할까요? HTML은 렌더링 형식이지 저장 형식이 아니기 때문입니다. 데이터베이스에 <h2>Title</h2><p>Some <strong>text</strong></p>를 저장하면 웹 렌더링에 잠기게 됩니다. 모바일 앱, 이메일 뉴스레터, PDF 또는 AI 에이전트의 컨텍스트 창에서 이를 깔끔하게 추출할 수 없습니다. 포터블 텍스트는 콘텐츠와 프레젠테이션을 분리합니다. 포터블 텍스트 사양은 오픈 소스이며, Sanity에 종속되지 않습니다.
프로덕션의 커스텀 블록
우리의 파이프라인은 Python 스크립트(scripts/md_to_portable_text.py)를 사용하여 Markdown을 포터블 텍스트로 변환합니다. 변환기는 표준 블록과 4가지 커스텀 유형을 처리합니다:
table,@sanity/table플러그인 스키마를 사용합니다. 행과 셀이 구조화된 데이터로 저장됩니다.codeBlock, 언어와 코드가 별도의 필드로 구분되어 렌더링 시 구문 강조를 가능하게 합니다.chartBlock, 차트 유형, 제목, 축 레이블, 시리즈 이름 및 데이터 포인트가 구조화된 JSON으로 저장됩니다. 프론트엔드는 Chart.js로 이를 렌더링합니다.inlineImage, 대체 텍스트, 소스 및 선택적 캡션이 별도의 필드로 저장됩니다.
이 구조 덕분에 GROQ를 통해 블로그의 모든 코드 예제(*[body[]._type == "codeBlock"])를 쿼리하거나, 차트가 있는 게시글을 찾거나, 대체 텍스트가 누락된 모든 이미지를 추출할 수 있습니다.
포터블 텍스트 렌더링
프론트엔드에서는 @portabletext/react(또는 Svelte/Vue equivalents)를 사용합니다. 각 블록 유형에 대해 커스텀 컴포넌트를 등록합니다:
import { PortableText } from '@portabletext/react'
const components = {
types: {
codeBlock: ({ value }) => (
<pre className={`language-${value.language}`}>
<code>{value.code}</code>
</pre>
),
chartBlock: ({ value }) => <Chart data={value} />,
inlineImage: ({ value }) => (
<figure>
<img src={value.src} alt={value.alt} />
{value.caption && <figcaption>{value.caption}</figcaption>}
</figure>
),
},
}
// In your component:
<PortableText value={post.body} components={components} />이것이 전체 렌더링 파이프라인입니다. PortableText 컴포넌트는 표준 블록(단락, 제목, 목록, 마크)을 자동으로 처리합니다. 커스텀 유형에 대해서만 커스텀 컴포넌트를 정의하면 됩니다.
Sanity CMS로 다국어 콘텐츠 관리
Sanity는 문서 수준 현지화(정규 참조로 연결된 언어별 별도 문서) 또는 필드 수준 현지화(하나의 문서 내에서 번역된 필드)를 통해 다국어 콘텐츠를 지원합니다. 문서 수준은 SEO와 대규모 발행에 더 적합하며, 우리는 10개 언어 파이프라인 전반에 걸쳐 이를 사용합니다.
문서 수준 vs 필드 수준 현지화
| 측면 | 문서 수준 | 필드 수준 |
|---|---|---|
| 접근 방식 | 언어별 별도 문서 | 하나의 문서에 모든 번역 포함 |
| SEO | 각 문서가 고유한 URL/슬러그 가짐 | 단일 URL, 언어별 페이지 제공이 어려움 |
| 쿼리 복잡성 | 단순 필터: language == "de" | 중첩 필드 접근: title.de |
| 콘텐츠 크기 | 작고 집중된 문서 | 모든 언어가 포함된 큰 문서 하나 |
| 최적 용도 | 블로그 게시글, 페이지, SEO 중심 콘텐츠 | 작은 UI 문자열, 레이블, 메타데이터 |
| 우리의 결론 | 모든 것에 사용 | 공유 UI 문자열에만 사용 |
우리는 각 번역이 고유한 슬러그, URL 및 메타데이터를 갖기 때문에 문서 수준 현지화를 선택했습니다. Supabase vs Firebase에 대한 게시글의 터키어 버전은 URL 매개변수 해킹이 아닌 적절한 터키어인 supabase-firebase-karsilastirma 슬러그를 갖습니다.
10개 언어 파이프라인 아키텍처
자동화 파이프라인의 작동 방식은 다음과 같습니다: 영어로 게시글을 작성한 후 9개 추가 언어(독일어, 프랑스어, 네덜란드어, 스페인어, 터키어, 이탈리아어, 스웨덴어, 노르웨이어, 아랍어)로 번역합니다. 각 번역은 Markdown 변환, 포터블 텍스트 생성 및 Sanity API 발행 과정을 거칩니다.
아키텍처는 다음과 같습니다:
- 작성, YAML 프런트매터가 포함된 영어 Markdown
- 번역, 9개 언어로 AI 번역(완전성 및 발음 기호 검증)
- 변환, Python 스크립트가 각
.md파일을 포터블 텍스트 JSON으로 변환 - 발행, Sanity에 API 호출: 문서 생성, 이미지 업로드, 참조 패치
각 문서에는 language 필드와 영어 원문을 가리키는 canonicalPost 참조가 있습니다. 게시글과 모든 번역을 가져오는 GROQ 쿼리는 다음과 같습니다:
// Fetch a post and all its translations
*[_type == "post" && slug.current == "sanity-cms-guide" && language == "en"][0]{
title,
language,
"translations": *[
_type == "post" &&
canonicalPost._ref == ^._id
]{
title,
language,
"slug": slug.current
}
}스키마 측면은 간단합니다. 지원되는 언어의 열거형(enum)을 가진 language 필드:
defineField({
name: 'language',
title: 'Language',
type: 'string',
options: {
list: [
{ title: 'English', value: 'en' },
{ title: 'German', value: 'de' },
{ title: 'French', value: 'fr' },
{ title: 'Dutch', value: 'nl' },
{ title: 'Spanish', value: 'es' },
{ title: 'Turkish', value: 'tr' },
{ title: 'Italian', value: 'it' },
{ title: 'Swedish', value: 'sv' },
{ title: 'Norwegian', value: 'no' },
{ title: 'Arabic', value: 'ar' },
],
},
validation: (Rule) => Rule.required(),
})우리가 어렵게 배운 함정 하나: 영어 문서를 먼저 발행한 후, 번역물의 canonicalPost 참조를 drafts. 접두사가 아닌 발행된 문서 ID를 사용하여 패치해야 합니다. Sanity는 내부적으로 초안과 발행된 문서를 별도의 엔티티로 취급합니다.
이 파이프라인이 Model Context Protocol과 어떻게 연결되는지에 대한 자세한 내용은 다음 섹션을 참조하세요.
Sanity AI 기능: MCP, Canvas 및 에이전트 컨텍스트
Sanity는 자신을 AI 시대의 콘텐츠 운영 체제로 위치시키고 있습니다. 주요 AI 기능에는 AI 에이전트가 콘텐츠를 읽고 쓸 수 있는 MCP 서버, Studio 내에서 AI 지원 편집을 위한 Canvas, 그리고 스키마 인식으로 구조화된 콘텐츠를 쿼리할 수 있는 프로덕션 AI 에이전트를 위한 Agent Context가 포함됩니다.
MCP 서버 통합
Sanity MCP 서버는 Claude Code, Cursor, Windsurf 등의 AI 에이전트가 Sanity 워크스페이스와 프로그래밍 방식으로 상호 작용할 수 있게 합니다. 에이전트는 커스텀 API 래퍼 없이 스키마를 읽고, GROQ 쿼리를 실행하며, 문서를 생성하고 콘텐츠를 관리할 수 있습니다.
우리는 콘텐츠 파이프라인에서 매일 Sanity MCP 서버를 사용합니다. 우리 AI 에이전트는 문서 구조를 이해하기 위해 스키마를 쿼리하고, 내부 링크 기회를 찾기 위해 기존 게시글을 가져오며, 새 문서를 발행합니다. MCP 프로토콜은 에이전트에 스키마 인식을 제공하므로, 어떤 필드가 존재하고, 어떤 유형을 기대하며, 어떤 유효성 검사 규칙이 적용되는지 알 수 있습니다. 비즈니스용 AI 에이전트 워크플로를 구축하고 있다면 이는 강력한 패턴입니다.
프로덕션 AI를 위한 에이전트 컨텍스트
에이전트 컨텍스트는 프로덕션 등급 AI 통합을 위한 별도의 기능입니다. 개발자 도구를 위해 설계된 MCP 서버와 달리, 에이전트 컨텍스트는 런타임에 콘텐츠를 쿼리해야 하는 AI 에이전트(챗봇, 추천 엔진 또는 콘텐츠 개인화 시스템 등)를 위해 읽기 전용의 범위 지정된 액세스를 제공합니다.
차이가 중요합니다: MCP는 빌드 타임 및 편집 워크플로(스키마 인식 개발 도구)를 위한 것이고, 에이전트 컨텍스트는 적절한 인증 및 속도 제한이 있는 런타임 콘텐츠 액세스를 위한 것입니다.
Sanity의 구조화된 콘텐츠는 여기서 진정한 이점을 제공합니다. WordPress 사이트는 콘텐츠를 HTML 덩어리로 저장하므로 AI 에이전트는 콘텐츠를 이해하기 위해 HTML을 파싱해야 합니다. Sanity는 정의된 스키마를 가진 타입화된 JSON 문서를 저장합니다. 에이전트는 *[_type == "product" && category == "electronics"]{name, price, features}를 쿼리하고 깨끗한 구조화된 데이터를 받을 수 있습니다. 스크래핑도, 파싱도, 추측도 필요 없습니다.
Techsy에서 Sanity를 사용하는 방법
이것은 가상의 섹션이 아닙니다. 우리는 지난 1년 동안 구축한 자동화 파이프라인을 통해 4개 프로덕션 웹사이트에서 Sanity CMS를 운영하고 10개 언어로 발행합니다. 아키텍처는 다음과 같습니다.
콘텐츠 파이프라인 아키텍처
파이프라인은 연구부터 10개 언어 전반에 걸쳐 발행된 게시글까지 이어집니다:
- 연구, 키워드 분석, 경쟁사 격차 식별, SERP 패턴
- 브리프, 섹션 지침, 단어 수, 내부 링크가 포함된 구조화된 작성 사양
- 작성, YAML 프런트매터가 포함된 영어 Markdown 생산
- 변환, Python 스크립트가 Markdown을 5가지 커스텀 블록 유형이 포함된 포터블 텍스트 JSON으로 변환
- 발행, Sanity에 API 호출:
createOrReplace문서, Sanity CDN에 이미지 업로드, 저자/카테고리 참조 패치 - 번역, 9개 언어로 AI 번역, 완전성 검증
- 번역 발행, 언어별 동일한 변환/발행 흐름,
canonicalPost참조를 영어 원문에 패치
커스텀 스키마는 block, table, codeBlock, chartBlock, inlineImage 유형을 지원하며, 모두 유효성 검사 규칙이 있는 프로덕션 Sanity 스키마 객체로 정의됩니다. 우리가 테스트한 스타트업용 AI 도구 중에서 이 Sanity 기반 파이프라인은 대규모 구조화된 콘텐츠에 가장 신뢰할 수 있었습니다.
400개 이상 발행된 콘텐츠에서 얻은 교훈
누군가가 미리 알려줬으면 좋았을 몇 가지 사항:
참조 패치 순서가 중요합니다. Sanity 참조는 아직 존재하지 않는 문서를 가리킬 수 없습니다. 영어 게시글을 먼저 발행한 후, 영어 문서의 발행된 ID를 가리키는 canonicalPost로 번역물을 생성하세요. 초기에 이를 여러 번 위반했습니다.
스키마 배포는 워크스페이스별입니다. 여러 Sanity 프로젝트를 실행하는 경우(우리는 4개를 실행함), 각 프로젝트 구성마다 npx sanity@latest schema deploy로 스키마를 별도로 배포해야 합니다.
무료 티어는 واقعی입니다. 4개 사이트 중 2개를 몇 달 동안 무료 플랜으로 운영했습니다. 사용자 20명, 월 50만 건의 API 요청, 10만 건의 CDN 요청이면 장난감 프로젝트가 아닌 실제 프로덕션 사이트에 충분합니다.
포터블 텍스트 변환이 병목 현상입니다. Markdown에서 포터블 텍스트로의 변환은 간단하지 않습니다. 중첩 목록, 인용구 내부의 테이블, 특수 문자가 있는 코드 블록 등 엣지 케이스가 곳곳에 있습니다. 우리는 변환기 스크립트를 몇 달 동안 반복 개선해 왔습니다.
프로젝트에 Sanity 설정 도움이 필요하신가요? 우리는 4개 프로덕션 사이트를 위한 다국어 콘텐츠 파이프라인을 구축했습니다. 무료 상담 받기
Sanity CMS 가격 분석
Sanity는 세 가지 플랜을 제공합니다: Free(사용자 20명, 월 50만 건 API 요청), Growth(고급 역할 및 예약된 초안 포함, 사용자당 월 $15), Enterprise(SLA 및 규정 준수 기능 포함, 맞춤 가격). 무료 티어는 헤드리스 CMS 시장에서 가장 관대합니다.
| 기능 | Free | Growth (사용자당 월 $15) | Enterprise |
|---|---|---|---|
| 사용자 | 20 | 50 | 무제한 |
| API 요청 | 월 50만 건 | 월 250만 건 | 맞춤 |
| CDN 요청 | 월 10만 건 | 월 50만 건 | 맞춤 |
| 역할 | 관리자만 | 관리자, 개발자, 편집자, 기여자 | 맞춤 역할 |
| 협업 | 실시간 편집 | + 예약 발행, 초안 | + 워크플로 |
| 지원 | 커뮤니티 | 이메일 | 전용 + SLA |
| 규정 준수 | , | , | SOC 2, HIPAA |
무료 티어에서 우리는 두 개의 사이트를 제한 없이 운영했습니다. $15/사용자/월의 Growth 플랜은 역할 기반 액세스(기술적이지 않은 편집자가 생긴 후 중요해짐)와 예약 발행을 추가했습니다. Growth에서 뷰어는 무료인데, 이는 이해관계자에게 읽기 액세스를 제공하는 데 불이익을 받지 않는다는 점에서 좋은 점입니다.
경쟁사와 비교하면 어떻게 될까요?
| 기능 | Sanity Free | Contentful Free | Strapi Cloud Free | Payload Cloud |
|---|---|---|---|---|
| 사용자 | 20 | 1 | 1 | 1 |
| 콘텐츠 유형 | 무제한 | 48 | 무제한 | 무제한 |
| API 호출 | 월 50만 건 | 포함 | 포함 | 포함 |
| 커스텀 유형 | 예 | 제한됨 | 예 | 예 |
| 성장 비용 | 사용자당 월 $15 | 월 $300 | 월 $29 | 월 $50 |
Sanity의 20명 사용자 무료 티어는 예외적입니다. Contentful은 무료에서 1명의 사용자로 제한하며 Team 플랜은 월 $300로 급등합니다. 스타트업이나 소규모 팀이라면 Sanity의 무료 플랜을 통해 비용 없이 실제 프로덕션 워크로드를 실행할 수 있습니다.
Sanity는 자격을 갖춘 스타트업에게 1년간 무료 Growth 액세스를 제공하는 스타트업 프로그램도 제공합니다. 자격이 있다면 신청해 볼 가치가 있습니다.
자주 묻는 질문
Sanity CMS란 무엇이며 어떻게 작동하나요?
Sanity CMS는 콘텐츠 레이크라고 불리는 관리형 백엔드에 구조화된 JSON 문서를 저장하는 헤드리스 콘텐츠 플랫폼입니다. Sanity Studio(맞춤형 React 앱)를 통해 콘텐츠를 편집하고, GROQ 또는 GraphQL로 쿼리하며, 어떤 프론트엔드 프레임워크에서도 렌더링합니다. 콘텐츠는 연결된 모든 클라이언트에서 실시간으로 동기화됩니다.
Sanity CMS는 무료인가요?
예. Sanity의 무료 티어에는 사용자 20명, 월 50만 건의 API 요청 및 10만 건의 CDN 요청이 포함되며, 이는 헤드리스 CMS 플랫폼 중 가장 관대한 무료 플랜입니다. Growth 플랜은 사용자당 월 $15이며 역할 기반 액세스, 예약 발행 및 더 높은 제한을 추가합니다. Enterprise 가격은 맞춤입니다.
Sanity와 Contentful의 차이점은 무엇인가요?
Sanity는 스키마 애즈 코드(스키마가 코드베이스에 상주), 쿼리를 위한 GROQ, 완전히 맞춤형인 오픈 소스 Studio를 사용합니다. Contentful은 GUI 기반 콘텐츠 모델링, GraphQL, customization이 적은 호스팅 편집기를 사용합니다. Sanity의 무료 티어에는 20명의 사용자가 포함되는 반면 Contentful은 1명입니다. Contentful은 더 큰 플러그인 마켓플레이스를 보유하고 있습니다.
Sanity CMS는 초보자에게 적합한가요?
Sanity Studio는 콘텐츠 편집자에게 직관적이며, 편집 경험에는 기술적 지식이 필요하지 않습니다. 그러나 스키마 설정에는 JavaScript 또는 TypeScript 숙련도가 필요합니다. Sanity는 우수한 문서, 프로젝트 템플릿 및 활발한 지원이 있는 커뮤니티 Slack을 제공합니다. npm create sanity@latest와 블로그 템플릿으로 시작하세요.
Sanity를 자체 호스팅할 수 있나요?
Sanity Studio는 오픈 소스 React 애플리케이션이므로 완전히 자체 호스팅 가능합니다. Vercel, Netlify 또는 정적 호스팅 제공업체에 배포할 수 있습니다. 콘텐츠 레이크 백엔드는 관리형 서비스이며, 데이터 계층에 대한 자체 호스팅 옵션은 없습니다. 이는 인프라 관리가 전혀 필요하지만 온프레미스 데이터 제어가 없다는 tradeoff입니다.
Sanity는 어떤 유형의 데이터베이스를 사용하나요?
Sanity의 콘텐츠 레이크는 전통적인 SQL 또는 NoSQL 데이터베이스가 아닙니다. GROQ 쿼리 계층이 위에 있는 구조화된 JSON으로 콘텐츠를 저장하는 관리형 문서 저장소입니다. underlying 데이터베이스와 직접 상호 작용하지 않고 Sanity의 API를 통해 상호 작용합니다. 문서는 전체 버전 기록과 실시간 동기화가 내장되어 있습니다.
Sanity CMS는 오픈 소스인가요?
Sanity Studio는 MIT 라이선스 하에 오픈 소스이며, 포크하고, customization하고, 자체 호스팅할 수 있습니다. 콘텐츠 레이크 백엔드는 독점 SaaS입니다. GROQ 쿼리 언어 사양도 GitHub에 게시된 오픈 소스입니다. 포터블 텍스트 사양도 portabletext.org에서 유지 관리되는 오픈 소스입니다.
Sanity의 포터블 텍스트란 무엇인가요?
포터블 텍스트는 구조화된 풍부한 텍스트를 위한 Sanity의 사양입니다. 콘텐츠를 HTML 문자열로 저장하는 대신, 단락, 제목, 이미지 및 커스텀 블록을 배열 내의 타입화된 JSON 객체로 표현합니다. 이는 콘텐츠를 프레임워크와 플랫폼 간에 이동 가능하게 만듭니다. 고유한 구조화된 필드를 가진 코드 스니펫, 차트, 테이블과 같은 커스텀 블록 유형을 정의할 수 있습니다.
GROQ란 무엇이며 GraphQL과 어떻게 다른가요?
GROQ(Graph-Relational Object Queries)는 Sanity의 네이티브 쿼리 언어입니다. 구문 *[filter]{projection}은 Sanity 데이터의 경우 GraphQL보다 간결하며, -> 연산자를 통한 조인 및 계산된 필드에 대한 내장 지원을 제공합니다. GraphQL은 표준화된 도구를 선호하거나 이미 Apollo Client를 사용하는 팀을 위해 사용할 수 있습니다.
Sanity는 다국어 콘텐츠를 어떻게 처리하나요?
Sanity는 문서 수준 현지화(정규 참조로 연결된 언어별 별도 문서)와 필드 수준 현지화(하나의 문서 내에서 번역된 필드)를 지원합니다. 각 번역이 고유한 URL과 메타데이터를 갖기 때문에 문서 수준이 SEO에 더 좋습니다. 우리는 자동화된 번역 및 발행 파이프라인을 통해 10개 언어로 발행하기 위해 문서 수준 현지화를 사용합니다.