← 문서 묶음 · 기술 명세서 · 출시·운영
bruni · 게임개발 환경·인프라 명세서
GitHub / CI / Infra / State
브루니 키우기: 바랜 섬
개발 환경·인프라 명세서
만든 날 2026-10-13 · 고친 날 2026-10-15 — 깃 이력에서 자동으로 박혀요
저장소 · 자동 검사 · 비밀값 · 서버 · 상태 관리 · 새 컴퓨터 세팅
| 항목 | 내용 |
|---|
| 문서 성격 | 아직 만들지 않은 것의 설계도입니다. 개발을 시작하는 날 이 문서대로 세팅합니다 (2026-10-13 주인 지시 — 미리 작업하지 않고 문서로만) |
| 기준 문서 | 기술 명세서(폴더·오토로드·서버 엔드포인트·D1 테이블) · 게임 개발 사양서(스토어·비용·행정) |
| 원칙 | 모든 것은 깃에 — 코드·데이터·서버 설정·DB 구조·작업 진행까지. 주인은 언제든 다른 컴퓨터로 옮길 수 있어야 합니다 |
| 현재 준비 | 이 맥에 Godot 4.7.2 설치 완료(2026-10-13, Homebrew). 나머지는 미설치 — 11장 순서대로 |
bruni01
bruni · 게임개발 환경·인프라 명세서
Overview / Contents
문서 개요 및 목차
게임 코드를 어디에 두고, 누가(사람·AI·자동 검사) 무엇을 확인하고, 비밀값을 어디에 숨기고, 서버를 어떻게 올리고, 게임·서버·인프라·작업 진행의 「지금 상태」를 무엇으로 관리하는지 정합니다.
bruni02
bruni · 게임개발 환경·인프라 명세서
01 / Repositories
저장소 구성
| 저장소 | 공개 | 담는 것 | 진실의 자리 |
|---|
| bruni-world (기존) | 비공개 | 설계 문서(/me/idle-rpg) · game-design/ (바이블·조사·설계 데이터·생성 스크립트) | 기획·수치·이름의 기준 |
| bruni-idle (새로) | 비공개 | Godot 프로젝트 · 서버(server/) · 도구(tools/) · CI | 실행되는 코드의 기준 |
bruni-idle 최상위 폴더
bruni-idle/
project.godot export_presets.cfg ← 키스토어 비밀번호는 넣지 않음(환경변수)
autoload/ core/ scenes/ ui/ shaders/ assets/ addons/ android/
data/ ← tools/export_data.py 산출물. 직접 고치지 않음
tests/unit tests/golden tests/save
tools/ ← export_data.py · validate_data.py · prob_publish.py · bump_version.py
server/ ← Cloudflare Worker (src/*.ts · migrations/*.sql · wrangler.jsonc)
design/ ← bruni-world/game-design/data 복사본 (export 입력)
.github/workflows/ ← ci.yml · android.yml · server.yml · data-sync.yml
CLAUDE.md ← 이 저장소에서 일하는 AI 를 위한 규칙 (10장)
docs/STATE.md ← 지금 어디까지 했나 한 장 (10장)
bruni-worldgame-design/data
복사
bruni-idledesign/
export_data.py
bruni-idledata/*.json
왜 두 저장소인가기술 명세서 2장의 결정입니다. 게임 코드는 빌드·바이너리·플러그인이 커지고 CI 가 돌아서 문서 저장소와 섞으면 둘 다 무거워집니다. 대신 설계 데이터의 원본은 bruni-world 하나로 두고, 게임 저장소에는 복사본과 복사한 커밋 해시를 남깁니다(9장). 서버는 게임과 같이 바뀌므로 같은 저장소 server/에 둡니다.
bruni03
bruni · 게임개발 환경·인프라 명세서
02 / Branch & Version
브랜치·커밋·버전
| 항목 | 규칙 |
|---|
| 브랜치 | main 하나. 1인 개발이라 풀 리퀘스트 절차는 두지 않습니다. 큰 실험만 try/이름 브랜치 → 끝나면 main 에 합치고 지움 |
| main 의 약속 | main 은 항상 CI 통과 상태. 깨진 채로 하루를 넘기지 않음 |
| 커밋 | 작업 한 덩어리마다 커밋 + 바로 푸시. 메시지 앞머리: feat · fix · data · server · ci · docs |
| 앱 버전 | 1.0.3 꼴. tools/bump_version.py 가 project.godot 의 version 과 Android versionCode 를 같이 올림 |
| 앱 태그 | v1.0.3 — 이 태그를 밀면 android.yml 이 AAB 를 만듦 (3장) |
| 서버 태그 | server-v12 — 이 태그를 밀면 server.yml 이 D1 마이그레이션·배포 (3장) |
| 데이터 버전 | data/manifest.json 의 data_ver = 날짜 + 원본 커밋 해시 (9장) |
| 저장 형식 버전 | 저장 파일 version — 형식이 바뀔 때만 +1, core/save_migrate.gd 에 이전 함수 추가 (7장) |
버전 넷을 섞지 않는다앱 버전 · 서버 태그 · 데이터 버전 · 저장 형식 버전은 따로 움직입니다. 이벤트 기록 공통 필드에 ver(앱)과 data_ver(데이터)가 같이 실려서, 문제가 생기면 어느 쪽이 바뀌어서인지 기록만 보고 가를 수 있습니다.
bruni04
bruni · 게임개발 환경·인프라 명세서
03 / CI
자동 검사·빌드 (GitHub Actions)
| 워크플로 | 언제 돌아가나 | 하는 일 | 실패하면 |
|---|
| ci.yml | main 에 푸시할 때마다 | ① validate_data.py 데이터·스키마 검사 ② Godot 4.7.2 헤드리스로 프로젝트 가져오기 ③ GUT 테스트(단위·골든 전투·저장 이전) ④ 서버 tsc 타입 검사 + vitest(모집 분포 100만 회 포함) | main 빨간불 — 그날 안에 고침 |
| android.yml | v* 태그 | 키스토어를 비밀값에서 복원 → --export-release "Android" → AAB 를 아티팩트로 보관(90일) | 태그 지우고 다시 |
| server.yml | server-v* 태그 또는 수동 | wrangler d1 migrations apply → wrangler deploy → GET /v1/time 응답 확인 | 직전 태그로 되돌려 배포 |
| data-sync.yml | 수동 | bruni-world 의 game-design/data 를 design/ 로 복사 → export → 검사 → 커밋 (9장) | 복사 취소, 원본에서 고침 |
ci.yml 뼈대
on: { push: { branches: [main] } }
jobs:
game:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: python3 tools/validate_data.py
- name: Godot 4.7.2 받기
run: | # 공식 빌드 저장소 godotengine/godot-builds 의 4.7.2-stable 리눅스 판
curl -sSLo g.zip "https://github.com/godotengine/godot-builds/releases/download/4.7.2-stable/Godot_v4.7.2-stable_linux.x86_64.zip"
unzip -q g.zip && mv Godot_v4.7.2-stable_linux.x86_64 godot
- run: ./godot --headless --import
- run: ./godot --headless -s addons/gut/gut_cmdln.gd -gdir=res://tests -gexit
server:
runs-on: ubuntu-latest
defaults: { run: { working-directory: server } }
steps:
- uses: actions/checkout@v4
- run: npm ci && npx tsc --noEmit && npx vitest run
iOS 는 CI 에서 빌드하지 않는다GitHub 의 macOS 실행기는 비공개 저장소 무료 시간을 빨리 씁니다(12장). iOS 는 이 맥에서 Xcode 26 으로 보관·서명·업로드합니다 —
사양서 4장.
bruni05
bruni · 게임개발 환경·인프라 명세서
04 / Secrets
비밀값
| 비밀값 | 어디에 | 누가 씀 | 잃어버리면 |
|---|
| 업로드 키스토어 (.jks) | GitHub Secrets ANDROID_KEYSTORE_B64 + 비밀번호 3개 | android.yml | Play Console 에서 업로드 키 재설정 요청 (며칠 걸림). 백업 2곳 필수 |
| Cloudflare API 토큰 | GitHub Secrets CLOUDFLARE_API_TOKEN · CLOUDFLARE_ACCOUNT_ID | server.yml | 대시보드에서 새로 발급 |
| 모집 서명 비밀 키 (Ed25519) | wrangler secret put GACHA_SIGN_KEY | Worker | 새 키 + 앱 공개 키 교체 = 앱 업데이트 필요 |
| Play 서비스 계정 JSON | wrangler secret put PLAY_SA_JSON | Worker 영수증 검증 | Google Cloud 에서 새로 발급 |
| App Store Server API 키 | wrangler secret put APPLE_* | Worker 영수증 검증 | App Store Connect 에서 새로 발급 |
| 저장 서명 양념 (APP_PEPPER) | GitHub Secrets → 빌드 때 상수로 주입 | 앱 저장 HMAC | 바꾸면 기존 저장이 안 열림 — 바꾸지 않음 |
| AdMob 앱 id·광고 단위 id | 저장소에 넣어도 됨 (공개값) | 앱 | — |
깃에 절대 안 들어가는 것키스토어 파일·비밀번호·API 토큰·서명 비밀 키·서비스 계정 JSON. .gitignore 에 *.jks · *.keystore · .dev.vars · *.p8 를 첫 커밋부터 넣습니다. 비밀값 목록과 자리는 이 표처럼 깃에 적고, 값은 적지 않습니다.
키스토어 백업① 암호화한 압축 파일을 주인 클라우드 드라이브에 ② 같은 파일을 오프라인 저장장치에. 비밀번호는 비밀번호 관리자에만. 업로드 키는 잃어도 재설정이 되지만 그동안 업데이트를 못 냅니다.
bruni06
bruni · 게임개발 환경·인프라 명세서
05 / Infrastructure
인프라 구성도
실행 중
게임 앱Godot · 기기
HTTPS
Cloudflare Worker/v1/* 13개
SQL
D1테이블 8개
Play Billing · StoreKit 2결제
토큰
Worker영수증 검증
Google API · Apple
지급 결정우편함
Play RTDN · Apple 알림환불
웹훅
Worker/v1/notify/*
갱신
D1 purchases미사용분 회수
만들고 올리는 길
이 맥Godot · Claude Code
git push
GitHubbruni-idle
Actions
검사·AAB·서버 배포
AABandroid.yml 아티팩트
수동 업로드
Play Console내부 → 비공개 → 프로덕션
—
스토어
| 부품 | 자리 | 비고 |
|---|
| Worker | Cloudflare — 게임 전용 계정 (바이블 12-2) | 기존 bruni-world 계정과 분리. 결제·개인정보가 걸린 서비스라 권한을 나눔 |
| D1 | 같은 계정, DB 이름 bruni-idle · bruni-idle-staging | 6장 |
| 원격 설정 | GET /v1/config — Worker 안 상수 + 정적 JSON | 이벤트 달력·배너·최소 버전·기능 스위치 |
| 확률 공개 페이지 | Cloudflare Pages 정적 페이지 (prob_publish.py 산출물) | 게임 안 확률 화면과 같은 원본에서 생성 |
bruni07
bruni · 게임개발 환경·인프라 명세서
06 / Infra State
인프라 상태 관리
서버 쪽 「지금 무엇이 어떻게 설정돼 있나」를 대시보드에서 손으로 바꾸지 않고, 전부 저장소의 파일로 둡니다. 파일이 곧 상태입니다.
| 대상 | 상태를 담는 파일 | 바꾸는 법 |
|---|
| Worker 코드·바인딩·라우트·환경 | server/wrangler.jsonc | 파일 수정 → 커밋 → server-v* 태그 |
| D1 테이블 구조 | server/migrations/0001_init.sql, 0002_….sql … (번호 순, 지우지 않음) | 새 번호 파일 추가 → 배포 때 migrations apply가 아직 안 한 것만 적용 |
| 적용된 마이그레이션 기록 | D1 안 d1_migrations 표 (wrangler 가 관리) | 직접 고치지 않음 |
| 비밀값 이름 목록 | server/SECRETS.md (이름만) | 값은 wrangler secret put |
| 원격 설정 | server/src/config.ts + 정적 JSON | 커밋·배포 |
환경 세 개
| 환경 | 어디 | D1 | 언제 |
|---|
| 로컬 | wrangler dev (이 맥) | 로컬 SQLite (.wrangler/, 깃 제외) | 개발 중 항상 |
| 스테이징 | bruni-idle-staging.*.workers.dev | bruni-idle-staging | 내부·비공개 테스트 빌드가 바라봄 |
| 프로덕션 | 정식 도메인 | bruni-idle | 스토어 빌드가 바라봄 |
Terraform 같은 도구는 쓰지 않는다부품이 Worker 1개 · D1 2개 · Pages 1개뿐이고 wrangler 설정 파일과 마이그레이션 파일만으로 「무엇이 있어야 하나」가 깃에 다 남습니다. 부품이 늘어 손으로 맞추는 일이 생기면 그때 다시 판단합니다.
bruni08
bruni · 게임개발 환경·인프라 명세서
07 / Game State
게임 상태 관리
게임의 「지금 상태」는 저장 데이터 하나(Save.data, 기술 명세서 12장의 43개 필드)뿐입니다. 화면은 상태를 들고 있지 않고, 보여 주기만 합니다.
화면버튼 누름
요청
도메인 오토로드Wallet · Missions …
수정
Save.data유일한 상태
Save.data
mark_dirty()
2초 뒤 저장원자적 쓰기
Bus 시그널
화면다시 그림
| 규칙 | 내용 | 왜 |
|---|
| 한 곳 | 상태는 Save.data 에만. 화면 스크립트에 재화·진행 값을 복사해 두지 않음 | 두 곳에 두면 언젠가 어긋남 |
| 입구 하나 | 재화는 Wallet.add/spend, 미션은 Missions, 모집 결과는 core/gacha_apply 로만 바꿈. 화면이 Save.data 를 직접 고치면 안 됨 | 변경마다 기록·검증·시그널이 빠지지 않게 |
| 알림은 Bus | 바뀐 뒤 Bus 시그널(과거형: wallet_changed, stage_cleared). 화면은 구독해서 다시 그림 | 화면끼리 직접 부르지 않음 |
| 저장 시점 | mark_dirty() 2초 모아 쓰기 + 앱이 내려갈 때 즉시 쓰기 | 잦은 쓰기 방지 · 강제 종료에도 안전 |
| 계산 값은 저장 안 함 | 전투력·미션 진행 중 max/has 유형은 저장값에서 다시 계산 (Missions.recheck_all) | 저장 크기·불일치 방지 |
| 형식 바뀜 | version +1 과 _v{n}_to_{n+1} 이전 함수를 같은 커밋에. tests/save 에 옛 샘플 추가 | 옛 사용자 저장이 안 깨지게 |
| 전투는 따로 | 전투 중 상태는 BattleCtx(메모리)에. 끝나면 결과만 Save 에 반영 | 판정·그리기 분리, 헤드리스 시험 가능 |
bruni09
bruni · 게임개발 환경·인프라 명세서
08 / Authority
서버가 진실인 것
| 값 | 믿는 쪽 | 기기의 역할 |
|---|
| 현재 시각 | 서버 (GET /v1/time) | 오프라인이면 기기 시계 + 상한 24시간, 다음 접속 때 대조(꺼 둔 동안 진행은 앱을 켤 때 30분 칸으로 재생) |
| 유료 빛구슬 | 서버 (gem_ledger) | 보여 주기·차감 보고. 복구 때 서버 잔액으로 맞춤 |
| 모집 결과·천장 | 서버 (gacha_pulls·gacha_state) | 서명 확인 후 적용만. 서버 연결 없으면 모집 버튼 비활성 |
| 결제 지급 | 서버 (purchases) | 검증 성공 뒤 우편으로만 |
| 그 밖의 진행 전부 | 기기 (save.json) | 서버에는 하루 2회 + 결제·장 완료 직후 백업 |
| 백업 충돌 | 누적 플레이 시간이 긴 쪽 (기술 명세서 12장) | 복구 코드로 기기 변경 |
왜 이렇게 나누나돈이 걸린 것(유료 재화·모집·결제)과 시간만 서버가 쥡니다. 나머지를 서버에 두면 요청이 늘어 무료 한도를 빨리 넘고(12장), 오프라인 플레이가 막힙니다. 방치 게임은 오프라인 플레이가 핵심입니다.
bruni10
bruni · 게임개발 환경·인프라 명세서
09 / Content Data
콘텐츠 데이터 상태
game-design/databruni-world · 원본
data-sync.yml
design/복사본 + 원본 커밋 해시
export_data.py --wave
data/*.json게임이 읽는 17개
| 항목 | 규칙 |
|---|
| 원본 | 수치·이름·대사는 bruni-world game-design/data 에서만 고침. 게임 저장소의 data/ 를 손으로 고치지 않음 |
| 복사 기록 | design/SOURCE 파일에 복사한 bruni-world 커밋 해시 한 줄 |
| 데이터 버전 | data/manifest.json 의 data_ver = 2027-01-15+a1b2c3d (날짜 + 원본 해시) |
| 공개 차수 | --wave W0~W6 — 아직 공개 안 한 동료·장·이벤트는 빌드에서 빼거나 잠금. 서버 원격 설정의 wave 로 열기 |
| 검사 | export 직후 validate_data.py — 기술 명세서 11장의 35개 검사 + 스키마 |
| 확률 공개 | 모집·항아리 확률표는 data/gacha.json 하나에서 게임 화면·공개 페이지·서버 모집 판정이 같이 만들어짐. 숫자를 코드에 적지 않음 |
bruni11
bruni · 게임개발 환경·인프라 명세서
10 / Work State
작업 진행 상태
「어디까지 했고 다음에 뭘 하나」도 깃에 둡니다. 주인이 다른 컴퓨터로 옮기거나 다른 AI 세션이 이어받아도 같은 곳에서 출발하게 합니다.
| 도구 | 쓰는 법 |
|---|
| GitHub 마일스톤 | 1차판 · 완성판 두 개. 출시·운영 사양서의 작업 목록을 이슈로 옮겨 마일스톤에 묶음 |
| GitHub 이슈 | 작업 하나 = 이슈 하나. 커밋 메시지에 #12 를 적어 연결, 끝나면 fixes #12 |
| 라벨 | 영역: battle · growth · gacha · story · ui · server · data · art / 종류: bug · task · idea |
| docs/STATE.md | 한 장짜리 현황 — 마지막으로 된 것 · 지금 하는 것 · 막힌 것 · 다음 3개. 세션을 끝낼 때마다 고침 |
| CLAUDE.md | 이 저장소 AI 규칙 — 7장 상태 규칙 · 9장 데이터 원본 규칙 · 4장 비밀값 규칙 · 「작업마다 커밋·푸시」 · 「STATE.md 갱신」 |
| 세션 시작 | ① git pull ② STATE.md 읽기 ③ 열린 이슈 확인 ④ CI 빨간불이면 그것부터 |
GitHub Projects 보드는 쓰지 않는다1인 개발에 보드까지 두면 관리 일이 늘어납니다. 마일스톤의 이슈 수(열림/닫힘)가 진척률을 대신합니다.
bruni12
bruni · 게임개발 환경·인프라 명세서
11 / New Machine
새 컴퓨터 세팅
맥 기준입니다. 위에서부터 순서대로 하면 개발·빌드·서버 배포까지 됩니다.
# 1. 기본 도구
xcode-select --install
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install git gh node python@3.12
# 2. 깃허브 로그인 + 저장소 받기
gh auth login && gh auth setup-git
git clone https://github.com/<계정>/bruni-world ~/dev/hey
git clone https://github.com/<계정>/bruni-idle ~/dev/bruni-idle
# 3. Godot 4.7.2
brew install --cask godot # 버전 확인: Godot --version → 4.7.2.stable
# 4. Android 빌드 도구
brew install openjdk@17
brew install --cask android-commandlinetools
sdkmanager "platform-tools" "platforms;android-36" "build-tools;«Godot 4.7 문서 기준 버전»"
# Godot 편집기 → 편집기 설정 → 내보내기 → Android: SDK 경로·Java 경로 지정
# 프로젝트 → Android 빌드 템플릿 설치 (android/ 폴더 — 이미 깃에 있으면 생략)
# 5. iOS (출시 단계에서)
# App Store 에서 Xcode 26 설치 → sudo xcode-select -s /Applications/Xcode.app
# 6. 서버
cd ~/dev/bruni-idle/server && npm ci
npx wrangler login # 게임 전용 Cloudflare 계정으로
npx wrangler dev # 로컬 서버 + 로컬 D1
# 7. 키스토어 (빌드할 기기만) — 백업에서 복원, 경로는 환경변수로
export GODOT_ANDROID_KEYSTORE_RELEASE_PATH=~/secure/bruni-upload.jks
# 8. 깊게 시키기 스킬
ln -sfn ~/dev/hey/.claude/skills/deep ~/.claude/skills/deep
한 줄로 시키기새 컴퓨터의 Claude Code 에 「bruni-idle 개발 환경 세팅해 줘 — /me/idle-rpg/infra 11장대로」라고 하면 이 순서를 따릅니다. 로그인(gh·wrangler)과 키스토어 복원만 사람이 합니다.
bruni13
bruni · 게임개발 환경·인프라 명세서
12 / Cost & Limits
비용·한도
| 항목 | 무료 범위 | 넘으면 | 판단 |
|---|
| GitHub 비공개 저장소 | 무료 계정 — Actions 월 2,000분(리눅스 기준, macOS 는 더 빨리 소모) | 유료 플랜 또는 분 단위 과금 | ci.yml 한 번 수 분 — 하루 수십 번 돌려도 여유. 정확한 한도는 GitHub 요금 페이지에서 확인(미확인) |
| Cloudflare Workers | 하루 요청 10만 | Workers Paid $5/월 | 기술 명세서 15장: 하루 요청 7만(≈DAU 4,965)에서 전환 |
| Cloudflare D1 | 저장 용량 무료 한도 안 | 같은 유료 플랜 | 기술 명세서 15장: 4GB(≈DAU 3,096, 90일 보관 기준)에서 전환 |
| Godot · GUT · 플러그인 | 무료(MIT 등) | — | — |
| Google Play · Apple | — | $25 1회 · 연 129,000원 | 사양서 5장 |
전환 규칙D1 4GB 또는 하루 요청 7만 중 먼저 닿는 쪽에서 Workers Paid($5/월)로 바꿉니다(기술 명세서 15장 계산값). 이벤트 기록이 용량을 가장 많이 먹으니, 그 전에 90일 지난 기록을 지우는 정리 작업을 먼저 붙입니다.
bruni14
bruni · 게임개발 환경·인프라 명세서
13 / Day One
첫날 세팅 순서
| 순서 | 할 일 | 끝났다는 기준 |
|---|
| 1 | GitHub 에 비공개 저장소 bruni-idle 생성 · .gitignore(Godot·비밀값) · CLAUDE.md · docs/STATE.md | 첫 커밋 푸시 |
| 2 | Godot 프로젝트 생성 — 렌더러 Compatibility · 720×1280 · 오토로드 14개 빈 파일 · boot/main 씬 | 편집기에서 실행해 빈 화면 |
| 3 | GUT 설치 · 테스트 1개(PCG32 벡터) · ci.yml | Actions 초록불 |
| 4 | design/ 복사 · export_data.py · validate_data.py | data/ 17개 생성 + 검사 통과 |
| 5 | server/ — GET /v1/time 하나 + 0001_init.sql · wrangler dev | 로컬에서 시각 응답 |
| 6 | Save · Wallet · Bus · Clock 네 오토로드 실제 구현 (7장 규칙) | 재화 +1 → 저장 → 재실행 후 유지 |
| 7 | GitHub 마일스톤 2개 · 라벨 · 1차판 작업을 이슈로 | 이슈 목록 완성 |
미확인 — 세팅 날 확인할 것① Godot 4.7.2 리눅스 빌드 파일 이름(3장 URL) ② Godot 4.7 이 요구하는 Android build-tools 버전 ③ GitHub Actions 무료 분 수 ④ 게임 전용 Cloudflare 계정을 새로 만들지(바이블 12-2) 기존 계정에 둘지 — 결제 정보가 걸리니 주인 결정
근거:
기술 명세서(game-design/data/tech.json, 2026-10-13 — 폴더·오토로드·엔드포인트·D1·예산·빌드 단계) · 바이블 12장 ·
게임 개발 사양서 · 이 맥 실측(Godot 4.7.2.stable.official 설치, 2026-10-13).
bruni15