사람함께에이전트▾
사람 — 직접 정하고 책임지는 비즈니스 규칙과 흐름. 직접 읽어보세요.
함께 — 개념은 알아두고, 세부 규칙은 에이전트가 따릅니다.
에이전트 — 에이전트가 따르는 규칙과 레퍼런스. 필요할 때 찾아보세요.
일반▾
인터페이스▾
관측성▾
성능▾
네이티브▾
개발▾

의존성 주입

service는 필요한 것을 serve()의 builder에 적어 두기만 합니다. Akan이 service가 시작되기 전에 그 필드를 채워 주므로, 비즈니스 코드는 작게 유지되고 외부 시스템은 코드를 건드리지 않고 바꿀 수 있습니다.
이 페이지에서 쓰는 말
주입기 (injector)
serve()나 adapt() 안에서 쓰는 service(), plug() 같은 도우미입니다. 하나가 필드 하나를 채웁니다.
adaptor
adapt()로 만든 class입니다. storage나 메일 API 같은 외부 도구 하나를 감쌉니다.
role
StorageAdaptorRole처럼 기본 adaptor가 들어가는 자리입니다. 무엇으로 채울지는 앱이 정합니다.
singleton
서버 프로세스마다 하나뿐인 인스턴스입니다. 주입받는 모든 곳이 같은 것을 씁니다.
server env
env/env.server.<environment>.ts에 있는 객체입니다. 타입은 lib/option.ts의 ModulesOptions입니다.
어떤 주입기를 쓸까
위에서부터 차례로 고르고, 먼저 맞는 것을 씁니다. 표시는 어느 builder에서 쓸 수 있는지를 뜻합니다.
주입기
serve()
adapt()
이 순서로 고릅니다. 먼저 맞는 것이 정답입니다
service<T>()
✓
다른 service의 업무 메서드를 씁니다.
plug(Class)
✓
✓
storage, cache, 메시지 API처럼 교체할 수 있는 도구를 씁니다.
use<T>()
✓
✓
option.ts에 등록된 레거시 singleton입니다. 알아보기만 하고 새로 쓰지는 않습니다.
env(factory)
✓
✓
런타임 설정을 함수마다 넘기지 않고 읽습니다.
특정 용도 전용
memory(Type)
✓
✓
호출 사이에도 유지되는 작은 값입니다.
signal<T>()
✓
이벤트를 발행하거나 작업을 큐에 넣는 server signal입니다. 필드 이름은 Signal로 끝납니다.
✓쓸 수 있음쓸 수 없음
  • 쓰는 것만 꺼냅니다. ({ service, env }) => ({ … })처럼 이 class에 필요한 주입기만 적습니다.
  • onInit() 전에 모든 필드가 채워집니다. 그래서 onInit()에서 주입받은 값을 바로 쓸 수 있습니다.

Service 주입

한 service가 다른 service의 업무 메서드를 써야 하면 service<T>()로 선언합니다. 직접 import해서 생성하는 것보다 흐름이 분명합니다:
apps/koyo/lib/article/article.service.ts
  • 필드 이름이 service를 고릅니다. subscriptionService는 subscription service로 연결되므로, 이름은 Service로 끝나야 합니다. 타입 인자는 타입을 알려 줄 뿐입니다.
  • lib의 service는 namespace를 거칩니다. 앱에서는 srv.shared.FileService로 타입을 적고, 필드 이름은 그대로 fileService입니다.
  • srv는 타입으로만 import합니다. import type * as srv from "../srv"로 적어야 런타임 import가 가볍게 유지됩니다.
  • 자동 생성 메서드는 이미 들어 있습니다. database service에는 this.getArticle, this.updateArticle, this.articleModel이 처음부터 있습니다.

adapt와 plug

자체 동작이 있고 나중에 교체될 수 있는 도구는 adaptor로 만듭니다. service는 class나 role을 요청할 뿐, client를 직접 만들지 않습니다.
1. srvkit/에 선언하기
adaptor는 srvkit/ 아래에 adapt() class로 작성합니다. 아래 예시는 앱이 쓰는 storage 위에 이미지 경로 규칙을 얹습니다:
apps/koyo/srvkit/imageStorage.ts
2. service에 plug하기
service는 plug()로 class를 지정하고, 다른 필드처럼 호출합니다:
apps/koyo/lib/article/article.service.ts
  • 등록은 plug(Class)가 전부입니다. option.ts에 적을 것이 없고, class 자체가 식별자입니다.
  • 프로세스마다 하나입니다. ImageStorage를 plug한 service는 모두 같은 객체를 씁니다.
  • this.logger는 기본으로 있습니다. adaptor 안에서 Logger를 만들지 말고, 초기화 작업은 override async onInit()에 둡니다.
  • 다른 adaptor와 겹치지 않는 이름을 줍니다. adapt()에 넘기는 이름은 as const로 적고, 앱과 lib 전체에서 하나만 있어야 합니다.
3. 기본 role 교체하기
프레임워크 인프라는 role로 plug합니다. plug(StorageAdaptorRole)은 그 role을 채운 구현을 받으며, role 아홉 개와 기본 구현은 다음과 같습니다:
Role기본 구현
↳ 쓰임
DatabaseAdaptorRoleSqliteDatabase
document 저장과 쿼리
CacheAdaptorRoleSolidCache
memory() 값과 document cache
StorageAdaptorRoleBlobStorage
업로드한 파일. 기본은 로컬 디스크입니다
QueueAdaptorRoleSolidQueue
signal이 큐에 넣는 백그라운드 작업
ScheduleAdaptorRoleScheduler
cron과 interval 작업
LoggingAdaptorRoleConsoleLogger
레벨별 로그 출력
WebsocketAdaptorRoleSolidPubSub
websocket 클라이언트의 pubsub room
CompressAdaptorRoleJsonCompressor
타입이 있는 값을 바이트로 바꾸고 되돌리기
LlmAdaptorRoleOpenaiLlm
인페이지 에이전트 relay의 LLM 호출
앱 전체에서 하나를 바꾸려면 lib/option.ts에서 applyAdaptor를 부릅니다:
apps/koyo/lib/option.ts
  • 교체 구현은 role의 interface를 구현합니다. R2Storage는 implements StorageAdaptor를 붙인 adapt() class입니다.
  • 교체할 수 있는 것은 이 아홉 role뿐입니다. applyAdaptor는 다른 class를 무시하며, adaptor를 등록하는 방법도 아닙니다.
  • 마지막 결정은 앱이 합니다. 앱의 option.ts는 모든 lib 다음에 읽히므로 앱의 선택이 이깁니다.
  • 기본 구현은 database mode를 따릅니다. database는 multiple 모드에서도 SQLite이고, cluster 모드에서는 Postgres로 바뀝니다. 두 모드 모두 cache와 websocket은 Redis, queue는 BullMQ를 씁니다.

환경값 읽기

env()는 service나 adaptor가 시작될 때 런타임 설정으로 값을 만듭니다. 앱 정보, hostname, feature flag가 필요한 코드에서 씁니다.
필요한 값
↳ 읽는 방법
server env의 필드: hostname, feature flag, API 옵션
env((options: ModulesOptions) => options.hostname)
앱 정보: appName, environment, operationMode
env(() => getEnv().operationMode)
컨테이너 환경 변수나 secret
env(() => process.env.PAYMENT_KEY)
직접 설정값 추가하기
  1. lib/option.ts의 ModulesOptions에 필드를 선언합니다.
  2. 값이 필요한 env/env.server.<environment>.ts마다 값을 적습니다.
  3. service나 adaptor에서 env()로 읽습니다.
1단계와 2단계는 각각 몇 줄이면 됩니다:
apps/koyo/lib/option.ts · apps/koyo/env/env.server.local.ts
3단계에서는 앱 정보와 함께 읽습니다:
apps/koyo/lib/article/article.service.ts
  • 시작할 때 한 번 실행됩니다. 값은 프로세스가 끝날 때까지 고정되며, factory는 async여도 됩니다.
  • factory 함수를 넘깁니다. env()는 함수를 받으며, env("KEY") 같은 형태는 없습니다.
  • getEnv()는 factory 안에서 부릅니다. 모듈 최상단에서 부르면 앱 환경값이 없는 akan build 도중에 오류가 납니다.

꿀팁

  • client는 한 번만 선언합니다. 외부 client를 메서드마다 만들지 말고, adapt() class로 한 번 선언해 plug()로 받습니다.
  • 업무 협력은 service(), 인프라는 plug(). 업무 흐름은 service끼리 잇고, 교체할 수 있는 인프라는 adaptor로 받습니다.
  • raw credential보다 준비된 client를 주입합니다. secret은 server env나 process.env에 두고, 모듈 최상단이 아니라 함수 안에서 process.env.X ?? options.x ?? generate(…) 순서로 읽습니다.
  • adapt()는 singleton 전용입니다. 쓸 때마다 새로 만드는 값 객체는 호출하는 곳에서 new 하는 평범한 class로 둡니다.
더 읽을 곳

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

내 AI에 이 문서 연결하기

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