Astro와 Cloudflare D1 구축 가이드: 배포 오류 및 바인딩 트러블슈팅
최근 웹 생태계에서 Astro와 Cloudflare D1의 조합은 강력한 시너지를 내고 있습니다. 하지만 로컬 개발 환경에서 완벽하게 동작하던 코드가 실제 프로덕션 환경에 배포되는 순간, 예상치 못한 API 오류나 바인딩 문제로 멈춰 서는 경우가 많습니다. 오늘은 시니어 개발자의 시각에서 D1 구축 시 반드시 겪게 되는 트러블슈팅 사례와 그 해결책을 깊이 있게 다뤄보겠습니다.
목차
- D1 바인딩의 핵심: 로컬과 리모트의 분리
- 결정적 오류: database_id: “local”의 함정
- Cloudflare API 10021 오류와 권한 설정
- 실전 마이그레이션: 리모트 DB에 스키마 반영하기
- FAQ: 자주 묻는 질문
D1 바인딩의 핵심: 로컬과 리모트의 분리
Cloudflare D1은 SQLite 기반의 서버리스 데이터베이스입니다. 우리가 wrangler.json 또는 wrangler.toml에서 정의하는 d1_databases 설정은 단순한 설정값이 아니라, Astro 런타임과 실제 데이터베이스 인프라를 연결하는 브릿지(Bridge) 역할을 합니다.
개발자가 흔히 하는 실수 중 하나는 로컬 환경의 편의성을 위해 작성한 설정을 그대로 배포 환경에 밀어 넣는 것입니다. Cloudflare 엣지 런타임은 매우 엄격하며, 설정 파일에 정의된 Binding Name이 코드와 일치하지 않거나 ID값이 유효하지 않으면 즉시 프로세스를 차단합니다.
결정적 오류: database_id: “local”의 함정
로컬 개발 단계에서 database_id: "local"이라는 설정은 매우 유용합니다. Wrangler가 별도의 서버 연결 없이 로컬 디렉토리에 SQLite 파일을 생성해 주기 때문이죠. 하지만 이 설정이 포함된 상태로 배포를 시도하면 다음과 같은 에러를 마주하게 됩니다.
ERROR: binding DB of type d1 must have a valid
idspecified [code: 10021]
시니어의 조언: 배포용 설정 파일에는 반드시 Cloudflare 대시보드나 CLI를 통해 생성된 실제 UUID가 포함되어야 합니다. 로컬 개발 시에는 Wrangler가 알아서 .wrangler 폴더를 사용하므로, 설정 파일은 항상 리모트 기준으로 유지하는 것이 관리 포인트 중복을 줄이는 길입니다.
// 수정 전 (오류 발생 가능성 높음)
"d1_databases": [
{
"binding": "DB",
"database_name": "my-db",
"database_id": "local"
}
]
// 수정 후 (안전한 프로덕션 설정)
"d1_databases": [
{
"binding": "DB", // 코드에서 Astro.locals.runtime.env.DB로 접근
"database_name": "astro-blog-db",
"database_id": "41afd64f-xxxx-xxxx-xxxx-ca21035484bb" // 실제 생성된 UUID
}
]
Cloudflare API 10021 오류와 권한 설정
배포 중 발생하는 Cloudflare API 오류는 대부분 권한(Scope) 문제입니다. Astro 어댑터가 빌드된 에셋을 Cloudflare Pages나 Workers로 업로드할 때, 해당 API 토큰이 D1 스크립트를 수정할 수 있는 권한이 없다면 배포는 실패합니다.
이를 해결하기 위해서는 Cloudflare 대시보드에서 생성한 API 토큰에 다음 권한이 포함되어 있는지 확인해야 합니다:
- Account.Cloudflare Pages: Edit
- Account.Workers Scripts: Edit
- User.API Tokens: Read
💡 시니어의 팁 (Troubleshooting) 만약 모든 권한이 올바른데도 오류가 지속된다면,
wrangler.json내의compatibility_date를 확인하세요. Astro 5.0 이상을 사용 중이라면 최소2024-04-03이후의 날짜를 지정해야 현대적인 D1 바인딩 메커니즘이 정상 작동합니다.
실전 마이그레이션: 리모트 DB에 스키마 반영하기
로컬에서 테이블을 만들고 데이터를 넣었다고 해서 리모트 DB에 자동으로 반영되지는 않습니다. 리모트 환경은 완전히 독립된 인스턴스이기 때문입니다. 배포 후 서비스가 정상 동작하려면 반드시 Remote Execution 명령을 통해 스키마를 동기화해야 합니다.
# 1. 로컬 스키마를 실제 서버 DB에 적용
npx wrangler d1 execute <DB_이름> --remote --file=./schema.sql
# 2. 서버 DB에 테이블이 잘 생성되었는지 확인
npx wrangler d1 execute <DB_이름> --remote --command="SELECT name FROM sqlite_master WHERE type='table';"
이 과정에서 --remote 플래그를 누락하면 로컬 SQLite 파일만 업데이트되니 주의가 필요합니다. 시니어 개발자라면 이러한 과정을 **CI/CD 파이프라인(Github Actions)**에 녹여내어 자동화하는 것을 고려해야 합니다.
FAQ: 자주 묻는 질문
1. .env 파일에 DB_ID를 넣어도 작동하나요?
Astro의 환경 변수 시스템은 빌드 타임에 결정됩니다. 하지만 wrangler.json의 d1_databases 설정은 빌드 도구 자체가 참조하는 설정이므로, 가급적 JSON/TOML 파일 내에 직접 명시하거나 wrangler.toml의 [vars] 섹션을 활용하는 것이 안전합니다.
2. Binding Name은 아무거나 지어도 되나요?
기술적으로는 가능하지만, 관습적으로 DB 혹은 astro_blog_db처럼 용도가 명확한 이름을 사용합니다. 주의할 점은 코드 내에서 호출할 때(env.BINDING_NAME)와 설정 파일의 이름이 대소문자까지 완벽히 일치해야 한다는 것입니다.
3. D1에서 ‘Invalid URL string’ 에러가 납니다.
이는 대개 D1 자체의 문제라기보다, DB 응답을 받아 HTML을 렌더링하는 Astro Container 엔진의 URL 설정 문제입니다. astro.config.mjs의 site 주소를 확인하고, Request 객체에 완전한 URL(Origin 포함)이 주입되고 있는지 점검하세요.
오늘 살펴본 내용들을 바탕으로 설정 파일을 다시 점검해 보세요. 인프라 설정의 사소한 차이가 전체 서비스의 안정성을 결정짓습니다.