사람함께에이전트▾
사람 — 직접 정하고 책임지는 비즈니스 규칙과 흐름. 직접 읽어보세요.
함께 — 개념은 알아두고, 세부 규칙은 에이전트가 따릅니다.
에이전트 — 에이전트가 따르는 규칙과 레퍼런스. 필요할 때 찾아보세요.
인프라 아키텍처
내가 쓴 비즈니스 코드는 어디서 실행되든 똑같습니다. 내 PC와 클라우드 사이에서 달라지는 것은 그 둘레입니다. 트래픽이 어디로 들어오는지, 앱이 어디서 실행되는지, 데이터와 배포를 어떻게 관리하는지요. 이 페이지는 그 둘레를 인프라라고 부릅니다.
Akan 앱은 개발자 PC 또는 클라우드 클러스터에서 실행되고, 같은 애플리케이션 코드를 두 환경에 맞게 패키징합니다. 인프라는 세 영역으로 나뉩니다:
영역설명
Local
내 PC입니다. MVP 화면, 기능 프로토타입, 디버깅처럼 빠르게 만들고 확인할 때 씁니다.
Cloud Cluster
팀 공용 환경과 운영에 가까운 워크로드를 위한 Kubernetes 기반 실행 환경입니다.
Master
배포 제어 영역입니다. CI/CD, 환경 파일, 비밀값, 릴리스 자동화를 관리합니다.

이 페이지에서 쓰는 말
용어설명
container
앱과 실행에 필요한 것을 한 묶음으로 포장한 것입니다. 어느 서버에서나 같은 방식으로 실행됩니다.
pod
Kubernetes가 실행하는 단위입니다. 서버(node) 하나에 함께 올라가는 컨테이너 한 개 이상의 묶음입니다.
Ingress
클러스터의 정문입니다. 도메인으로 들어온 외부 트래픽을 받아 안쪽으로 보냅니다.
Service
클러스터 안의 고정 주소입니다. 앱을 실행 중인 pod로 요청을 넘겨 줍니다.
chart
Kubernetes 매니페스트를 묶은 Helm 패키지입니다. Akan이 제공하는 차트는 infra/app에 있습니다.
Secret
데이터베이스 URL 같은 비공개 값을 pod에 환경변수로 넘겨 주는 Kubernetes 객체입니다.
ReadWriteOnceReadWriteMany
볼륨 접근 모드입니다. 앞의 것은 노드 하나만 마운트하고, 뒤의 것은 여러 노드의 pod가 함께 씁니다.
WAL
SQLite의 write-ahead log 모드입니다. 쓰기가 진행되는 동안에도 읽기가 계속될 수 있게 합니다.


따로 준비된 엣지 인프라는 없습니다. infra/에는 클러스터 차트와 배포 제어 영역만 있으므로, 엣지 사이트는 직접 실행하는 컨테이너 하나입니다.
AKAN_PUBLIC_OPERATION_MODE=edge와 AKAN_DATABASE_MODE=single을 설정하고, AKAN_SQLITE_DIR이 볼륨을 가리키게 하면 데이터베이스 파일과 캐시·큐 파일이 모두 그 볼륨에 놓입니다. operation mode는 데이터베이스 모드와 별개이며, 이 모드로 한정할 수 있는 것은 endpoint가 아니라 internal 작업뿐입니다(operationMode: ["cloud"]).어떤 구성을 선택할까?
인프라 이름보다 제품 상황에서 먼저 출발하세요. 작은 내부 도구, 팀 QA 환경, 운영 서비스는 서로 다른 수준의 인프라가 필요합니다. 지금 내 상황에 맞는 칸을 찾으면 됩니다:
MVP 또는 기능 프로토타입→ 로컬
먼저 로컬 개발을 사용합니다. 제품에 공용 데이터, 팀 테스트, 배포 자동화가 필요해질 때까지 구성을 작게 유지합니다.
팀 QA 또는 스테이징→ 클라우드 · debug / develop
debug 또는 develop 환경의 클라우드 배포를 사용해 팀이 같은 서비스를 함께 검증할 수 있게 합니다.
운영 서비스→ 클라우드 · main
같은 클라우드 배포의 main branch를 사용합니다. 차트는 single 모드에서 pod 하나를 실행하므로, 트래픽이 그 한계를 넘기 전에 직접 준비한 Postgres와 Redis로 그 branch를 cluster 모드로 옮깁니다.
트래픽 흐름
인프라는 앱 내부의 비즈니스 코드를 바꾸지 않습니다. 대신 요청이 어떤 경로로 Akan 런타임에 도착할지를 결정합니다. 내 PC에서는 경로가 단순하고, 클라우드 클러스터에서는 구조화된 계층을 거칩니다.
요청 경로
브라우저 또는 기기
도메인 또는 로컬 주소
로컬 개발 서버
클라우드 Ingress
Kubernetes Service
Akan 앱 런타임
페이지 · API · 웹소켓 · 에셋
브라우저 또는 기기
도메인 또는 로컬 주소
로컬 개발 서버
클라우드 Ingress
Kubernetes Service
Akan 앱 런타임
페이지 · API · 웹소켓 · 에셋
로컬 경로
개발자는 localhost로 접속하고 Akan 개발 런타임에 거의 직접 연결됩니다. 화면을 만들고 비즈니스 흐름을 확인하기에 가장 빠른 경로입니다.
클라우드 경로
사용자는 공개 도메인으로 들어옵니다. Kubernetes Ingress가 요청을 받고, Service가 적절한 앱 pod를 찾은 뒤, Akan 런타임이 실제 페이지나 API 응답을 처리합니다.
요청이 Akan App Runtime에 도착하면 런타임은 어떤 종류의 작업인지 분류합니다. 종류마다 응답하는 방식이 다릅니다:
요청 종류설명
Page
브라우저 사용자를 위한 SSR 또는 CSR 페이지 응답입니다.
API
signal과 service 로직을 거치는 비즈니스 작업입니다.
WebSocket
계속 열려 있는 클라이언트 연결로 주고받는 실시간 업데이트입니다.
Asset
정적 파일, 클라이언트 번들, 이미지, 생성 산출물을 파일 그대로 응답합니다.


핵심: 인프라는 앱 안의 비즈니스 동작을 바꾸는 것이 아니라 앱으로 들어오는 경로를 선택합니다. 로컬 경로와 클라우드 경로는 서로 다르게 보이지만 결국 같은 Akan 런타임에 작업을 전달합니다.
데이터베이스 모드
앱 말고도 서비스에는 데이터를 저장할 곳, 백그라운드 작업을 위한 큐, 그리고 캐시가 필요합니다. 데이터베이스 모드는 이 세 역할을 각각 어떤 엔진이 맡을지 정합니다.
처음에는 single 모드로 시작하세요. 대부분의 서비스는 첫날부터 별도 데이터베이스 클러스터가 필요하지 않습니다. 실제 성능 한계, 큐 처리, 다중 인스턴스 운영 요구가 생기면 앱의 비즈니스 구조를 바꾸지 않고 multiple 또는 cluster 모드로 올리면 됩니다.
| 모드 | 데이터베이스 | 실행 위치 | 캐시 · 큐 · PubSub |
|---|---|---|---|
| single | SQLite 파일 하나 | 컨테이너 하나 | SQLite Solid |
| multiple | 모든 컨테이너가 여는, 호스트 볼륨의 SQLite 파일(WAL) 하나 | 호스트 한 대의 여러 컨테이너 | Redis |
| cluster | Postgres | 여러 서버 | Redis |


SQLite는 장난감 서비스용이 아닙니다. single 모드의 기본 데이터베이스는 SQLite이고, WAL 모드를 기본 지원하기 때문에 실제 성능은 상당히 좋습니다. DAU 1만 명 이하의 웬만한 일반 서비스는 실제 사용 데이터가 병목을 증명하기 전까지 single 모드로도 충분한 경우가 많습니다.
모드마다 언제 고르는가
모드언제 → 얻는 것
single
MVP, 초기 내부 도구, 관리자 화면, 콘텐츠 사이트, 많은 중소규모 서비스의 출발점으로 가장 적합합니다.
캐시, PubSub, 큐는 SQLite 파일 기반으로 돌아가며 Bun IPC로 가속합니다.
→ WAL 모드 기준으로 DAU 약 1만 명 이하의 웬만한 제품에는 충분한 성능입니다.
multiple
호스트 한 대에서 캐시, pub/sub, 큐를 함께 써야 하는 여러 컨테이너를 실행할 때 씁니다.
→ cluster보다 가볍습니다. 캐시와 백그라운드 작업은 Redis로 옮기고, 데이터는 SQLite 파일 하나에 그대로 둡니다.
cluster
앱이 여러 서버에 걸치거나, 로컬 동작을 운영 클러스터와 맞추고 싶거나, 더 무거운 관계형 영속성이 필요할 때 씁니다.
→ 가장 운영에 가까운 모드입니다. 무거운 동시 처리와 클러스터 지향 검증에 맞습니다.
모드 선언하기
앱은 자기 배포가 쓸 수 있는 모드를 모두
akan.config.ts에 나열하고, 그중 첫 번째가 기본값입니다:apps/myapp/akan.config.ts
- 배포는 그중 하나를 고릅니다.
AKAN_DATABASE_MODE로 선언된 모드 하나를 고르며, 선언이 하나면 생략해도 되고 여럿이면 배포마다 적어야 합니다. - 이미지 하나가 선언한 모든 모드를 실행합니다. 빌드가 모드마다 드라이버를 싣기 때문에, 같은 이미지로 엣지 사이트는
single로, 클라우드 클러스터는cluster로 실행합니다. - 내 PC에서는 첫 번째 모드로 실행합니다. 셸의
AKAN_DATABASE_MODE가 선언된 다른 모드를 가리키지 않으면akan start는 첫 번째 모드를 씁니다.
로컬 서비스
내 PC에서
multiple은 앱 옆에 Redis가, cluster는 Redis와 Postgres가 떠 있어야 합니다. akan start는 실행하는 모드에 맞춰 이들을 띄우고, akan dbup은 따로 띄웁니다:Terminal
처음 실행할 때
local/docker-compose.yaml을 만들고, 그 뒤로는 이 파일을 직접 관리합니다. 예전 파일에 서비스가 빠져 있다면 직접 추가하거나, 파일을 다른 곳으로 옮겨 두면 다음 akan dbup이 지금의 템플릿으로 다시 만듭니다.

막연히 더 안전해 보인다는 이유로 올리지 마세요. Redis 기반 pub/sub, 분리된 큐/캐시 동작, 더 무거운 동시 쓰기, 운영과 비슷한 배포 검증이 실제로 필요해질 때까지 single을 유지하면 됩니다.
multiple 배포: 호스트 한 대의 docker compose
multiple은 차트 모드가 아니며, 호스트 한 대에서 docker compose로 실행합니다. 모든 replica가 호스트 볼륨의 같은 SQLite 파일을 열고 Redis 하나를 함께 쓰며, 앞단의 리버스 프록시가 replica들에 요청을 나눕니다:docker-compose.yaml
- 앱의 빌드가 multiple을 선언합니다.
database.modes에multiple이 있어야 이미지에 필요한 Redis 드라이버가 실립니다. - SQLite 파일은 호스트의 로컬 디스크에 둡니다.
app-data볼륨은 NFS가 아니라 호스트 자체 파일시스템에 있어야 합니다. - 업로드도 볼륨을 함께 씁니다.
app-files는 모든 replica의/workspace/local에 마운트되며,AKAN_STORAGE_SHARED가 그 사실을 알립니다.
cluster 배포: Kubernetes 차트
infra/app의 차트는 branch 값에 적으면 그 branch를 cluster로 실행합니다. Postgres와 Redis는 직접 운영하고, 차트는 직접 만든 Secret에서 두 URL을 읽습니다:infra/app/values/myapp-values.yaml
- single은 그대로입니다. 항상 SQLite 볼륨 위의 pod 하나로 실행합니다. 그 ReadWriteOnce 볼륨은 두 번째 pod가 마운트할 수 없기 때문입니다.
- Postgres 설정은 URL에 적습니다. 풀 크기, SSL, prepared statement는 쿼리 문자열로 넘깁니다.
?max=20&ssl=require처럼 쓰고, transaction 모드의 PgBouncer 뒤에서는prepare=false를 붙입니다.
업로드한 파일
배포된 multiple이나 cluster 앱은 인스턴스가 여럿이고, 각 인스턴스는 다른 인스턴스가 쓴 파일을 읽을 수 있어야 합니다. 업로드는 둘 중 한 곳에 둡니다:
- 오브젝트 스토리지.
libs/util을 쓴다면 서버 env에objectStorage를 설정하고, 모든 인스턴스가 같은 버킷을 읽습니다. - 공유 볼륨 하나. 모든 인스턴스의
/workspace/local에 마운트하고AKAN_STORAGE_SHARED=true를 설정합니다. 위의 compose 파일과 차트의sharedClaim이 둘 다 해 줍니다.
개발 환경 밖에서는 인스턴스 하나만 읽을 수 있는 로컬 디스크로의 업로드가 거부됩니다.
모드 사이에서 데이터 옮기기
akan db-export는 모델 테이블마다 NDJSON 파일(한 줄에 JSON 행 하나) 하나씩을 쓰고, akan db-import는 그 파일을 데이터베이스로 다시 읽어 들입니다. 둘 다 셸의 AKAN_DATABASE_MODE가 가리키는 모드(기본값은 앱이 선언한 첫 번째 모드)로 실행하며, 앱이 그 모드를 선언해 두어야 합니다:Terminal
- 행은 저장된 그대로 옮겨집니다. 삭제된 행도 함께 가고, 이미 있는 id의 행은 교체되므로 import를 다시 실행해도 됩니다. 텍스트 검색은 import 뒤에 다시 만들어집니다.
- 세션, 대기 중인 작업, 파일은 옮겨지지 않습니다. 세션과 작업은 캐시와 큐에 있으므로 사용자는 다시 로그인합니다. 업로드한 파일은
local/을 공유 볼륨이나 오브젝트 스토리지로 직접 복사합니다. - 배포된 single 앱에서 옮길 때는 SQLite 파일을 복사하고,
SQLITE_DATABASE_PATH가 그 복사본을 가리키게 한 뒤db-export를 실행합니다. - 다른 일은 하지 않습니다. 두 명령 모두 요청을 받지 않고 cron과 init 작업도 돌리지 않은 채 앱을 띄우며,
--dir로 다른 디렉터리를 주지 않으면local/transfer를 씁니다.
cluster의 SQL 콘솔
SQL 콘솔(
runAdminSql)은 SQLite에서는 설정할 것이 없습니다. Postgres에서는 기본 컬럼만 읽을 수 있고 그 밖에는 아무것도 못 하는 전용 로그인 role로 읽습니다:CREATE ROLE <name> LOGIN PASSWORD '…';로 role을 만들고, 다른 role은 부여하지 않습니다.POSTGRES_INSIGHT_URL(또는database.postgres.insightUrl)에 그 role로 로그인하는 URL을 설정합니다.- 모델 테이블마다 부팅할 때 기본 컬럼 권한을 이 role에 줍니다. 앱이 스키마의 소유자가 아니라면 DBA가
GRANT USAGE ON SCHEMA <schema> TO <name>을 실행합니다.
이 role이 없으면 cluster에서 콘솔은 실행을 거부합니다.
SELECT *는 기본 컬럼을 돌려주며, secret을 포함한 모든 모델 필드가 담긴 _doc은 절대 읽을 수 없습니다.성장 단계
인프라는 처음부터 크게 시작할 필요가 없습니다. 비즈니스는 서버 하나와 컨테이너 하나로 시작하고, 트래픽과 안정성 요구가 커질 때 단계적으로 확장하면 됩니다. 세 단계를 한눈에 보면 이렇습니다:
| 단계 | 서버 | 컨테이너 | 데이터베이스 모드 |
|---|---|---|---|
| 1. 싱글 서버안정 | 1대 | 1개 | single |
| 2. 다중 컨테이너베타 | 1대 | 여러 개 | multiple / cluster |
| 3. 클라우드 클러스터베타 | 여러 대 | 여러 개 | cluster |
1단계는 안정화되어 있고, 2, 3단계는 베타입니다. 두 단계의 구성 방법은 위 데이터베이스 모드에 있습니다. 2단계는 호스트 한 대의 docker compose, 3단계는 차트의 cluster 모드입니다.
1. 싱글 서버안정
작은 제품, MVP, 내부 도구, 초기 관리자 화면은 서버 하나와 Akan 컨테이너 하나로 충분히 운영할 수 있습니다. 컨테이너 하나가 데이터베이스, API, 웹, CSR, 이미지 최적화, 캐시, 큐를 모두 처리하고, 데이터베이스도 보통 single 모드면 충분합니다.
차트는 debug/develop pod에 0.05 CPU와 250M을 요청하고, 상한을 0.5 CPU, 1G로 둡니다.

2. 싱글 서버 다중 컨테이너베타
트래픽은 늘었지만 서버 한 대로 아직 충분하다면 같은 서버 안에서 여러 컨테이너를 실행합니다. 더 강한 서버, 더 많은 컨테이너, multiple 또는 cluster 데이터베이스 모드로 올리는 수직 확장 단계입니다.


로드밸런싱 때문에 런타임을 여러 개 띄울 필요는 없습니다. Akan Runtime 하나가 AKAN_REPLICA 환경변수 설정에 따라 여러 child 서버를 실행해 로드밸런싱을 합니다. 안정성 향상이 필요하다면 그때 여러 런타임을 실행하면 됩니다.

3. 클라우드 클러스터 확장베타
서버 한 대로 부족해지면 클라우드 클러스터로 이동합니다. 여러 서버에서 여러 컨테이너가 실행되고, cluster 모드로 데이터베이스/캐시 계층도 운영에 가까운 형태가 됩니다.


Postgres와 Redis는 직접 운영합니다. infra/app의 차트는 branch에
database.mode: cluster를 설정하면 이 단계까지 가고, 두 URL은 Secret에서 읽습니다. infra/ 아래에 Redis나 Postgres 매니페스트는 없습니다. single 모드에서는 pod를 하나로 유지합니다. ReadWriteOnce 볼륨은 두 번째 pod가 마운트할 수 없기 때문입니다.


비즈니스가 요구할 때만 확장하세요. 작게 시작하고 실제 사용량을 측정한 뒤, 싱글 서버에서 다중 컨테이너로, 그리고 클라우드 클러스터로 이동하면 됩니다.