dev.to

🎡 Social Ferris Wheel: Sanity is the Hub

🎡 소셜 페리스 휠: Sanity를 중심에 둔 콘텐츠 파이프라인

1인 사업자가 Sanity를 콘텐츠 허브로 삼아 상품 정보와 지식 베이스를 읽고 소셜 게시물 초안을 만드는 시스템을 구축했습니다. 플랫폼별 검수와 승인 단계는 자동화했지만 실제 게시 기능은 아직 연결하지 않았습니다. 원문은 데이터 충돌 처리, 토큰 보관, 프롬프트 캐싱과 구축 과정의 문제를 함께 설명합니다.

AI 요약

1인 사업자가 온라인 상점의 상품 정보를 바탕으로 여러 소셜 플랫폼 게시물 초안을 만들고, 사람이 확인한 뒤 승인하는 콘텐츠 파이프라인을 구축했습니다. 콘텐츠 허브는 Sanity이며, 기존 Rails 7.1 상점과 Obsidian 메모, Sanity Knowledge Base, Claude 기반 에이전트, 게시 작업자와 검수 앱을 연결합니다. 자동 게시까지 구현하지 않은 점은 의도한 설계입니다. 승인 단계에서 사람의 판단을 남겨 두고, 시스템은 게시물의 상태를 ‘검수 필요’에서 ‘승인’으로 바꾸는 데 그칩니다.

구성과 검수 흐름

게시물 하나에 Instagram, Facebook 등 플랫폼별 변형(variant)을 여러 개 둡니다. 플랫폼마다 문구와 이미지가 다르고, 승인 여부도 따로 관리하므로 상태는 게시물 단위가 아니라 변형 단위에 기록합니다. Instagram 초안은 승인하고 TikTok 초안은 다시 작성하도록 돌려보내는 식입니다.

검수 화면은 Sanity App SDK로 만든 React 앱입니다. 게시물 문서 하나씩 편집하는 Studio와 달리, 여러 게시물의 플랫폼별 변형을 한곳에 모아 판단하는 큐로 구성했습니다. 검수자는 게이트의 판정과 함께 승인하거나 초안으로 되돌립니다. 앱은 Sanity 변경을 구독해 새 초안이 생기면 화면을 새로 고치지 않아도 목록에 반영합니다. 게시물은 화면 가운데 놓고 플랫폼별 카드를 바깥에 배치하는 페리스 휠 형태입니다. 카드 각도는 균등하게 나누고, 겹치지 않도록 타원형 배치의 세로 반지름을 계산합니다.

플랫폼별 글자 수 제한, 해시태그 수, 이미지 비율과 개수 같은 규칙은 lib/platformSpec.ts 한곳에 모았습니다. 미리보기, 게시 전 검사(preflight), 게시 작업자와 검수 앱이 같은 규칙을 읽어 결과가 어긋나지 않게 했습니다. 상품에 가격이나 이미지가 없으면 저장은 하되 게시를 막습니다. 제목·가격·이미지는 차단 조건이며, 설명과 카테고리는 경고 조건입니다. 플랫폼 제한 일부는 실제 개발자 문서와 대조해 수정 중이고, 영상 길이는 Sanity에 정보가 없어 검사하지 못합니다.

에이전트와 데이터 충돌 처리

Claude 에이전트는 상점 상품과 Sanity Knowledge Base를 읽고 계절과 주변 행사를 고려해 게시물 초안을 제안합니다. Knowledge Base 접근에는 Sanity Context MCP를 씁니다. 에이전트는 이미 게시물이 있는 상품을 제외하며, 제안은 상품 존재 여부와 판매 상태, 이미지 유무, 후크 문구의 길이, 캡션 사전 검사 규칙을 통과해야 기록됩니다. 초안에는 출처와 서로 다른 자료가 충돌한 지점도 남깁니다.

예를 들어 Knowledge Base는 특정 의류가 자외선에 반응한다고 적었지만 상품 목록에는 설명이 없었습니다. 또 바지 관리법을 두 자료가 다르게 안내했고, 일부 상품의 설명에는 XS~4XL이 적혔지만 장바구니에서 고를 수 있는 크기는 XXL까지였습니다. 에이전트는 충돌한 주장을 캡션에서 빼고, 검수 카드에 출처 간 불일치를 표시했습니다. 이 경고는 승인을 막지 않습니다. 초안이 문제의 정보를 이미 제외했기 때문입니다.

서비스 연결과 자격 증명

기존 상점은 Rails 7.1, PostgreSQL, Hotwire를 사용하며 집의 Jetson에서 실행합니다. Sanity Studio의 동기화 버튼은 상점에 직접 요청하지 않고 syncRun 문서를 생성합니다. 문서 생성 이벤트에 반응하는 Sanity Function이 상점 API를 호출하고, 실행 로그도 같은 문서에 기록합니다. 브라우저에는 상점 API 키가 필요하지 않습니다.

Sanity 토큰을 프로젝트 단위보다 좁게 제한하려면 Enterprise 요금제가 필요합니다. Rails 앱에 토큰을 두면 비공개 지식 메모까지 읽을 수 있으므로 게시 작업자가 토큰을 보관합니다. 이 작업자는 에이전트 실행을 담당하고, 상점에는 로컬 루프백을 통해 큐를 제공합니다. 게시 시도와 응답은 로그 한 줄 대신 publishAttempt 문서로 저장하도록 설계했습니다.

구축 과정에서 드러난 문제

Obsidian은 작성 공간으로 유지하고, 메모를 Sanity로 한 방향 동기화합니다. 상품 카탈로그는 상점에서 가져옵니다. 다만 지식 베이스 변경분이 서버에 반영되려면 개발자 컴퓨터에서 동기화를 실행해야 했고, 동기화 목록에 파일을 추가하지 않으면 에이전트가 오래되거나 불완전한 자료를 읽었습니다. 새 항목을 추가한 뒤 지식 베이스를 빌드해야 에이전트에 보인다는 점도 놓치기 쉬웠습니다.

Context MCP는 게시물 자체를 제공하지 않아, 첫 실행에서는 이미 초안이 있는 상품을 다시 제안했습니다. 해당 상품은 검사 단계에서 제외됐지만 실행 비용 0.49달러는 소모됐습니다. 이후 에이전트 프롬프트에 이미 게시물이 있는 상품을 미리 전달했습니다. 검수 앱에서도 일부 필드만 조회한 변형 배열을 그대로 다시 쓰면 자산 정보가 사라질 수 있었습니다. 작성자는 SDK 문서를 확인해 useEditDocument의 기능형 업데이트를 쓰는 방식으로 고쳤습니다.

프롬프트 캐싱 비용

작성자는 변하지 않는 지침과 도구 정의를 캐시에 넣고, 대화가 이어질 때 앞선 내용을 재사용하도록 프롬프트 캐싱을 구성했습니다. 실행 요약에는 캐시 읽기·쓰기와 비용 추정치를 출력합니다. 6회 실행에서 캐시 읽기 106,104토큰, 캐시 쓰기 50,122토큰이 기록됐고, 일반 입력으로 과금된 토큰은 12개였습니다. 같은 토큰을 캐싱 없이 계산하면 이 실행은 약 1.09달러이며, 캐싱 비용은 약 0.68달러로 약 0.41달러, 38%를 절약했다고 설명합니다. 별도로 작성자는 3개 초안을 만드는 실행이 약 1.50달러에서 0.39달러로 줄었고, 여섯 차례 실행의 실제 지출은 4.34달러였다고 적었습니다. 동일 토큰 기준 실행별 절감률은 47~62%였으며, 턴 수가 늘수록 절감 폭도 커졌습니다.

9월 27일 재실행에서는 8주 앞까지 살펴보고 이미 게시물이 있는 상품 네 개를 제외한 뒤 할로윈 초안 다섯 개를 만들었습니다. 여섯 턴에 약 0.58달러가 들었습니다. 게시물은 아직 어떤 플랫폼에도 실제로 올라가지 않습니다. TikTok과 LinkedIn 변형은 검수 화면의 네 갈래 구성을 위해 Instagram 문구를 복사한 예시 데이터이며, 실제 생성 결과가 아닙니다.

원문: dev.to / 번역·요약: Trawling