본문으로 건너뛰기
Acecore

Sveltia CMS 도입 가이드

by Gui
목차
Sveltia CMS 도입 가이드

Sveltia CMS는 정적 사이트에 편집 화면을 추가하고 싶지만 외부 데이터베이스를 늘리고 싶지 않을 때 유용합니다. 이 글은 Acecore의 Astro 사이트에 Sveltia CMS를 도입한 방식과, 이후 PR과 commit을 통해 드러난 문제를 어떻게 고쳤는지 정리합니다.

제목은 의도적으로 단순하게 Sveltia CMS 도입 가이드 로 정했습니다. CMS 비교 글이 아니라, 다른 사이트에 바로 적용할 수 있는 설계 메모입니다.

Sveltia CMS가 맞는 경우

Sveltia CMS는 별도 데이터베이스와 API를 가진 CMS가 아닙니다. 브라우저에서 동작하는 SPA가 GitHub backend를 통해 저장소 파일을 편집합니다.

다음 조건에 잘 맞습니다.

  • 콘텐츠가 Markdown 또는 JSON으로 저장소에 있음
  • 기사, 작성자, 태그, 페이지 문구 변경을 Git diff로 리뷰하고 싶음
  • 외부 DB나 별도 관리자 서버를 추가하고 싶지 않음
  • 이미지를 public/uploads 같은 저장소 디렉터리에 둘 수 있음
  • CMS 저장 후에도 Pull Request로 확인하고 배포하고 싶음

복잡한 권한, 예약 발행, 대량 미디어 관리, 실시간 데이터 편집이 필요하면 다른 headless CMS나 전용 관리자 화면이 더 적합합니다.

전체 구조

public/admin/index.html
  -> CDN에서 @sveltia/cms 로드

public/admin/config.yml
  -> GitHub backend, collections, media folder 정의

workers/sveltia-cms-auth
  -> GitHub OAuth용 Cloudflare Worker

main branch
  -> 운영 환경의 유일한 기준

CMS save proxy
  -> 허용 경로를 검증하고 변경마다 단기 branch와 main 대상 PR 생성

.github/workflows/create-translation-prs.yml
  -> cms: commit에만 번역 PR task 생성

관리 화면을 두는 것만으로는 충분하지 않습니다. 인증, 이미지 경로, preview branch, 번역, merge 방식까지 CMS 설계의 일부입니다.

1. public/admin에 관리 화면을 둔다

Astro에서는 public 아래 파일이 정적 파일로 배포됩니다. Sveltia CMS 공식 문서도 Astro, Next.js, Nuxt, Remix, VitePress의 정적 폴더로 public을 안내합니다.

<!doctype html>
<html lang="ko">
  <head>
    <meta charset="utf-8" />
    <meta name="robots" content="noindex,nofollow" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>CMS</title>
  </head>
  <body>
    <script src="https://unpkg.com/@sveltia/cms@0.172.4/dist/sveltia-cms.js"></script>
  </body>
</html>

불필요한 CSS나 type="module"을 추가하지 않습니다. 현재 CDN bundle은 일반 script로 읽는 구성이 자연스럽습니다.

Acecore에서는 backend를 명시적으로 설정하기 위해 수동 초기화를 사용합니다. publication branch는 모든 환경에서 main으로 유지합니다.

CMS.init({
  config: {
    backend: {
      branch: 'main',
    },
  },
})

2. GitHub backend 설정

최소 설정은 backend.namebackend.repo입니다. 실제 운영에서는 branch, OAuth, commit message도 처음에 정하는 편이 안전합니다.

backend:
  name: github
  repo: owner/repository
  branch: main
  base_url: https://your-sveltia-cms-auth-worker.example.workers.dev
  api_root: /admin/api/github
  graphql_api_root: /admin/api/graphql
  auth_methods: [oauth]
  commit_messages:
    create: 'cms: create {{collection}} "{{slug}}"'
    update: 'cms: update {{collection}} "{{slug}}"'
    delete: 'cms: delete {{collection}} "{{slug}}"'
    uploadMedia: 'cms: upload "{{path}}"'
    deleteMedia: 'cms: delete media "{{path}}"'

main을 publication branch로 유지하고 읽기와 저장을 same-origin proxy로 보냅니다. proxy는 단기 branch와 PR을 만들기 전에 repository, GitHub 사용자의 쓰기 권한, 변경 경로, 최신 main HEAD를 검증합니다.

2026년 7월 20일 기준으로 Sveltia CMS에는 Editorial Workflow가 구현되어 있지 않습니다. Decap CMS의 publish_mode: editorial_workflow 설정을 추가해도 Sveltia CMS가 단기 branch나 PR을 자동 생성하지는 않습니다.

cms-content 같은 영구 branch는 지속적인 동기화가 필요하며 충돌이나 잘못된 배포 source 설정 위험을 높입니다. PR마다 단기 branch를 닫으면 main을 유일한 기준으로 유지할 수 있습니다.

3. OAuth Worker 준비

Personal Access Token은 테스트에는 충분하지만 여러 편집자가 쓰기에는 적합하지 않습니다. Acecore는 Sveltia CMS Authenticator를 Cloudflare Workers에서 실행하고 base_url로 설정합니다.

GitHub OAuth App의 callback은 Worker의 /callback으로 향합니다. Worker에는 GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, 필요하면 ALLOWED_DOMAINS를 설정합니다.

Turnstile과 역할이 다릅니다. CMS 로그인은 GitHub OAuth가 담당하고, 폼이나 댓글 API의 bot 대책은 Turnstile이 담당합니다.

4. 이미지 업로드 위치를 먼저 정한다

Sveltia CMS internal media storage는 업로드 파일을 저장소에 저장합니다. Astro에서는 다음 구성이 실용적입니다.

media_folder: public/uploads
public_folder: /uploads

Acecore는 나중에 PR #116에서 이 부분을 수정했습니다. 저장소 경로와 공개 URL은 CMS 도입 시점에 함께 정해야 합니다.

5. collection으로 편집 범위 나누기

collection대상방침
blogsrc/content/blog/*.md일본어 source 기사만 편집
authorssrc/content/authors/*.json작성자 정보와 다국어 표시명 편집
tagssrc/content/tags/*.json태그명과 다국어 표시명 편집
page textsrc/i18n/source/ja/**/*.json페이지와 공통 UI의 일본어 source 편집

모든 번역 Markdown을 CMS에 노출할 필요는 없습니다. Acecore는 일본어를 source of truth로 두고, 번역은 Sveltia CMS로 다국어 블로그를 운영하는 방법으로 반영합니다.

6. relation과 select 사용

태그는 자유 입력보다 relation이 안전합니다.

- name: tags
  label: 태그
  widget: relation
  collection: tags
  value_field: name
  display_fields: ['{{name}} ({{id}})']
  search_fields: [name, id]
  multiple: true
  required: false

작성자, 아이콘, 공지 tone도 같은 방식으로 제한합니다. CMS의 가치는 편집 가능성뿐 아니라 잘못된 값을 넣기 어렵게 만드는 데 있습니다.

7. 일본어 source JSON도 편집

고정 페이지 문구도 CMS화할 수 있습니다. Acecore는 src/i18n/source/ja/**/*.json에 일본어 source를 모으고 페이지별로 CMS에 노출합니다.

반성점은 한 번에 모든 필드를 넣지 않는 것입니다. config.yml이 급격히 커지면 리뷰와 유지보수가 어려워집니다. 블로그, 작성자, 태그, 공지, 자주 바뀌는 페이지부터 시작하는 편이 좋습니다.

8. preview에서도 publication branch를 main으로 유지한다

저장 proxy는 최신 main에서 모든 단기 branch를 만듭니다. 따라서 Cloudflare Pages preview가 backend branch를 해당 PR branch로 바꾸면 안 됩니다. 저장 결과는 생성된 CMS PR의 Pages preview에서 확인합니다.

CMS.init({
  config: {
    backend: {
      branch: 'main',
    },
  },
})

9. 저장 proxy로 단기 branch와 PR 만들기

same-origin 저장 proxy가 변경마다 단기 branch와 main 대상 PR을 만듭니다.

backend:
  name: github
  repo: owner/repository
  branch: main
  api_root: /admin/api/github
  graphql_api_root: /admin/api/graphql

proxy는 main에 직접 commit하지 않습니다. 허용된 content와 media를 한 commit으로 묶고 최신 main에서 cms/acecore/*를 만든 뒤 PR을 엽니다. 검토와 merge 후 단기 branch를 자동 삭제합니다.

GitHub OAuth 방식에서는 편집자의 token이 identity와 쓰기 actor 역할을 합니다. Cloudflare Access 방식에서는 사이트 전용 GitHub App이 actor가 될 수 있습니다. 경로 제한, 단기 branch, PR, CI 정책은 두 방식 모두 같습니다.

merge 방식도 중요합니다. 번역 workflow는 cms: create ..., cms: update ... 같은 commit subject를 봅니다. squash merge로 subject가 사라지면 자동화가 감지하지 못할 수 있으므로 CMS PR은 merge commit 또는 rebase merge가 적합합니다.

10. CMS commit만 번역 트리거

PR #98에서는 --cms-only를 추가해 push 기반 번역 PR task가 CMS commit에만 반응하도록 했습니다.

function isCmsCommitSubject(subject) {
  return /^cms: (create|update|delete) /.test(subject || '')
}

cms:는 장식이 아니라 workflow 계약입니다.

11. /admin 전용 CSP

관리 화면은 CDN, GitHub API, OAuth Worker, blob URL에 접속합니다. 그래서 Acecore는 /admin/*에 공개 페이지와 다른 CSP를 적용하고 noindex도 설정합니다.

Turnstile은 분리한다

이 글의 이전 버전은 CMS와 Cloudflare Turnstile을 한 글에 섞었습니다. 지금 보면 주제가 흐려졌습니다.

Sveltia CMS는 GitHub backend, OAuth, collections, media, PR 운영의 문제입니다. Turnstile은 폼이나 댓글 API의 bot 대책입니다. 둘은 같은 보안 운영에 기여하지만 레이어가 다릅니다.

PR과 commit에서 얻은 교훈

  • CMS가 바뀌면 기사와 내부 링크도 함께 업데이트해야 합니다.
  • OAuth는 나중 일이 아니라 실제 도입 범위입니다.
  • 이미지 경로는 업로드 전에 고정해야 합니다.
  • config.yml은 단계적으로 확장해야 합니다.
  • cms:는 자동화 계약입니다.
  • publication branch는 환경별로 바꾸지 않고, preview에서는 CMS PR 결과를 확인합니다.

최소 시작점

public/admin/index.html
public/admin/config.yml
public/admin/init.js
public/admin/runtime-config.js

그 다음 작성자 relation, 태그 relation, 이미지, source JSON, CMS PR 자동화, 번역 PR task 순서로 넓히면 됩니다.

참고 링크

정리

Sveltia CMS를 public/admin에 두는 것은 쉽습니다. 하지만 운영하려면 저장 branch, OAuth, media folder, source 언어 정책, 번역 workflow, merge 전략까지 정해야 합니다. 이 규칙이 명확하면 Astro 정적 사이트의 가벼움을 유지하면서도 실제로 쓰기 쉬운 콘텐츠 운영을 만들 수 있습니다.

Sveltia CMS 도입 흐름

관리 화면, 인증, 편집 대상, 미디어, PR 운영을 각각 분리해 설계합니다.

관리 화면 배치

public/admin 아래에 index.html과 config.yml을 두고 Sveltia CMS를 로드합니다.

GitHub backend 설정

repo, branch, OAuth Worker, CMS commit message를 먼저 정합니다.

편집 범위 제한

블로그, 작성자, 태그, 일본어 source JSON처럼 CMS에서 다룰 파일만 collection으로 노출합니다.

운영 자동화

main을 publication branch로 사용하고 검증된 저장 proxy의 단기 branch, CMS 편집 PR, 번역 PR task를 연결합니다.

CMS 도입 전후

Markdown 직접 편집

  • GitHub나 에디터에 익숙한 사람만 쉽게 수정할 수 있음
  • 이미지 경로, 작성자 ID, 태그명을 수동 입력하기 쉬움
  • 일본어 source와 번역 파일 수정 범위가 섞이기 쉬움
  • 저장 대상과 쓰기 가능한 경로가 불명확해질 수 있음

Sveltia CMS 편집

  • 브라우저 폼에서 Markdown과 JSON을 편집할 수 있음
  • relation, image, select로 잘못된 값을 줄임
  • CMS commit만 번역 PR task를 트리거함
  • same-origin proxy가 허용 경로만 단기 branch와 PR에 저장함
도입 체크리스트
  • 완료: public/admin/index.html에서 Sveltia CMS 로드
  • 완료: public/admin/config.yml에 GitHub backend와 collections 정의
  • 완료: 여러 사용자가 편집한다면 OAuth Worker 사용
  • 완료: media_folder와 public_folder를 Astro public 디렉터리에 맞춤
  • 완료: CMS commit이 번역 또는 배포 workflow를 어떻게 트리거할지 결정
자주 묻는 질문
Sveltia CMS는 어떤 사이트에 적합한가요?
Markdown이나 JSON이 저장소에 있는 정적 사이트에 잘 맞습니다. Astro, Hugo, VitePress처럼 콘텐츠를 Git으로 관리하는 사이트라면 외부 DB 없이 CMS를 추가할 수 있습니다.
GitHub Personal Access Token만으로도 되나요?
가능합니다. 하지만 여러 명이나 비개발자가 쓴다면 OAuth Worker가 더 안전하고 설명하기 쉽습니다. Acecore는 Cloudflare Worker를 OAuth 클라이언트로 사용합니다.
다국어 사이트는 모든 언어를 CMS에서 편집해야 하나요?
작은 팀에서는 일본어 source만 CMS에서 편집하고 번역은 PR로 반영하는 편이 안전합니다. 모든 언어를 노출하면 리뷰와 오래된 번역 감지가 어려워집니다.

댓글

댓글을 불러오는 중...

URL, 이메일 주소, 홍보 문구는 게시할 수 없습니다.

G

Gui

Acecore 대표. 업무 시스템, 웹, DB/인프라, 품질, AI 활용을 사업 과제 정리부터 설계, 도입 후 개선까지 이어지는 흐름으로 추진합니다. C#/.NET 기반의 실무 구현력을 바탕으로 PHP/JavaScript, SQL Server/PostgreSQL/MySQL, Linux/Windows Server도 고려하며, 요구사항 정리, 기술 선택, 품질 기준, GitHub 기반 개발 운영을 하나의 흐름으로 설계합니다. 생성형 AI는 개발, 검증, 정보 정리 등 업무 프로세스에 도입해 소규모 팀도 빠르고 확실하게 성과를 낼 수 있는 실무 기반으로 활용하고 있습니다.

사업 과제 정리기술 선택시스템 설계C#/.NETDB/인프라 설계GitHub 개발 운영생성형 AIAI 워크플로 설계품질 설계현장 연동

서비스에 대해 더 알고 싶으신가요?

시스템 개발, 웹 디자인, 서버 운영, 그래픽 디자인을 포괄적으로 지원합니다.

관련 기사

사이트 검색

2자 이상 입력하면 관련 콘텐츠를 찾기 위해 검색어가 Cloudflare Workers AI로 자동 전송됩니다. 개인정보나 기밀정보를 입력하지 마세요.검색 데이터 처리 방법