preview
🍽️ Culinary Commons — 서버 권한 기반 타이쿤 오케스트레이션 키트
Download
모듈식이며, 서버 우선(server-first) 시뮬레이션 프레임워크로, 지속적인 경제 기반 경험을 구축할 수 있도록 합니다. 이 환경에서는 모든 원장 기록(ledger entry)이 획득되고, 모든 확장(expansion)이 검증되며, 모든 플레이어의 주방 제국이 신뢰 경계면(trust boundary)의 오른쪽에 존재합니다.
Download
📜 목차
🍳 Culinary Commons란 무엇인가?
Culinary Commons는 엄격한 Luau로 작성되었으며 Rojo를 통해 연결된 모듈식, 서버 권한 백엔드 프레임워크입니다. 이 프레임워크는 오직 하나의 목적을 가지고 존재합니다: 서버가 단일 진실 공급원(single source of truth)이고 클라이언트가 조종자가 아닌 존중하는 손님인 레스토랑 스타일의 타이쿤 경험을 구축하려는 빌더들에게 신뢰할 수 있는 골격을 제공하기 위함입니다.
다른 타이쿤 키트들이 클라이언트에게 지갑을 건네주고 최선을 바라보는 것과 달리, Culinary Commons는 이 공식을 뒤집습니다. 클라이언트가 요청하고, 서버가 결정합니다. 그리고 영속성(Persistence)은 서버가 결정한 내용을 기록할 뿐입니다. 그 외의 어떤 것도 장부에 손댈 수 없습니다.
이는 공공 광장에 보관된 원장과 은행 금고 안에 보관된 원장의 차이점이라고 생각해보세요. 둘 다 숫자를 기록합니다. 하지만 호기심 많은 낯선 사람과의 접촉에서 살아남는 것은 오직 하나뿐입니다.
🧠 프레임워크의 철학적 배경
이 프레임워크는 네 가지 흔들리지 않는 약속을 기반으로 설계되었습니다:
- 결정론(Determinism) 우선. 동일한 순서의 유효성 검사된 입력값이 주어지면, 시뮬레이션은 매번 동일한 출력을 생성합니다. 임의적인 클라이언트 측 주사위 굴림은 없습니다.
- 명시적 경계(Explicit boundaries). 모든 원격 경계는 선언되고, 버전 관리되며, 속도 제한이 적용됩니다. 숨겨진 표면은 기능이 아니라 버그로 간주됩니다.
- 상속보다 구성(Composition over inheritance). 타이쿤 스테이션, 메뉴 및 업그레이드는 광범위한 거대 객체(god-object) 대신 작고 테스트 가능한 모듈들로부터 구축됩니다.
- 작성자보다 읽는 사람(Readers over writers). 새로운 기여자라면 누구나 기존 서비스 계약을 먼저 읽어본 후 새로운 것을 제안하는 것이 기본 자세입니다. 재협상하기 전에 담보 대출(mortgage) 내용을 이해하세요.
🔒 서버 권한이 중요한 이유
타이쿤은 그 경제의 인식된 가치에 따라 흥망성쇠를 경험합니다. 플레이어들이 숫자가 허공에서 소환될 수 있다고 느끼는 순간, 전체 루프가 무너집니다. 이는 단 하나의 사건 때문이 아니라 신뢰가 증발하기 때문입니다.
여기서 서버 권한(Server-authoritative) 설계란 다음을 의미합니다:
- 모든 통화 변동은 유효성 검사된 서버 서비스 내부에서 발생합니다.
- 모든 업그레이드 구매는 수락되기 전에 결정론적 계층 구조 확인 절차를 거칩니다.
- **모든 영속성 쓰기(persistence write)**는 서버 액션이며, 레이스 컨디션을 방지하기 위해 단일 저장 조정자(save coordinator)를 통해 직렬화됩니다.
- 모든 클라이언트 호출은 요청으로 취급되며, 결코 명령으로 취급되지 않습니다.
이것은 편집증이 아닙니다. 이것은 환대입니다. 집주인이 손님이 즐거움에 집중할 수 있도록 진행 상황을 보호하는 것입니다.
🗂️ Repository 레이아웃
트리 구조에 대한 개요:
- /src/server — 권한 있는 서비스: 경제(economy), 스테이션 등록부(station registry), 업그레이드 계층(upgrade hierarchy), 저장소 조정자(save coordinator).
- /src/shared — 경계를 넘어 공유되는 순수 모듈: 스키마(schemas), 상수(constants), 신호 정의(signal definitions), 검증기(validators).
- /src/client — 표현 전용 컨트롤러: UI 바인딩(UI bindings), 입력 어댑터(input adapters), 낙관적 미리보기(optimistic previews).
- /assets — Rojo 프로젝트 파일, 장소 템플릿(place templates), 그리고 현지화 테이블(localization tables).
- /docs — 긴 형식의 문서(long-form documentation), 아키텍처 결정 기록(architecture decision records), 마이그레이션 노트(migration notes).
- /tests — 시뮬레이션 하네스(simulation harness), 결정론적 리플레이(deterministic replays), 불변성 검사(invariant checks).
모든 폴더는 목적을 가지고 있습니다. 트리에 '혹시 몰라서' 존재하는 것은 없습니다.
🧩 핵심 아키텍처
Culinary Commons는 작고 협력하는 서비스들의 집합체로 구성되었으며, 각 서비스는 정확히 하나의 관심사(concern)를 소유합니다:
1. Ledger Service (원장 서비스)
The economy의 심장부입니다. 잔액(balances), 거래(transactions), 감사 추적(audit trails)을 소유합니다. 모든 변경 사항은 기록을 생성하며, 결코 조용한 산술 계산이 일어나지 않습니다. 구매가 발생하면, 검토자가 수동으로 재구성할 수 있는 종이 흔적이 남습니다.
2. Station Registry (스테이션 등록부)
어떤 타이쿤 스테이션이 존재하는지, 어떤 것이 잠금 해제되었는지, 그리고 어떤 것이 활성화 대기 중인지를 추적합니다. 이 서비스는 하나의 질문에 잘 답합니다: "지금 당장 이 스테이션이 운영되는 것이 허용되나요?" 가격 책정, 애니메이션 또는 UI는 결정하지 않습니다. 그것들은 다른 곳에 속합니다.
3. Upgrade Hierarchy (업그레이드 계층)
선행 조건(prerequisites)으로 이루어진 방향성 비순환 그래프(directed acyclic graph, DAG)입니다. 업그레이드를 구매하기 전에 이 계층을 참조합니다. UI에서 업그레이드가 제공되기 전에, 동일한 계층을 캐시된 투영(cached projection)을 통해 다시 참조합니다. 두 경우 간의 일관성은 공유 검증기(shared validator)에 의해 강제됩니다.
4. Save Coordinator (저장소 조정자)
단일 작성자 직렬화 레이어(single-writer serialization layer)입니다. 세션은 여기에 등록하고, 쓰기는 대기열에 추가되며, 충돌은 기록되고 숨겨지지 않는 '마지막 검증된 쓰기가 승리하는(last-validated-write-wins)' 정책으로 해결됩니다. 저장이 거부되면, 그 거부는 삼켜진 오류가 아니라 일급 객체 이벤트(first-class event)입니다.
5. Signal Bus
내부 이벤트를 위한 타입이 지정된 pub/sub 계층입니다. 이는 클라이언트-서버 경계를 직접 가로지르지는 않습니다. 원격 이벤트는 별도로 선언되고 검증됩니다. 이 버스는 서버가 스스로와 통신하는 것이며, 원격(remotes)은 서버가 외부 세계에 응답하는 것입니다.
✨ 주요 기능 하이라이트
- 결정론적 경제 시뮬레이션 — 장부(ledger)-핵심 경로에서 부동 소수점 드리프트 없음; 중요한 곳에는 정수 카운터 사용.
- 선언적 스테이션 매니페스트 — 스테이션을 한 번 설명하면, 그 유일한 설명을 기반으로 잠금 해제 규칙, 가격 책정 후크(pricing hooks), UI 투영이 생성됩니다.
- 타입 지정 원격 계약 — 모든 클라이언트-서버 호출은 명시적인 시그니처와 비즈니스 로직에 도달하기 전에 잘못된 페이로드를 거부하는 검증 계층을 가집니다.
- 멱등성(Idempotent) 저장 작업 — 재시도한다고 해서 두 번 적용되지 않습니다. 코디네이터가 작업 ID를 추적하여 재생(replays)이 안전합니다.
- 핫 리로드 친화적인 모듈 — Rojo 컴포지션은 소스 트리를 평평하게 유지하고 반복 중에 검사하기 쉽게 합니다.
- 감사 등급 로그 — 거부된 요청 각각에 이유 코드가 포함되며, 단순히 부울 값만 제공하지 않습니다.
- 조합 가능한 UI 바인딩 — 클라이언트는 서버 상태를 보여주는 뷰이며, 결코 소유자가 아닙니다.
- 스키마 버전 관리 지속성(Schema-versioned persistence) — 마이그레이션은 명시적이고 번호가 지정되며 원칙적으로 되돌릴 수 있습니다.
- 제로 트러스트 클라이언트 자세(Zero-trust client posture) — 클라이언트는 권위자가 아닌 도움이 되는 화자(narrator)로 취급됩니다.
- 전반적인 엄격한 Luau 타입 지정 — 코드베이스에 암시적인
any가 없습니다. 타입 시스템은 장식이 아니라 계약입니다.
📱 반응형 및 접근성 인터페이스
프레젠테이션 계층은 가독성이나 상호 작용의 명확성을 희생하지 않으면서, 컴팩트한 휴대용 방향부터 넓은 데스크톱 레이아웃까지 다양한 뷰포트 크기에 우아하게 적응하도록 설계되었습니다.
클라이언트에 내장된 반응형 UI 고려 사항:
- 클리핑(clip) 대신 리플로우(reflow)되는 유연한 그리드 레이아웃.
- 마우스와 손가락 입력 모두에 적합하도록 크기가 조정된 터치 타겟.
- 동적 배경에서도 텍스트 가독성을 유지하는 대비 인식 팔레트 토큰.
- 사용자가 애니메이션을 선호하지 않을 경우 즉각적인 상태 변화로 저하되는, 리듀스 모션(Reduced-motion) 친화적인 전환 효과.
- 플랫폼이 지원하는 경우 키보드 탐색이 가능한 메뉴.
접근성(Accessibility)은 여기에서 체크박스가 아닙니다. 이는 클라이언트를 신뢰할 수 있는 서버에 대한 존중받는 인터페이스로 취급하는 자연스러운 결과입니다.
🌍 다국어 지원 (Multilingual Support)
Culinary Commons의 현지화(Localization)는 단순히 붙이는 것이 아니라 구조적입니다. 플레이어가 접하게 되는 모든 문자열은 안정적인 식별자(stable identifier)로 키가 지정된 현지화 테이블을 거칩니다. 새로운 언어를 추가하는 것은 클라이언트 코드를 다시 작성하는 것이 아니라, 새로운 테이블을 추가하는 것을 의미합니다.
현지화 원칙:
- 문자열 키는 위치 기반이 아닌 의미론적입니다 (
station.upgrade.confirm와 같이 text_042가 아님).
- 복수형 규칙은 데이터 주도적(data-driven)으로 처리되어, 복잡한 복수 형태를 가진 언어도 일급 시민처럼 다뤄집니다.
- 숫자 및 통화 형식 지정은 공유 포매터(shared formatter)를 통해 로케일 관습을 존중합니다.
- 우측에서 좌측으로 쓰는 스크립트(Right-to-left scripts)는 텍스트 수준이 아닌 레이아웃 수준에서 지원됩니다.
그 결과: 코드 분기 없이 어느 지역의 프랜차이즈라도 열 수 있는 레스토랑 제국을 만들 수 있습니다.
🕐 연중무휴 지원 모델 (Round-the-Clock Assistance Model)
타이쿤 경험은 종종 시간대와 플레이어 일정에 걸쳐 진행되기 때문에, 이 프레임워크는 관리자 및 통합자를 위해 **지속적으로 이용 가능한 지원 태세(continuously available support posture)**를 가정합니다:
- 문서는 어느 시간에든 답변할 수 있도록 작성됩니다. 명확하고, 자족적이며, 검색 가능합니다.
- 이슈 템플릿은 보고서가 오전 3시에 접수되든 정오에 접수되든 똑같이 실행 가능한 구조로 되어 있습니다.
- 아키텍처 결정 기록(Architecture decision records)은 이유를 기록하여, 대화 중간에 합류하는 관리자가 추측할 필요가 없도록 합니다.
- 에스컬레이션 경로(Escalation paths)는 즉흥적으로 만들어지지 않고 문서화됩니다.
여기서
🚀 패키지 매니저 없이 시작하기
이 섹션은 의도적으로 일반적인 종속성 관리자(dependency-manager)의 오케스트레이션을 피합니다. 이 프레임워크는 소스 트리(source tree)이며, 사용자가 선호하는 방식으로 자신의 워크플로우에 통합할 수 있습니다.
필요한 것:
- 외부 소스 트리를 동기화할 수 있는 Roblox Studio 설치본.
/assets 내에 위치한 Rojo와 호환되는 프로젝트 디스크립터(project descriptor).
- Luau 및 Roblox 데이터 모델에 대한 작동 지식.
일반적인 흐름:
- 동기화 도구를 리포지토리 루트를 가리키도록 설정하고,
/src/server, /src/shared, 그리고 /src/client가 예상되는 서비스 컨테이너로 매핑되게 합니다.
/assets에 포함된 플레이스 템플릿(place template)을 열고, 서비스 토폴로지(service topology)가 디스크립터와 일치하는지 확인합니다.
- 새로운 기능을 작성하기 전에 반드시
/shared/schemas를 검토하세요 — 스키마는 이 프레임워크의 어휘입니다.
/tests에서 시뮬레이션 하네스(simulation harness)를 실행하여 환경이 올바르게 연결되었는지 확인합니다.
- 빈 페이지가 아닌, 기존 서비스의 복사본부터 확장하는 것을 시작하세요.
만약 동기화되지 않는 것이 있다면, 문제는 거의 항상 프레임워크 자체에 있는 것이 아니라 프로젝트 디스크립터의 경로 불일치(path mismatch) 때문입니다.
⚙️ 설정 및 환경 신호
설정은 /src/shared/constants 아래의 일반 Luau 테이블에 존재합니다. 숨겨진 환경 변수나 원격 파일에서 가져오는 마법 같은 문자열(magic strings)은 없습니다.
구성 카테고리:
- 경제성 튜닝 (Economy tuning) — 시작 잔액, 거래 상한선, 재사용 대기 시간(cooldown windows).
- 계층 정의 (Hierarchy definitions) — 업그레이드 및 스테이션 잠금 해제의 선행 조건 그래프.
- 지속성 스키마 버전 (Persistence schema versions) — 저장된 데이터의 마이그레이션 사다리.
- 원격 계약 (Remote contracts) — 선언된 이름, 인자 형태(argument shapes), 그리고 비율 제한(rate limits).
- 현지화 기본값 (Localization defaults) — 폴백 언어 및 포매터 기본 설정.
모든 구성 값은 문서화된 기본값과 문서화된 안전 범위를 가집니다. 범위 밖의 값은 런타임(runtime)이 아닌 시작 시점(startup)에 거부됩니다.
💾 데이터 지속성 모델
타이쿤 게임에서 저장 기능은 단순한 세션과 스토리를 가르는 경계입니다. Culinary Commons는 영속성(persistence)을 일급 서브시스템으로 다룹니다: