소마 프로젝트 레포지토리 전략 결정안

결론

현재 소마 프로젝트에는 모듈형 모노레포가 가장 적합하다.

완전한 멀티레포로 Godot, Swift, LLM 엔진, 백엔드를 각각 분리하기보다는, 하나의 제품 레포 안에서 각 계층을 폴더와 패키지 단위로 나누는 방식이 좋다. 이 프로젝트의 핵심 리스크는 각 파트가 독립적으로 잘 동작하는지가 아니라, Godot 캐릭터 UI, Swift 행동 로직, 온디바이스 LLM 엔진, 서버 API가 서로 정확히 맞물리는지에 있기 때문이다.

추천 구조

soma-pet/
  apps/
    ios/                 # Swift host app, Xcode project
    godot/               # Godot project: character, scene, UI/UX
  packages/
    behavior-core/       # Swift Package: 행동 결정, tool routing, memory 호출
    llm-engine/          # 온디바이스 모델 래퍼, inference adapter
    memory-core/         # memory schema/retrieval/pruning logic
  services/
    api/                 # backend server
  contracts/
    godot-swift-events/  # Godot <-> Swift event schema
    api/                 # OpenAPI / JSON schema
  models/
    manifests/           # 모델 메타데이터, 다운로드 스크립트만
  docs/
  infra/

이 구조의 핵심은 소스와 계약은 한 레포에 두고, 무거운 산출물은 레포 밖에 둔다는 것이다.

모델 weight, 변환된 모델 파일, benchmark output, Godot export build, iOS archive는 Git에 넣지 않는다. 모델 파일은 Hugging Face, GitHub Release, S3/R2/OCI Object Storage 같은 artifact 저장소에 두고, 레포에는 manifest와 다운로드 스크립트만 둔다.

왜 모듈형 모노레포인가

이 프로젝트는 세 명의 작업 영역이 분리되어 있지만, 제품 관점에서는 하나의 앱이다.

  • 프론트엔드: Godot Engine으로 캐릭터 제어와 전반적인 UI/UX 개발
  • 모델/앱 로직: 온디바이스 LLM 엔진 개발, Swift 단 행동 로직 개발, Godot 연동
  • 백엔드: 서버 인프라와 API

문제는 각 영역이 독립적으로 릴리즈되는 라이브러리라기보다, 매번 서로의 계약을 바꾸며 같이 진화한다는 점이다.

예를 들어 Godot에서 캐릭터 이벤트를 바꾸면 Swift 행동 로직이 바뀌고, Swift 행동 로직이 바뀌면 LLM 호출 schema나 memory action schema가 바뀔 수 있다. 백엔드 API 응답 구조가 바뀌면 Swift와 Godot 쪽 화면 상태도 같이 바뀐다.

이런 상황에서 멀티레포를 쓰면 변경 하나를 반영하기 위해 여러 레포의 브랜치, PR, 버전, dependency pin을 계속 맞춰야 한다. 반대로 모노레포에서는 하나의 PR에서 contracts/, apps/godot/, apps/ios/, services/api/ 변경을 같이 검토하고 테스트할 수 있다.

멀티레포보다 나은 점

모듈형 모노레포는 MVP 단계에서 다음 이점이 크다.

  1. 계약 변경이 쉽다.

    Godot-Swift event schema, backend API schema, memory action schema를 contracts/에 두면, 계약 변경과 구현 변경을 한 PR에서 같이 처리할 수 있다.

  2. 통합 테스트가 쉽다.

    Godot 이벤트가 Swift 행동 로직으로 들어가고, Swift가 LLM/memory/backend를 호출하는 흐름을 한 레포에서 재현할 수 있다.

  3. 팀 동기화 비용이 낮다.

    세 명이 같은 source of truth를 보고 작업하므로 "백엔드 레포는 최신인데 iOS 레포는 옛날 schema" 같은 문제가 줄어든다.

  4. MVP 속도가 빠르다.

    초기에는 안정된 패키지 릴리즈보다 빠른 인터페이스 수정과 실험이 중요하다. 모노레포는 이 반복에 유리하다.

주의할 점

모노레포라고 해서 모든 파일을 넣으면 안 된다. 특히 이 프로젝트는 Godot asset과 on-device model artifact가 커질 수 있으므로 아래 규칙이 중요하다.

  1. 모델 weight는 Git에 넣지 않는다.

    models/manifests/에는 모델명, 버전, checksum, 다운로드 URL, target runtime 같은 메타데이터만 둔다.

  2. Godot 캐시는 ignore한다.

    Godot 공식 문서에서도 .godot/ 같은 생성 캐시 폴더는 version control에서 제외하라고 안내한다.

  3. 큰 binary asset은 기준을 따로 둔다.

    작은 소스 asset은 Git에 둬도 되지만, 큰 이미지, 오디오, 3D 모델은 Git LFS 또는 외부 asset storage 기준을 정한다.

  4. contracts를 최상위 시민으로 둔다.

    contracts/가 바뀌면 Godot, Swift, backend 테스트가 모두 도는 식으로 CI를 설계한다.

CI 운영 기준

초기 CI는 path 기반으로 단순하게 나누면 된다.

apps/godot/**       -> Godot import/export check
apps/ios/**         -> xcodebuild, Swift tests
packages/**         -> Swift Package tests
services/api/**     -> backend tests
contracts/**        -> Godot + Swift + backend contract tests

처음부터 복잡한 monorepo tool을 도입할 필요는 없다. MVP 단계에서는 GitHub Actions의 path filter와 간단한 shell script만으로 충분하다.

언제 멀티레포로 분리할까

멀티레포는 나중에 다음 조건이 생기면 고려한다.

  • 백엔드가 앱과 독립된 서비스로 운영된다.
  • backend 팀과 app 팀의 배포 주기가 완전히 달라진다.
  • 특정 모듈을 외부에 공개하거나 별도 SDK로 배포해야 한다.
  • 권한 관리상 일부 코드를 별도 레포로 분리해야 한다.
  • 모델 엔진이 여러 앱에서 공통으로 쓰이는 독립 제품이 된다.

지금 단계에서는 이 조건들이 아직 강하지 않다. 그래서 분리보다 통합의 이점이 더 크다.

최종 결정

소마 MVP 단계에서는 단일 제품 모노레포로 시작한다.

단, 내부는 반드시 모듈 경계를 둔다.

  • Godot은 apps/godot/
  • iOS Swift app은 apps/ios/
  • Swift 행동 로직과 memory/LLM engine은 packages/
  • backend는 services/api/
  • 모든 경계 계약은 contracts/
  • 모델 대형 파일은 외부 artifact storage

이 방식이 현재 팀 구성과 기술 리스크에 가장 잘 맞는다.

참고 근거