엔지니어링 노트

앱 160여 개를 혼자 유지하는 구조

출시한 앱 160여 개를 혼자서 계속 돌아가게 하고 심사를 통과하게 하려고, 무엇을 공유하고 어떻게 출시하고 무엇을 재는지, 서버에는 무엇이 있는지 정리했습니다.

2026-10-08 갱신 · iOS · macOS · 안드로이드 · 웹

01구성

워크스페이스 하나에 여섯 개 도메인이 있고, 앱은 각각 자기 저장소를 씁니다. 공용 코드와 배포 레인, 규약은 특정 앱에 속하지 않고 따로 관리합니다.

App Store에 25개(iOS 20, macOS 5), Google Play에 150개가 올라가 있고, 두 스토어에 다 있는 앱을 한 번씩만 세면 160개 남짓입니다. iOS는 공용 레인 하나로 출시하고, 안드로이드는 Flutter 모노레포에서 빌드합니다.

도메인별 프로젝트 수202
안드로이드 (Flutter)
151
iOS
25
웹 서비스
13
게임 (LÖVE)
9
macOS
4

워크스페이스 안의 프로젝트 디렉터리 기준, 2026-10-08 집계. 실제 스토어에 올라간 수는 이보다 적습니다. 만들어두고 아직 제출하지 않은 것이 있습니다.

유지 비용이 늘어나는 모양
앱마다 복사했을 때기반을 하나만 뒀을 때
관리에 드는 손 →낸 앱 수 →혼자 감당할 수 있는 선

02공유와 개별

결제, 분석, 리뷰 요청, 다국어, 개인정보 처리는 앱마다 새로 만들지 않고 공용 패키지에 둡니다. 안드로이드 쪽 공용 패키지는 10개이고, Flutter 앱 136개가 같은 UI 패키지를, 126개가 같은 결제·광고 패키지를 씁니다. iOS 쪽 공용 Swift 패키지도 10개입니다. 패키지를 고치면 그 패키지를 쓰는 앱 전부에 반영됩니다.

사용자 눈에 보이는 부분은 공유하지 않습니다. 기능, 화면을 만드는 디자인 시스템, 그래픽, 앱 고유의 콘텐츠는 앱마다 따로 만듭니다. 백엔드도 마찬가지로 앱마다 자기 서버를 두고, 공통 부분만 모듈 하나에서 가져옵니다.

앱마다아무것과도 공유하지 않음
기능
디자인 시스템
그래픽
콘텐츠
공용결제분석리뷰 요청다국어개인정보

전부 아래에 같은 코드가 있습니다.

03출시

출시 한 번은 빌드, 스크린샷, 36개 로케일 메타데이터, 제출까지입니다. 전부 브라우저가 아니라 스토어 공식 API로 처리합니다.

앱마다 배포 스크립트를 따로 두지 않습니다. iOS 앱 27개가 Fastfile 하나를 가져다 쓰고, 앱별 값은 다시 적지 않고 그 값이 이미 들어 있는 파일에서 읽습니다. 사람이 직접 하는 일은 실기기 확인과 출시 버튼 두 가지입니다.

01
빌드
서명과 버전까지
02
스크린샷
필요한 크기 전부
03
메타데이터
36개 로케일
04
제출
브라우저 아니고 API로
사람
출시
사람이 누릅니다
사람이 하는 단계
실기기 확인
시뮬레이터는 코드가 도는지까지만 알려줍니다. 제스처가 어색한지, 실제 기기에서 레이아웃이 깨지는지는 알려주지 않습니다
출시
승인이 나도 자동으로 나가지 않고, 손으로 출시할 때까지 대기합니다

04측정

키워드 순위는 앱별·국가별로 매일 쌓아 시계열로 봅니다. 사용 이벤트는 자체 수집기로 받아, 같은 제품의 iOS와 안드로이드를 한 줄로 봅니다.

사용 분석은 처음에 GA4로 했습니다. 무료 구성은 Firebase 프로젝트 하나에 앱 30개까지인데 앱 수가 이미 그보다 많았고, 프로젝트를 나누면 같은 제품의 iOS와 안드로이드가 다른 속성에 들어갑니다. 그래서 Cloudflare Worker와 D1로 수집기를 만들고, 클라이언트 패키지는 iOS용과 Flutter용을 하나씩 두었습니다.

키워드 순위
검색에 걸리는가, 어떤 말로?
앱·국가별 · 서버에서 매일 스냅샷
획득과 광고
광고비 대비 수익이 어떤가?
스토어와 AdMob에서 매일 수집
앱 안에서의 행동
설치 다음에는 무엇을 하는가?
자체 수집기 · 사내 SaaS에서 집계

05인프라

사내 서비스 12개는 Go 서버이고, 각각 Docker 이미지 하나로 배포합니다. 키워드 순위 집계, 사용 분석, 광고 지표 수집, 리포트, 에셋을 만드는 GPU 작업, 나머지를 관리하는 콘솔이 모두 그렇습니다. 서비스를 옮기거나 되살릴 때는 이미지 하나를 옮기면 되고, 새 서비스도 같은 방법으로 붙입니다.

예전에는 자체 서버에 경량 쿠버네티스(K3s)를 올려 돌렸습니다. 이 정도 서비스 수에서는 클러스터에 드는 설정과 관리가 얻는 것보다 많아서 모두 Docker로 옮겼습니다.

이미지는 자체 러너에서 CI가 서비스별로 빌드해 레지스트리에 올리고, 서버는 받아 오기만 합니다. 빌드가 끝나면 콘솔이 웹훅을 받아 텔레그램으로 승인을 물은 뒤 컨테이너를 교체합니다.

앞단에는 nginx 리버스 프록시 하나가 있습니다. 예전에는 도메인 하나를 붙이려면 네 군데를 고쳐야 했는데, 지금은 콘솔에서 도메인을 추가하면 DNS 레코드와 HTTP 설정이 만들어집니다. 프록시 컨테이너 안의 감시 프로세스가 새 설정 파일을 보고 Let’s Encrypt 인증서를 받고, 인증서가 생기면 다른 감시 프로세스가 nginx를 다시 읽힙니다. 콘솔은 30초 안에 설정을 HTTPS로 바꾸고, 갱신은 12시간마다 확인합니다.

서버 모니터링도 콘솔이 합니다. 5분마다 서버별 CPU·메모리·디스크와 컨테이너 상태를 기록해 48시간 보관하고, 값이 90%를 넘거나 컨테이너가 실행 중이 아니면 텔레그램으로 알립니다.

GPU 작업 서버는 Ubuntu Server에서 ComfyUI를 Docker로 돌립니다. 런타임과 노드 버전을 이미지에 고정해 두어서, 업데이트로 노드가 깨지면 서버 안에서 고치지 않고 이전 이미지로 돌아갑니다.

공통 기술 스택
Go 1.26chiReact 19TypeScriptViteTailwind CSSTanStack QueryDocker
Docker 이미지로 도는 것들
리버스 프록시·인증서관리 콘솔ASO·리포트트렌드 수집기광고 지표제휴 링크커머스 연동LLM 게이트웨이콘텐츠 생성GPU 에셋 생성데이터베이스

06에이전트와 규약

반복되는 작업은 에이전트가 합니다. 출시 절차, 36개 로케일 메타데이터 갱신, 정책이 바뀌었을 때 앱 전체를 점검하는 일 같은 것들입니다.

절차는 스킬로 적어 둡니다. 스킬 44개와 에이전트 31개를 대상 코드와 같은 저장소에서 버전 관리하고, 규약 파일 7개(루트 1개, 도메인별 6개)에 영역마다 지킬 규칙을 둡니다. 세션이 끝나도 남아야 할 내용은 메모로 남기는데 지금 376개입니다. 같은 실수가 두 번 나오면 메모에서 규약으로, 규약에서 점검 스크립트나 권한 규칙으로 올립니다.

일의 성격에 따라 모델을 나눕니다. 구조를 정하거나 원인을 모르는 문제를 쫓는 일은 가장 강한 모델이 맡고, 빌드·업로드·콘솔 조작처럼 끝이 정해진 일은 가벼운 모델에 고정한 실행 에이전트가 맡습니다. 이렇게 나눈 근거는 7주 동안의 세션 기록(메인 세션 89개, 서브에이전트 실행 625회)입니다.

work/
CLAUDE.md루트와 도메인마다 하나씩. 프롬프트보다 우선합니다
.claude/skills/일마다 적어둔 절차
ios-releaseios-paywallios-testweb-saas-bootstraplove-bootstrap+39 more
.claude/agents/그 절차를 실행하는 쪽
pentarab-advisorpentarab-job-runnerpentarab-browser-operatormac-appboard-builder+27 more