프로젝트
약 8분 분량 개인 프로젝트/발루노

발루노 프로젝트 소개: 게임 밸런스 수치를 스프레드시트로 관리하는 워크스페이스

BalrunoReactTypeScriptNext.jsSpring BootPostgreSQLWebSocketSide ProjectGame Balance
목차

발루노는 캐릭터 스탯과 무기 데미지, 레벨별 성장 곡선처럼 게임 기획에서 표로 쌓이는 데이터를 스프레드시트로 편집합니다. 버전별 수치와 변경 이력도 함께 관리하는 게임 밸런싱 플랫폼입니다. 전담 밸런스 디자이너가 없는 1~5인 규모 인디 개발팀을 대상으로 만들었습니다.

발루노의 시트 편집 화면. 게임 밸런스 변수를 스프레드시트처럼 입력합니다

한눈에 보기

항목내용
한 줄 소개게임 데이터를 스프레드시트로 편집하고 버전별 수치와 변경 이력을 관리하는 밸런싱 플랫폼
기간2026.01 ~ 2026.08 (시장조사 2025.12, 1인 개인 프로젝트)
기술Next.js 16·React 19·TypeScript·Zustand, Java 25·Spring Boot 4·PostgreSQL 18(JSONB), Hocuspocus(yjs), OCI 셀프호스트
데모balruno.com
코드github.com/dj258255/balruno (클라이언트 MIT, 백엔드 AGPL v3)

왜 만들었나

인디게임 커뮤니티를 보면서 같은 패턴이 반복되는 걸 봤습니다. 캐릭터 HP를 1500으로 잡아도 적정한지 확신이 없습니다. 시간이 지나면 왜 이 숫자로 했는지 잊어버립니다. 유닛끼리 붙었을 때 밸런스가 맞는지도 일일이 손으로 테스트해야 합니다. 대형 스튜디오에는 전담 밸런스 디자이너와 내부 툴이 있지만 1~2인 인디 개발자에게는 없었습니다.

이 문제의식을 만들기 전에 먼저 검증했습니다. 인디게임 개발 오픈카톡방(2,200명 규모)에 설문을 올려 12명의 응답을 받았습니다. 절반이 현직자였고 10명이 프로젝트를 완성하거나 출시한 경험이 있었습니다. 표본이 12명이라는 한계는 있지만 몇 가지 방향은 분명히 나왔습니다. 대다수가 엑셀이나 구글 시트를 쓰고 있었습니다. 가장 큰 페인 포인트는 “밸런스가 맞는지 확인하기 어렵다”였습니다. 가격은 무료에서 월 5천원대를 대다수가 선택했습니다.

비판적인 피드백도 있었습니다. 엑셀에 익숙하고 VBA도 다루는 사람은 넘어올 메리트를 못 느낀다는 지적이었습니다. 이 지적을 받아 타겟을 다시 정의했습니다. VBA를 직접 짤 시간이나 역량이 없는 사람, “이 정도면 충분하다”는 80% 솔루션을 원하는 사람으로 좁혔습니다. 동시에 하지 않는 것도 명시했습니다. 게임을 대신 만들어주지 않습니다. AI가 밸런스를 자동으로 잡아주지도 않습니다. 모든 케이스를 커버하는 만능 툴도 목표로 하지 않습니다.

누구를 위한 서비스인가

대상이유
전담 밸런스 디자이너가 없는 1~5인 인디 개발팀수치 근거와 변경 이력을 스프레드시트 밖에서 따로 관리할 여력이 없습니다
엑셀은 쓰지만 VBA를 직접 짤 시간이 없는 사람게임 특화 수식과 시뮬레이션이 이미 만들어져 있는 쪽이 유리합니다
”80% 솔루션”으로 충분한 사람모든 케이스를 커스텀할 필요 없이 자주 반복되는 패턴만 풀면 됩니다

대형 스튜디오나 VBA를 자유자재로 다루는 사람, 100% 커스텀이 필요한 사람은 타겟이 아닙니다. 이 사람들은 직접 만드는 편이 낫다고 판단했습니다.

주요 기능

1. 스프레드시트 편집과 게임 특화 수식

동적 컬럼을 자유롭게 구성하는 시트에 DAMAGE(atk, def), SCALE(base, level, rate, type), TTK(hp, dps), EHP(hp, def), DIMINISH(value, soft, hard) 같은 70여 개 게임 공식을 바로 씁니다. REF("시트명", "행이름", "컬럼명")로 시트 간 참조를 걸면 값이 자동으로 재계산됩니다. 순환 참조는 감지해 경고합니다.

2. 버전 히스토리와 프리셋 비교

시트의 스냅샷을 찍어 임시 저장한 뒤 값을 바꾼 상태와 이전 상태를 나란히 비교합니다. 밸런스 패치 전후 수치 변화를 확인할 때 씁니다. Cmd+Z는 새로고침 뒤에도 120분 안에는 살아 있고 탭 단위로 격리됩니다. Diff baseline picker는 이 undo 로그의 역방향 replay를 그대로 재사용해서 별도 스냅샷 인프라 없이 버전 비교 기능이 하나 더 생겼습니다.

3. 시각화와 곡선 설계

성장 곡선(Linear·Exponential·Logarithmic·Quadratic·S-Curve)을 다중 시나리오와 구간별로 그리고 보간까지 지원합니다. 원하는 결과값을 넣으면 필요한 수치를 역산하는 Goal Solver, 그래프를 직접 드로잉하면 맞는 수식을 뽑아내는 곡선피팅 기능도 있습니다.

성장 곡선 차트. 선형·지수·로그·2차·S-곡선과 수확체감 구간을 함께 설정합니다

4. 몬테카를로 시뮬레이션과 밸런스 분석

1,000~100,000회 전투 시뮬레이션을 Wilson 신뢰구간 95%로 통계 검증하고 1:1, 1:N, N:N 팀 전투를 지원합니다. Z-score 기반 이상치 자동 감지, 파워 커브 분석, 피어슨 상관관계 분석, 유닛 간 승률 매트릭스를 제공합니다.

밸런스 분석의 승률 매트릭스. 유닛 네 종류의 상성 관계를 우세, 균형, 열세로 보여줍니다

5. 게임 엔진 연동과 협업 워크스페이스

Unity(ScriptableObject + JSON), Godot(Resource + JSON), Unreal(USTRUCT + CSV) 코드를 자동 생성합니다. 문서 본문은 실시간 공동 편집을 지원합니다. 코멘트와 멘션, 공유 링크, 아웃바운드/인바운드 웹훅, Discord 슬래시 커맨드까지 붙어 있습니다. 뷰는 Grid, Form, Kanban, Calendar, Gallery, Gantt 여섯 가지에 더해 Heatmap, Curve, Probability, Diff 네 가지 밸런싱 전용 뷰를 지원합니다.

구조

프론트엔드는 Next.js 16과 React 19, Zustand로 짜고 로컬에는 IndexedDB를 캐시로만 씁니다. 데이터의 기준은 항상 서버 PostgreSQL입니다. 백엔드는 Java 25와 Spring Boot 4, Spring Modulith로 모듈 경계를 나눴습니다.

프로젝트 한 row 안에 시트 셀, 시트 트리, 문서 트리 세 영역을 각각 JSONB 컬럼과 버전 컬럼으로 둡니다. GIN 인덱스로 검색합니다. 시트 셀과 트리는 서버가 진실원인 op log 방식으로 WebSocket 하나(/ws/projects/{projectId})에서 동기화합니다. 문서 본문만 Hocuspocus(yjs CRDT) 사이드카가 따로 맡아 글자 단위 자동 병합을 처리합니다.

인프라는 OCI Always Free 4대(ARM 12GB 2대, x86 1GB 2대)에 Ansible로 재현 가능하게 올렸습니다. Nginx blue/green으로 무중단 배포를 합니다. Prometheus·Loki·Grafana로 관측하고 3-2-1 원칙(로컬 PostgreSQL, backup 머신 rsync, Cloudflare R2)으로 백업합니다.

고민한 선택

DB를 정규화할지 JSONB로 받을지

스프레드시트는 통째 조회와 부분 수정이 둘 다 많고 컬럼이 고정돼 있지 않습니다. 정규화하면 1,000행 30컬럼 시트가 30,000 row가 되어 조회마다 JOIN이 커집니다. 정규화, MySQL JSON, PostgreSQL JSONB, MongoDB 네 후보를 같은 CRUD API로 50,000 시트, 50 VU, 5분 조건에서 직접 측정했습니다. 시트 통째 GET은 PostgreSQL이 p95 16ms로 MySQL(25ms), MongoDB(45ms)보다 빨랐습니다. 부분 수정(patch)은 MongoDB가 p95 37ms로 PostgreSQL(40ms)을 근소하게 앞섰습니다. 읽기와 쓰기의 우위가 갈렸지만 DB를 둘로 나누지 않고 하나로 백업과 모니터링을 묶는 편이 1인 운영에서는 더 단순하다고 보고 PostgreSQL을 골랐습니다. 부분 수정에서 MongoDB보다 근소하게 밀리는 점은 감수한 부분입니다.

같은 프로젝트에서 동기화 방식을 두 가지로 나눈 것

시트 셀과 트리는 값과 구조를 서버가 확정해야 하는 도메인입니다. 문서 본문은 글자 단위 자동 병합이 자연스러운 도메인입니다. 둘 다 서버 진실원 op log로 합치면 문서 편집의 실시간 체감이 떨어집니다. 둘 다 CRDT로 합치면 시트 값에 대한 서버 통제가 약해집니다. 그래서 시트/트리는 baseVersion 비교와 멱등키를 쓰는 op log로, 문서 본문만 yjs CRDT로 나눴습니다. 그 대가로 한 프로젝트 안에서 서로 다른 두 가지 충돌 해결 방식을 함께 유지보수해야 합니다.

매니지드 서비스 대신 셀프호스트 인프라

베타 시점에 Vercel Pro, Fly.io, Aurora MySQL, MongoDB Atlas, Datadog Pro를 통합했다면 월 약 $155가 들었을 것으로 추산했습니다. paying user가 0명인 단계에서 매달 이 돈을 먼저 쓰는 판단은 맞지 않다고 보고 OCI Always Free 4대와 Cloudflare 무료 기능으로 직접 운영하기로 했습니다. 그 대가로 인프라 장애 대응과 배포, 모니터링을 전부 직접 감당합니다. Redis 캐시나 읽기 복제본처럼 나중에 필요할 항목은 사용자 수나 부하 기준의 트리거를 미리 정해 그때 붙이기로 했습니다.

JWT 서명 알고리즘 HS256을 유지한 것

처음에는 검증 주체가 Spring 하나라고 보고 HS256으로 갔습니다. 이후 Hocuspocus가 별도 Node 프로세스로 collab 토큰을 검증하는 걸 알고 verifier가 둘로 늘어난 상황에서 RS256 전환을 검토했습니다. 그런데 Baserow, Outline, Hocuspocus playground, Supabase Auth 같은 동종 오픈소스를 열어보니 다들 기본값이 HS256이었습니다. 같은 vault, 같은 운영자, 같은 host인 환경에서는 발급과 검증 권한을 분리하는 RS256의 이득이 이름뿐이고 1인 운영 부담만 늘어난다고 판단해 HS256을 유지했습니다. 대신 전환 트리거를 별도 운영팀이나 외부 verifier가 들어오는 시점으로 다시 정의해뒀습니다.

일정과 작업 방식

시장조사는 2025년 12월에 했습니다. 2026년 1월 17일 저장소를 열어 개발을 시작했습니다. 그 달 안에 MVP 15개 기능을 공개(1월 19일)하고 첫 사용자 피드백 24건을 반영(1월 28일)했습니다. 저장소 기록으로 보면 1월 커밋 42회, 2월 10회, 3월은 커밋이 없습니다.

4월 18일에 프로젝트명을 PowerBalance에서 발루노로 통일하는 커밋을 남겼습니다. 이 시점부터 시트 도메인을 서버 진실원으로 다시 짜는 대규모 작업이 시작됐습니다. 4월 커밋이 198회, 5월이 507회로 전체 785 커밋 중 가장 많습니다. 40건의 아키텍처 결정 문서(ADR, 번호는 45번까지 매기고 5건은 다른 ADR에 합쳐 결번) 대부분은 5월 2일에서 10일 사이에 확정됐습니다. 시트/트리 동기화의 4개 영역 배포(Stage A~G)는 5월 6일에, 무중단 blue/green 배포 적용은 5월 10일에 각각 끝났습니다. 6월은 2커밋, 7월은 25커밋(보안 정리 위주)이었고 가장 최근 커밋은 8월 9일입니다.

1인 프로젝트라 작업을 영역별로 나눴습니다. 재단, 데이터/저장, 인증/권한, 동기화 엔진, 인프라/운영, 가격, 협업/공유, 연동, 제품 표면, AI/ML(보류) 아홉 갈래로 ADR을 묶어 관리했습니다. ADR은 한 번 배포되면 불변으로 둡니다. 결정을 바꿀 때는 옛 ADR을 참조하는 새 번호를 매겼습니다. GitHub Issue나 마일스톤은 쓰지 않았습니다. 커밋 기록과 이 결정 문서가 작업 진행의 기록입니다.

직접 재 본 결과

DB 비교(50,000 시트, 50 VU, 5분 조건)에서 시트 통째 GET은 PostgreSQL p95 16ms, MySQL 25ms, MongoDB 45ms였습니다. 같은 환경에서 PostgreSQL의 jsonb_set 부분 수정 patch는 p95 8ms였습니다.

가상 스레드를 켜기 전후로 셀 업데이트 100 동시 부하에서 서버 요청 지연을 비교했습니다. p95는 320ms에서 180ms로, p99는 450ms에서 240ms로, heap 사용량은 380MB에서 220MB로 줄었습니다.

GIN 인덱스를 붙이기 전후로는 같은 조회 시나리오에서 p95가 280ms에서 65ms로 줄었습니다.

무중단 배포 전환 실측입니다. 옛 단일 컨테이너에서 새 blue/green 구조로 넘어간 첫 전환에는 21초의 다운타임이 있었습니다. 이후 정상 운영에서의 전환은 다운타임 0초였습니다.

매니지드 통합 대비 회피 비용은 paying user가 0명인 시점 기준으로 연 약 $5,460으로 추산했습니다. 처음부터 쓰지 않은 비용이라 회피 비용으로 적었습니다.

아직 못 한 것

  • 결제(PortOne) 연동과 3단계 가격 정책은 만들어뒀지만 실제로 결제한 사용자는 아직 없습니다.
  • 코멘트 알림의 이메일/브라우저 푸시 발송은 실사용자가 붙기 전까지 보류해뒀습니다.
  • Discord 슬래시 커맨드의 OAuth2 설치 흐름은 아직 수동 설정이 필요합니다. /balruno query로 시트 값을 직접 조회하는 기능은 확인 응답만 하고 실제 조회는 붙지 않았습니다.
  • AI 연동(BYOK로 여러 모델 연결)과 이상치 클러스터링·TrueSkill 같은 ML 기능은 계획은 세웠지만 우선순위를 낮춰 보류한 상태입니다.
  • 실제 회사나 여러 사람이 오래 써본 사례는 없습니다. 검증은 대부분 직접 만든 환경과 설문 12명 응답 위에서 했습니다.

직접 실행해 보기

Node.js와 npm이 있으면 됩니다.

Terminal window
git clone https://github.com/dj258255/balruno.git
cd balruno
npm install
# 웹 앱 실행
cd packages/web && npm run dev
# http://localhost:3000
# 또는 데스크톱 앱(Electron) 실행
cd packages/desktop && npm run dev

더 보기

프로필 사진
작성자 @범수

오늘의 노력이 내일의 전문성을 만든다고 믿습니다.

댓글

댓글 수정/삭제는 GitHub Discussions에서 가능합니다.