BinaryHub 개발기 ④ StudyAce — 같은 우산 아래 다른 결의 사이트
BinaryHub 개발기. 같은 우산 아래여도 사이트마다 호스팅 결이 다를 수 있다.
시작은 친구들과 쓰던 것
학교 시험 기간이 되면 한국사·중국어를 같이 풀던 친구들이 있었다. 처음엔 그냥 카톡으로 문제 사진을 주고받았는데, 점점 정리할 게 늘어났다. 단원별 요약·기출 문제·암기 카드·오답 노트… 결국 사이트로 만들었다.
이름은 StudyAce, 주소는 study.binaryhub.club. 처음엔 BinaryHub와 별개로 띄웠는데, 우산 브랜드를 정리하면서 자연스럽게 그 안으로 들였다. 서브도메인 하나 추가하는 거니까 어렵지 않았다.
다른 사이트와 결이 다른 점
다른 사이트들과 다른 점이 많았다.
- 사용자 인증이 필요하다. 친구들 계정만 들어올 수 있어야 한다.
- 데이터를 저장해야 한다. 누가 어느 단원을 풀었는지, XP는 얼마인지, 오답이 어디였는지.
- 관리자 UI가 있어야 한다. 가입 요청을 승인하고, 건의사항을 보고, 트랙(
student_free/student_sponsor/gbs_free/gbs_sponsor)을 부여한다.
블로그처럼 마크다운만 정적으로 빌드해서 끝낼 수가 없다. 그렇다고 한컴 COM이 필요한 것도 아니라서, 운영 PC에 올릴 이유도 없다. 결국 Cloudflare가 답이었다.
Cloudflare Workers + Static Assets + D1
StudyAce는 처음부터 Cloudflare에서 굴렸다.
- 정적 자산 —
index.html+ 학습 데이터 JSON + 클라이언트 JS.[assets]binding으로 서빙된다. - API 라우트 —
functions/api/*. 로그인, 건의사항 제출, 관리자 작업, XP 누적 등. 이게 Worker 코드로 컴파일된다. - DB — Cloudflare D1 (SQLite). 사용자·진행도·건의사항·랭킹.
wrangler.jsonc 한 파일에 다 묶여 있다.
{
"main": "./dist/worker/index.js",
"assets": { "directory": "./dist/client/", "binding": "ASSETS" },
"d1_databases": [{ "binding": "DB", "database_name": "studyace-db", ... }]
}배포는 npx wrangler deploy 한 번. 로컬에서 빌드하고 결과물을 Cloudflare에 업로드하는 모델이라, GitHub Actions를 따로 깔지 않고 쓰고 있다.
정적은 무료, API만 카운트된다
Cloudflare Workers 무료 플랜은 하루 10만 요청까지다. 처음엔 "100명이 매일 천 번씩 쓰면 한도가 빡빡한 거 아닌가"가 신경 쓰였다.
근데 잘 보니 — 정적 자산 응답은 카운트되지 않는다. assets binding으로 매칭되는 요청은 Worker 코드가 실행되지 않기 때문이다. 100,000번 한도가 적용되는 건 진짜로 Worker 코드가 도는 API 라우트뿐. StudyAce 한 명이 천 번 들어와도 그 대부분은 HTML·CSS·JS·이미지·학습 데이터 JSON으로, 다 무료다. API 호출은 로그인·XP 갱신·건의사항 정도라 한 명당 수십~수백 번 수준이다.
지금 트래픽에선 여유가 충분하다. 더 늘어나면 그때 Workers Paid($5/월, 1천만 요청/월)로 올리면 된다.
D1과 마이그레이션의 두려움
D1은 SQLite 호환이라 schema.sql을 그대로 적용하면 된다. 다만 운영 DB에 새 컬럼을 추가하는 작업은 매번 조심스럽다. 한 번은 phone·affiliation·info_consent 세 컬럼을 추가해야 했는데, 운영 DB에 ALTER TABLE을 직접 쳤다.
다행히 SQLite의 ALTER TABLE ADD COLUMN은 안전한 편이고, D1도 잘 받아 줬다. 새 컬럼은 기본값 NULL로 들어가서 기존 행에 영향 없이 끝났다. 그래도 운영 DB를 손대는 작업은 매번 손이 떨린다.
트랙 시스템
가입 요청을 admin이 승인할 때, 사용자에게 트랙을 부여한다. 트랙 네 종류:
student_free— 외부 학교 학생, 기본 기능만student_sponsor— 외부 학교 학생 중 후원자, 일부 프리미엄 기능gbs_free— 경기북과학고 학생, 기본 기능gbs_sponsor— 경기북과학고 학생 중 후원자
프리미엄 기능 게이팅은 학교 종류와 무관하게 sponsor 여부로만 판단한다. 학교 라벨은 통계와 환영 메시지에만 쓴다.
SEO와 AdSense는 살짝 분기
StudyAce에도 AdSense는 깔려 있다. BinaryHub 전체가 단일 publisher 계정이라, 한 번 등록한 ads.txt가 모든 서브도메인에 자동으로 적용된다.
다만 SEO는 BBlog만큼 본격적으로 하진 않는다. StudyAce는 검색으로 들어오는 일반 방문자를 받는 사이트가 아니라, 친구들이 직접 들어오는 사이트라서. sitemap·robots는 둬서 크롤러는 들이지만, OG 이미지·구조화 데이터까지 깔진 않았다.
정리
같은 BinaryHub 우산 아래 사이트라고 다 똑같이 운영하지 않는다. 블로그는 운영 PC에서 Next.js로, 무쓸모는 Cloudflare Pages로, StudyAce는 Cloudflare Workers + D1로. 사이트의 요구가 호스팅을 정한다.