사람함께에이전트▾
사람 — 직접 정하고 책임지는 비즈니스 규칙과 흐름. 직접 읽어보세요.
함께 — 개념은 알아두고, 세부 규칙은 에이전트가 따릅니다.
에이전트 — 에이전트가 따르는 규칙과 레퍼런스. 필요할 때 찾아보세요.
CLI 레퍼런스▾
AkanJS 레퍼런스▾
UI 레퍼런스▾

akanjs/server

akanjs/server는 Akan의 서버 쪽 패키지입니다. 앱을 띄우고, 서버 설정을 담고, 라우터보다 먼저 페이지 로드를 봅니다. main.ts, lib/option.ts, srvkit/ 같은 서버 파일에서만 import합니다.
이 페이지에서 쓰는 말
gateway
앞단 프로세스입니다. replica를 띄우고 HTTP와 WebSocket 요청을 replica로 넘깁니다.
replica
모듈을 실행하는 서버 프로세스 하나입니다. 요청을 받거나, batch 작업을 돌리거나, 둘 다 합니다.
solo
gateway 없이 main.ts 프로세스 안에서 바로 도는 replica 하나입니다.
RSC worker
서버에서 페이지를 그리는 별도 프로세스입니다. 페이지를 서비스하는 replica마다 하나씩 있습니다.
web proxy
라우터보다 먼저 페이지 로드를 받아 redirect하거나, rewrite하거나, 직접 응답하는 class입니다.
export 목록
main.ts에서 앱을 띄웁니다. 프로세스 하나로, 또는 gateway와 replica로 실행합니다.
new AkanApp()이 받는 옵션입니다. replica 수, 포트, 경로 prefix, 부팅할 모듈을 정합니다.
AkanServer
replica 하나의 서버입니다. server.ts에 생성되며, 어떤 웹 표면을 서비스할지 정합니다.
AkanLib
앱이나 lib 하나의 모듈과 option을 묶습니다. 이것도 server.ts에 생성됩니다.
lib/option.ts가 export하는 builder입니다. 주입 값, middleware, proxy, MCP·LLM 설정을 담습니다.
web proxy가 돌려주는 helper입니다. 요청을 계속 보내거나, rewrite하거나, redirect합니다.
web proxy class가 구현하는 interface입니다. use(request) 메서드 하나뿐입니다.
기본 proxy 두 개입니다. 모든 앱이 자기 proxy보다 먼저 실행합니다.
legacy decorator입니다. 에러를 던지는 대신 경고를 남기고 undefined를 반환합니다.
legacy decorator입니다. 메서드를 DB transaction 안에서 실행합니다.
그 밖의 export
빌드 artifact 타입과 console·OAuth·sitemap·metrics helper입니다. 프레임워크와 CLI가 씁니다.
어디서 import하나
main.ts
이 경로로 import해야 gateway가 SSR 렌더러를 싣지 않습니다.
server.ts
생성되는 파일이므로 직접 고치지 않습니다.
lib/option.ts
앱마다, lib마다 하나씩 있습니다.
srvkit/*.ts
서버 전용 helper입니다. proxy class와 legacy decorator를 쓰는 class가 있습니다.

AkanApp

AkanApp은 main.ts가 실행하는 객체입니다. 서버 프로세스를 몇 개 띄울지 정하고, 컨테이너가 멈출 때까지 그 프로세스들을 살려 둡니다.
main.ts 전체는 이 정도입니다:
apps/myapp/main.ts
  • 두 가지로 부릅니다. new AkanApp(serverPath?, options?) 또는 new AkanApp(options)입니다. server 경로 기본값은 main.ts 옆의 ./server입니다.
  • start()가 전부 띄웁니다. solo일 때는 같은 프로세스에서 server.ts를 불러오고, 아니면 replica마다 자식 프로세스를 하나씩 띄웁니다.
프로세스 하나, 또는 gateway
요청을 받는 replica가 하나뿐이면 나눠 줄 대상이 없으므로, AkanApp은 그 replica를 자기 프로세스 안에서 실행합니다. 그 밖의 경우에는 앞에 gateway를 둡니다:
설정
solo 실행
gateway 실행
기본값
AKAN_REPLICA=0,0,1
✓
요청과 batch 작업을 모두 맡는 replica 하나입니다. 나눠 줄 대상이 없습니다.
gateway가 다시 필요한 경우
AKAN_REPLICA=0,0,2
✓
replica가 둘 이상이면 gateway가 요청을 나눠 줍니다.
AKAN_REPLICA=0,1,0
✓
batch 전용 replica는 요청을 받지 않으므로 health check는 gateway가 답합니다.
new AkanApp({ replica })
✓
코드에 replica 구성을 적으면 그 구성을 위한 gateway를 띄웁니다.
AKAN_SOLO=false
✓
replica가 하나여도 gateway를 띄웁니다.
akan start
✓
개발 서버는 항상 gateway로 실행합니다.
✓이렇게 실행해당 없음
gateway가 하는 일은 네 가지입니다:
replica 실행
replica를 자식 프로세스로 띄우고, 죽으면 1초, 2초, 4초… 최대 30초 간격으로 다시 띄웁니다.
요청 중계
HTTP는 unix socket으로, WebSocket은 로컬 포트로 각 replica에 넘깁니다.
상태 보고
replica마다 metrics를 모아 전체 프로세스의 상태를 알려 줍니다.
/_akan/app/health · /_akan/app/metrics
깔끔한 종료
SIGINT나 SIGTERM을 받으면 replica마다 종료를 요청하고, 30초 뒤에도 남은 프로세스는 강제로 끝냅니다.
  • solo도 같은 경로에 답합니다. solo 프로세스도 /_akan/app/health와 /_akan/app/metrics를 gateway와 같은 모양으로 제공하므로, probe는 어느 쪽이든 똑같이 읽습니다.
  • solo 프로세스를 다시 띄우는 것은 오케스트레이터뿐입니다. 컨테이너에 liveness, readiness probe를 걸어 둡니다.

AkanAppOptions

모든 필드는 선택입니다. 아무것도 주지 않으면 8282 포트에서 replica 하나로 실행합니다. 각 필드는 옆에 적힌 환경변수로도 줄 수 있고, 둘 다 있으면 옵션이 이깁니다.
replicanumber | string기본값 "0,0,1"AKAN_REPLICA
federation,batch,all 순서의 replica 수입니다. 여기에 적으면 gateway가 켜집니다.
serverPathstring기본값 "./server"
replica가 실행할 server 모듈입니다. main.ts 옆에서 찾습니다.
runtimeDirstring기본값 local/apps/<app>/runtimeAKAN_RUNTIME_DIR
replica 소켓과 순환 로그 파일을 두는 곳입니다. NODE_ENV=production이면 ./runtime입니다.
portnumber기본값 8282PORT
앱이 요청을 받는 포트입니다. 서버가 자기 자신을 호출할 때도 이 포트를 씁니다.
wsBasePortnumber기본값 port + 10000AKAN_WS_BASE_PORT
i번 replica는 이 포트에 i를 더한 포트로 gateway의 WebSocket 요청을 받습니다.
openapiboolean기본값 falseAKAN_OPENAPI
모든 endpoint를 설명하는 /openapi.json을 제공합니다.
prefixstring기본값 "/api"AKAN_API_PREFIX
endpoint가 붙는 경로입니다. CSR·모바일 번들은 akan.config.ts의 api.prefix를 따릅니다.
websocketPrefixstring기본값 "/ws"AKAN_WS_PREFIX
prefix 아래에서 WebSocket 연결을 받는 경로입니다.
modulesstring[]AKAN_MODULES
이 모듈과 이 모듈이 닿는 모듈만 부팅합니다. 비워 두면 활성화된 모듈을 전부 부팅합니다.
disableModulesstring[]AKAN_DISABLE_MODULES
이 모듈과 이 모듈에 닿는 모듈만 빼고 전부 부팅합니다. modules 다음에 적용됩니다.
disableLibsstring[]AKAN_DISABLE_LIBS
지정한 lib이 등록한 모듈 전부와, 그 모듈에 닿는 모듈을 뺍니다.
solobooleanAKAN_SOLO
solo·gateway 자동 선택을 덮어씁니다. 환경변수로는 solo를 끌 수만 있고 켤 수는 없습니다.
replica 값 읽는 법
replica는 쉼표로 나눈 숫자 세 개이고, 자리마다 역할이 다릅니다:
1federation
요청을 받습니다. serverMode: "batch"로 선언한 작업은 건너뜁니다.
2batch
요청을 받지 않습니다. serverMode: "federation"으로 선언한 작업은 건너뜁니다.
3all
요청도 받고 모든 작업을 실행합니다.
gateway 뒤에 replica 세 개를 두고, 모두 article 모듈만 부팅하는 예시입니다:
apps/myapp/main.ts
  • 1,0,2는 프로세스 세 개입니다. federation replica 하나와 all replica 둘이고, 앞에 gateway가 섭니다.
  • 숫자 하나만 쓰면 federation입니다. replica: 3은 3,0,0이므로, serverMode: "batch"로 선언한 작업은 어디서도 돌지 않습니다.
  • 모듈은 의존하는 모듈을 데려옵니다. modules: ["article"]는 article이 주입받는 service와 signal까지 모든 replica에서 함께 부팅합니다.

AkanServer 웹 표면

앱은 API 말고도 웹 표면을 최대 두 개 제공합니다. SSR 페이지와, 모바일 앱에 들어가는 CSR 번들입니다. 무엇이 있을지는 빌드가 정하고, 실행 중에는 끄는 것만 할 수 있습니다.
설정
API
SSR
CSR
빌드 — akan.config.ts
web: true
✓
✓
✓
기본값입니다. 페이지, 모바일 번들, API를 모두 제공합니다.
web: { csr: false }
✓
✓
모바일 번들이 없어 /__csr와 ?csr=true가 사라집니다. native 설정이 있으면 쓸 수 없습니다.
web: false
✓
API 전용 빌드입니다. page/ 아래는 아무것도 제공하지 않습니다.
실행 중 — 환경변수
AKAN_CSR=false
✓
✓
이 배포에서 CSR 번들만 끕니다.
AKAN_SSR=false
✓
페이지와 RSC worker를 끕니다. CSR 번들은 SSR 스타일시트를 쓰므로 함께 꺼집니다.
✓제공함제공 안 함
배포 하나에서만 표면을 끌 때는 환경변수를 씁니다:
Terminal
  • 실행 중에는 좁히기만 합니다. false나 0이면 표면을 끄고, 빌드에서 뺀 표면은 다시 켤 수 없습니다.
  • 코드로도 똑같이 합니다. server.setWeb(true | false | { csr })나 server.init({ web })로, 서버가 시작하기 전에 같은 방식으로 좁힙니다.
  • SSR을 끄는 이유. SSR은 RSC 렌더러와, replica마다 따로 도는 RSC worker 프로세스로 이뤄집니다. API 전용 프로세스는 둘 다 띄우지 않습니다.
  • 개발 서버는 무시합니다. akan start는 web 설정과 상관없이 모든 표면을 제공합니다.

AkanOption

lib/option.ts는 AkanOption 하나를 export합니다. 주입할 값부터 MCP·LLM 설정까지, lib이나 앱이 가진 서버 설정을 담습니다.
use(fn | object)
service가 use<T>()로 읽을 값을 등록합니다. 함수는 env를 받고, Promise 값은 기다렸다가 씁니다.
applyMiddleware(...classes)
signal middleware를 추가합니다. Logging과 Timeout은 이미 등록되어 있습니다.
applyAdaptor(role, adaptor)
LlmAdaptorRole 같은 기본 adaptor 역할을 내 class로 바꿉니다.
applyWebProxy(...proxies)
web proxy를 추가합니다. class 또는 { proxy, matcher } 형태로 넘깁니다.
setMcp(option | fn)
/mcp에 뜨는 MCP 서버의 설정입니다. false를 주면 MCP 서버를 내립니다.
setAgentAccess(guards)
agent 채팅으로 LLM 키를 쓰려면 통과해야 하는 guard입니다. 여러 개면 모두 통과해야 합니다.
setLlm(option | fn)
agent relay가 말을 거는 모델입니다. apiKey, model, host 등을 줍니다.
setCrossSite(option)
브라우저가 mutation을 보내고 웹소켓을 열어도 되는 다른 origin입니다. { enabled: false }면 검사를 끕니다.
앱의 option은 보통 이렇게 생겼습니다:
apps/myapp/lib/option.ts
  • 타입 인자는 env의 모양입니다. AkanOption<ModulesOptions>가 함수 형태가 받는 값의 타입을 정하므로, 키와 secret은 앱의 서버 env에서 꺼냅니다.
  • use는 생성자 방식 client용입니다. adapt() adaptor는 스스로 등록되므로 여기에 적지 않습니다.
여러 lib이 같은 설정을 하면
lib의 option은 마운트 순서대로 읽고, 앱의 option을 마지막에 읽습니다:
use
키는 모든 lib을 통틀어 겹치면 안 됩니다. llmOption은 예약된 키입니다.
applyMiddleware
refName마다 하나입니다. 나중에 등록한 것이 이깁니다.
applyAdaptor
같은 역할이면 마지막 override가 이깁니다.
applyWebProxy
모두 실행됩니다. 기본 proxy 두 개가 먼저, 그다음 lib 순서대로입니다.
setMcp
필드 단위로 합쳐지고, 앱의 값이 이깁니다.
setLlm
필드 단위로 합쳐집니다. lib이 host를, 앱이 키를 정할 수 있습니다.
setAgentAccess
마지막 호출이 이깁니다. null은 lib이 정한 값을 지웁니다.
setCrossSite
마지막 호출이 이깁니다.

AkanResponse

AkanResponse는 web proxy의 use()가 돌려줄 값을 만듭니다. 요청을 계속 보낼지, 다른 URL로 옮길지, 여기서 끝낼지를 정합니다.
helper
↳ 결과
AkanResponse.next({ request: { headers } })
바꾼 header를 들고 다음 proxy와 page로 넘어갑니다.
AkanResponse.rewrite(url, { request? })
새 URL로 계속 진행합니다. 브라우저 주소창은 원래 URL 그대로입니다.
AkanResponse.redirect(url, status = 307)
redirect Response를 돌려줍니다. 뒤의 proxy와 page는 실행되지 않습니다.
세 helper를 모두 쓰는 proxy입니다:
apps/myapp/srvkit/docsRoutingProxy.ts
  • 넘긴 header는 원래 header를 통째로 대신합니다. new Headers(request.headers)에서 시작하고, 아무것도 넘기지 않으면 원래 header가 그대로 갑니다.
  • rewrite는 요청을 그대로 둡니다. method, body, route params는 유지되고 URL만 바뀝니다.
  • redirect는 평범한 Response입니다. 어떤 Response든 돌려주면 똑같이 거기서 끝납니다.
  • 클라이언트 쪽 이동에는 하나도 적용되지 않습니다. /en/help로 가는 <Link>는 redirect도 rewrite도 없이 /en/help 자체를 그리므로, proxy가 골랐을 페이지로 바로 링크합니다.

WebProxy

WebProxy는 use(request) 메서드 하나를 가진 class입니다. 라우터보다 먼저 모든 페이지 로드를 보므로, redirect, host 기반 라우팅, page가 읽을 header 설정에 알맞습니다. <Link> 클릭이나 router.push 같은 클라이언트 쪽 이동은 페이지 로드가 아니어서 proxy에 닿지 않습니다.
점검 중에 shop 페이지를 닫는 proxy입니다:
apps/myapp/srvkit/maintenanceProxy.ts
lib/option.ts에 등록하고, matcher로 shop 페이지만 걸러냅니다:
apps/myapp/lib/option.ts
  • 페이지 로드에만 적용됩니다. API prefix 아래 endpoint, WebSocket, /_akan/*, 그리고 /__rsc로 불러오는 클라이언트 쪽 이동은 proxy를 거치지 않습니다. 이동에도 기본 locale·basePath 처리는 그대로 적용됩니다.
  • 기본 proxy가 먼저 돕니다. LocaleWebProxy와 HostBasePathWebProxy가 내 proxy보다 먼저 실행되므로, 내가 받는 경로는 이미 locale로 시작합니다.
  • proxy는 앞 proxy가 넘긴 요청을 받습니다. 등록한 순서대로 실행되며, 앞에서 바꾼 header나 URL을 다음 proxy가 그대로 읽습니다.
  • static refName은 꼭 있어야 합니다. proxy의 이름이며, class 타입이 이 값을 요구합니다.
use()의 반환값
undefined
요청을 손대지 않고 넘깁니다.
Response
바로 응답합니다. 뒤의 proxy와 page는 실행되지 않습니다.
AkanResponse.nextAkanResponse.rewrite
새 header나 새 URL로 계속 진행합니다. 자세한 내용은 AkanResponse 절에 있습니다.
matcher
(생략)
페이지 경로만 받습니다. /__csr, /_akan/*, 확장자가 붙은 경로는 건너뜁니다.
"/ko/shop"
그 경로와 그 아래 전부입니다.
/^\/[a-z]{2}\/shop/
pathname에 대해 검사하는 RegExp입니다.
(request) => boolean
요청 전체를 보고 직접 판단하는 함수입니다.
기본 proxy
LocaleWebProxy
locale이 없는 경로를 /<locale>/…로 redirect(307)하고 x-locale, x-path header를 붙입니다.
HostBasePathWebProxy
요청 host를 akan.config.ts의 routes에 적은 basePath로 연결하고 그 경로로 rewrite합니다.
타입
WebProxy
interface입니다. use(request)는 WebProxyReturn을 동기나 비동기로 돌려줍니다.
WebProxyCls
proxy class입니다. 인자 없이 생성되고 static refName을 가집니다.
WebProxyRegistration
applyWebProxy가 받는 값입니다. class 또는 { proxy, matcher? }입니다.
WebProxyMatcher
경로 prefix string, RegExp, (request) => boolean 중 하나입니다.
WebProxyReturn
Response, WebProxyResult, undefined 중 하나입니다.
WebProxyResultWebProxyNextInit
next와 rewrite가 돌려주는 값, 그리고 두 helper가 받는 { request: { headers } }입니다.

Try

@Try()는 실패해도 호출한 쪽이 멈추면 안 되는 외부 호출에 쓰던 legacy method decorator입니다. 메서드가 에러를 던지면 경고를 남기고 대신 undefined를 반환합니다. libs/util의 storage adaptor가 아직 씁니다.
생성자 방식 client에 붙인 legacy 형태입니다:
apps/myapp/srvkit/partnerApi.ts
  • this.logger로 기록합니다. logger 필드가 없는 class에서는 에러가 조용히 사라집니다.
  • 호출한 쪽은 undefined를 받습니다. 결과를 쓰기 전에 확인합니다.
  • 메서드가 async가 됩니다. 동기 메서드에 붙여도 감싼 함수는 항상 Promise를 돌려줍니다.
새 코드는 adapt() adaptor 안에서 에러를 직접 잡습니다. catch → logger.error → return null 순서입니다:
apps/myapp/srvkit/partnerApi.ts

Transaction

@Transaction()은 같은 파일에 있는 legacy method decorator로, 서버 쪽 service에 붙입니다. 전부 되거나 전부 안 됩니다. 메서드가 끝나면 commit하고, 에러를 던지면 rollback합니다.
DB service에서, 함께 반영되어야 하는 쓰기 두 번을 묶는 예시입니다:
apps/myapp/lib/wallet/wallet.service.ts
  • 안쪽 호출은 바깥 transaction에 합류합니다. transaction 안에서 다른 transaction 메서드를 부르면 바깥 transaction에서 함께 실행됩니다.
  • DB가 있어야 합니다. model이나 DB service에서 DB를 찾으며, 그 밖의 class에서는 에러를 던집니다.
  • 캐시는 decorator로 하지 않습니다. 응답을 기억해 두려면 query endpoint에 { cache: <ms> }를 선언하거나, 값을 memory(...) 필드에 담으세요.

MIT 라이선스 하에 배포되었습니다.

내 AI에 이 문서 연결하기

MCPhttps://akanjs.com/mcp
Copyright © 2026 Akan.js 모든 권리 보유.시스템 관리자bassman