🚀 SvelteKit Cloudflare Blog 모노레포
Cloudflare Pages, D1, KV 및 SvelteKit을 사용하여 구축한 초고속 오픈소스 블로그 및 어드민 관리 시스템입니다. 본 프로젝트는 GNU Affero General Public License v3.0 (AGPL-3.0) 라이선스 하에 배포됩니다.
⚠️ 중요: D1 데이터베이스의 특성상 로컬 환경에서 로컬 데이터베이스만을 별도로 완벽하게 구동하여 테스트하는 것은 권장되지 않으며, 실제 클라우드플레어(Cloudflare) 플랫폼 상에 리소스를 배포하여 연동하는 방식으로 구동됩니다. 따라서 본 가이드는 실제 클라우드플레어 원격 환경에 리소스를 생성하고 빌드/배포하는 과정을 중심으로 설명합니다.
📋 목차
- 프로젝트 소개
- 의존성 설치
- Cloudflare 인프라 생성 및 초도 배포 (npm run setup)
- 환경변수 및 비밀값 자동 동기화 (.dev.vars)
- 개별 빌드 및 배포 명령어
- 라이선스
🔍 프로젝트 소개
이 프로젝트는 단일 Git 리포지토리 내에서 블로그 프론트엔드와 어드민 대시보드를 관리할 수 있는 모노레포 구조로 구성되어 있으며, 실제 클라우드플레어의 에지 서비스들과 결합하여 동작합니다.
- apps/blog: 누구나 방문하여 글을 읽을수 있고 소셜 로그인, 또는 이메일로 가입하여 댓글/방명록을 작성할 수 있는 블로그 프론트엔드 (SvelteKit)
- apps/admin: 게시글 작성, GA4 통계 및 애드센스 조회, 레이아웃/디자인 변경을 담당하는 어드민 대시보드 (SvelteKit)
- packages/shared: Drizzle ORM 스키마, 데이터베이스 구조 정의, 유틸리티, 다국어(i18n) 설정 등이 포함된 공유 패키지
🛠️ 의존성 설치
루트 디렉토리에서 아래 명령어를 실행하여 모노레포 내의 전체 의존성을 링크 및 설치합니다:
npm install
npm audit fix
☁️ Cloudflare 인프라 생성 및 초도 배포 (npm run setup)
이 프로젝트는 클라우드플레어의 D1(SQL DB) 및 KV(키-값 저장소)를 데이터 공간으로 사용합니다. 첫 가동 시 아래의 원클릭 셋업 스크립트를 사용하여 실제 클라우드플레어 원격 플랫폼 상에 리소스를 자동으로 구축하고 초도 배포를 완료합니다.
Tip: 기존 인프라 데이터를 날리지 않고 설정 정보(wrangler.backup.json 등)만 백업에서 안전하게 복구하여 재배포하고 싶다면
npm run restore명령어를 사용해 복구 모드로 실행하십시오.
npm run setup
⚙️ npm run setup 실행 시 진행되는 작업:
- Cloudflare 계정 인증: Wrangler CLI를 통해 클라우드플레어 계정에 로그인합니다 (
wrangler login). - 인프라 자동 생성: 원격 클라우드플레어에 D1 데이터베이스(
BLOG_DB,USER_DB)와 KV 네임스페이스(IMAGES_KV)를 유저가 정한 이름으로 신규 생성합니다. 그냥 '엔터'를 누르면 랜덤 문자열이 포함된 값으로 자동 설정합니다. - 원격 마이그레이션 실행: 생성된 원격 D1 데이터베이스에 테이블 스키마를 구성하고, 선택한 기본 언어(한국어, 영어, 일본어)에 따른 초기 시드(Seed) 데이터를 직접 주입합니다.
- 설정 자동 동기화: 리소스 고유 ID 값들을 각 앱의
wrangler.json에 자동으로 주입하고, 향후 배포 프로젝트명을 사용자에게 입력받아 설정합니다. 그냥 '엔터'를 누르면 랜덤 문자열이 포함된 값으로 자동 설정합니다. - 보안 환경변수 및 배포: 로컬의
.dev.vars내 비밀값들을 Pages Secret으로 자동 전송한 후, 앱을 빌드하여 Cloudflare Pages에 최종 배포합니다. 최최 배포 시에는 환경 변수 등록이 불가능한 상태(pages의 부재)이므로 최초 배포 이후.dev.vars내 값을 수정 후 1회만 재배포를 하면 자동으로 비밀 환경 변수에 값이 입력됩니다.
🔐 환경변수 및 비밀값 자동 동기화 (.dev.vars)
애플리케이션 구동에 필요한 비밀키(OAuth 키, API 계정 정보 등)는 각 앱의 .dev.vars 파일에 템플릿 형태로 공유되고 있습니다. 이 파일은 깃에 공개되므로, 실제 개인키를 깃에 기재해서 커밋하면 절대 안 됩니다.
사용자는 로컬의 .dev.vars 파일 내의 값을 본인의 실제 키 정보로 수정해 두기만 하면 됩니다. 배포 명령어를 실행할 때 스크립트가 내부적으로 값을 읽어 **Cloudflare Pages Secret으로 안전하게 일괄 자동 업로드(wrangler pages secret bulk)**해 줍니다. (대시보드 웹페이지에 직접 들어가 수동으로 적어줄 필요가 없습니다.)
1. apps/blog/.dev.vars (블로그 OAuth 및 로그인 인증 비밀값)
BETTER_AUTH_SECRET=여러분의_무작위_비밀_문자열_입력 (최소 32자 권장)
GITHUB_CLIENT_ID=여러분의_GITHUB_OAuth_앱_클라이언트_ID
GITHUB_CLIENT_SECRET=여러분의_GITHUB_OAuth_앱_비밀키
GOOGLE_CLIENT_ID=여러분의_GOOGLE_OAuth_앱_클라이언트_ID
GOOGLE_CLIENT_SECRET=여러분의_GOOGLE_OAuth_앱_비밀키
2. apps/admin/.dev.vars (어드민 접속 및 구글 API 연동 비밀값)
GA4_PRIVATE_KEY의 경우 반드시 쌍따옴표 안에 예시와 같은 형태로 값을 넣어야 작동합니다.
ADMIN_PASSWORD=여러분의_어드민_접속_비밀번호
GA4_PROPERTY_ID=여러분의_GA4_속성_ID
GA4_CLIENT_EMAIL=여러분의_구글_서비스_계정_이메일
GA4_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n여러분의_GA4_서비스계정_비공개키_값\n-----END PRIVATE KEY-----\n"
ADSENSE_ACCOUNT_ID=accounts/pub-여러분의_애드센스_게시자_ID
ADSENSE_CLIENT_ID=여러분의_애드센스_API_OAuth_클라이언트_ID.apps.googleusercontent.com
ADSENSE_CLIENT_SECRET=여러분의_애드센스_API_OAuth_비밀키
ADSENSE_REFRESH_TOKEN=여러분의_애드센스_OAuth_리프레시_토큰
🔒 어드민 접속 IP 제한 (ALLOWED_IP) 자동 처리
- 보안 유지를 위한 어드민 접속 허용 IP 제어 설정(
ALLOWED_IP)은 대시보드에 직접 입력하거나 파일을 수정할 필요가 없습니다. - 배포 스크립트 실행 과정에서
sync-secrets.js파일이 사용자의 현재 공인 IP 주소를 자동으로 감지하여apps/admin/.dev.vars에 동적으로 갱신해 주며, 이 정보 역시 Cloudflare Pages Secret으로 자동 주입됩니다. - 이후 배포한 IP 주소 이외의 다른 IP로는 admin페이지로 접근 자체가 불가능합니다. IP 주소가 변경된 경우 DB를 유지하는 형태로 재배포를 하거나 DB에서 직접 IP를 수정해야 접속이 가능해집니다.
🚀 개별 빌드 및 배포 명령어
초기 setup을 통해 리소스 생성이 완료된 후, 코드가 수정되거나 비밀키가 변경되어 개별적으로 프로젝트를 다시 배포할 때는 아래 명령어를 사용합니다. (해당 명령어 실행 시 자동으로 로컬 비밀값이 원격 Pages Secret에 동기화된 후 빌드 및 업로드가 완료됩니다.)
- 블로그(Blog) 서비스 배포:
npm run deploy:blog - 어드민(Admin) 대시보드 배포:
npm run deploy:admin
📄 라이선스
이 프로젝트는 GNU Affero General Public License v3.0 (AGPL-3.0) 라이선스에 따라 오픈소스로 배포됩니다. 자세한 내용은 루트의 LICENSE 파일을 참조하십시오.
AGPL-3.0 조건에 따라 본 소프트웨어를 수정하거나 네트워크를 통해 서비스로 제공하는 경우, 해당 수정 버전에 대한 전체 소스 코드를 사용자에게 무상으로 공개해야 합니다.
댓글 0개
댓글을 작성하려면 로그인이 필요합니다.