사람함께에이전트▾
사람 — 직접 정하고 책임지는 비즈니스 규칙과 흐름. 직접 읽어보세요.
함께 — 개념은 알아두고, 세부 규칙은 에이전트가 따릅니다.
에이전트 — 에이전트가 따르는 규칙과 레퍼런스. 필요할 때 찾아보세요.
앱 & 라이브러리▾
도메인▾
스칼라▾
Service module 개요
토큰 서명, 저장된 파일을 브라우저로 돌려보내기, OAuth 핸드셰이크를 끝까지 진행하기는 모두 레코드가 아닙니다. 저장된 model 중심의 폴더를 쓰면 비워 둘 파일 다섯 개와 채울 파일 하나가 생길 뿐입니다.
service module은 그 폴더에서 model을 뺀 것입니다.
위치
앞에 밑줄(_)을 붙인
lib/_<service>에 둡니다. 안의 파일 이름에서는 밑줄을 뗍니다.libs/util/lib/_security/security.service.ts맡는 일
테이블이 아니라 동작이나 기능을 맡습니다. 나열하거나 고치거나 내일까지 남겨 둘 데이터가 없습니다.
sign · encrypt · stream · authorize없는 것
document 파일, filter, slice, 생성된 CRUD가 없습니다. 뒤에 테이블이 없기 때문입니다.
no *.document.ts · no slice()호출 경로
model module과 같은 경로에서 document 계층만 빠집니다.
fetch → signal → service → srvkit/_oauth를 예로 들면, service module을 지나는 호출 하나는 이렇습니다:Service module을 지나는 호출 하나
페이지, store, MCP client
런타임 자체cron · process · initialize
oauth.signal.tsendpoint · internal
oauth.service.tsworkflow
다른 module의 serviceservice<srv.UserService>()
srvkit/의 adapterplug() · use()
외부 시스템
페이지, store, MCP client
런타임 자체cron · process · initialize
fetch.listOAuthConnections()
oauth.signal.tsendpoint · internal
oauth.service.tsworkflow
다른 module의 serviceservice<srv.UserService>()
srvkit/의 adapterplug() · use()
외부 시스템
- 들어오는 길은 둘입니다. 호출자는
fetch로Endpoint를 부르고, 런타임은 스케줄, 큐 작업, 시작 시점에Internal을 실행합니다. - 일은 service가 합니다. 다른 module은
service<srv.X>()로, 외부 시스템은srvkit/의 adapter로 부릅니다.
지금 있는 여덟 개
이 워크스페이스에는 service module이 여덟 개 있고, 설명을 읽기보다 실물을 보는 편이 빠릅니다. 서버 전용 primitive부터 인가 서버 하나 전체까지 있고, 앱과 라이브러리마다 빈 루트 컨테이너가 하나씩 있습니다.
모듈설명
_security
JWT 서명과 검증, AES 암복호화, refresh token 발급을 맡습니다. 서버 전용이라 store도 UI도 없습니다.
_oauth
/mcp가 받아 주는 OAuth 2.1 토큰을 발급하는 인가 서버입니다._doc
Akan.js 문서를 MCP로 에이전트에게 제공합니다. 생성된 폴더를 읽기만 하고 아무것도 쓰지 않습니다.
_localFile
지정한 경로로 들어온 요청에 공개 blob을 HTTP
Response로 스트리밍합니다. 파일 네 개, endpoint 하나입니다._util_shared
라이브러리의 루트 컨테이너입니다. 빈 batch service와, 여러 module이 함께 쓰는 client store를 둡니다.
_akan_minimal
앱의 루트 컨테이너입니다.
_akan은 아직 빈 스캐폴드이고, _minimal은 벤치마크 endpoint 네 개를 더했습니다.여덟 개 모두에 있는 파일은 네 개뿐입니다. 선택 파일을 가진 module은 다음과 같습니다:
모듈
store
*.store.ts
test
*.test.ts
Util
*.Util.tsx
Zone
*.Zone.tsx
기능 module
_security
✓
_oauth
✓
_doc
✓
service를 직접 테스트합니다:
doc.service.test.ts._localFile
루트 컨테이너
_util
✓
_shared
✓
_akan
✓
store는 빈 스캐폴드입니다.
_minimal
✓
store는 빈 스캐폴드입니다.
✓파일 있음파일 없음
여덟 개 중 Util이나 Zone을 가진 것은 하나도 없습니다. 이 워크스페이스만의 우연이 아닙니다. 왜 드문지, 대신 무엇을 쓰는지는 UI 문서 두 개에 있습니다.
양 끝
작은 기능 module인
_security와 가장 큰 _oauth를 나란히 놓으면 파일 종류는 똑같이 다섯입니다. 다른 것은 각 파일이 담는 양입니다:_security · 바닥service는 secret 두 개를 들고 서명하거나 암호화한 문자열을 돌려줄 뿐입니다. 화면에 그려지는 것이 없으니 store도 component도 없습니다.
- abstract.md
- 맡는 일과 규칙 네 개
- dictionary.ts
- endpoint label
- service.ts
- secret 두 개를 쥔 75줄 남짓
- signal.ts
- mutation 하나,
encrypt - signal.test.ts
- barrel을 부팅해 호출
_oauth · 천장인가 서버 하나 전체인데도 store가 없습니다. 필요한 화면은 다른 화면의 한 구획이 아니라
libs/shared/page/oauth의 route이기 때문입니다.- abstract.md
- 규칙 여덟 개와 workflow 체인
- dictionary.ts
.endpoint()에 label,.error()에 error key,.translate()에 동의 화면 문구- service.ts
- 500줄 남짓: PKCE, 토큰 교체, 폐기
- signal.ts
- endpoint 10개, 그중 5개는 origin 루트 경로
- signal.test.ts
- 프로토콜 전체를 처음부터 끝까지


상태가 있는 service module이라도 테이블을 만들지 않습니다.
_oauth는 client, request, grant를 모두 memory(Map, { of: cnst.OauthGrant }) 캐시에 담고, 담는 모양은 libs/shared/lib/__scalar/ 아래의 scalar입니다. scalar는 JSON 텍스트로 오가므로 같은 선언이 Redis 캐시와 sqlite 캐시를 그대로 왕복합니다.Service 파일 구성
네 파일은 언제나 있습니다. 나머지는 기능에 필요해질 때 생기며, 두 목록 모두 이 섹션 문서의 순서를 따릅니다.
항상 있는 네 파일
파일설명
<service>.abstract.md
제목, 무엇을 맡는지 한 문장, 그리고 코드로는 보이지 않는 불변식을 적은
## Rules입니다.<service>.dictionary.ts
serviceDictionary로 만듭니다. endpoint label, error key, UI 문구를 담습니다.<service>.service.ts
workflow 본체입니다. module 이름을 넘긴
serve()로 만들고, 본문이 비어 있어도 둡니다.<service>.signal.ts
<X>Internal과 <X>Endpoint 두 class입니다. 넘겨 볼 테이블이 없으니 Slice는 없습니다.필요할 때만 생기는 파일
파일설명
<service>.store.ts
client state가 있을 때만 씁니다. 여덟 중 넷에 있고, 그중 둘은 빈 스캐폴드입니다.
<service>.signal.test.ts
barrel 전체를 부팅하고
fetch로 endpoint를 호출합니다. _security와 _oauth에 있습니다.<Service>.Util.tsx<Service>.Zone.tsx
드뭅니다. 여덟 중 하나도 없습니다. 이유는 이 섹션의 UI 문서 두 개에 있습니다.
빈 파일도 남겨 둔다
가장 자주 실수처럼 보이는 규칙입니다. 스캐폴드 파일은 아무것도 담지 않아도 트리에 남깁니다. 아래는
libs/util/lib/_util/util.signal.ts의 전문이며, 손대지 않은 그대로입니다:libs/util/lib/_util/util.signal.ts
- export한 class 둘, method 0개. 이것이 파일 전체이고, 그대로 둡니다.
- 지우면 워크스페이스가 작아지는 것이 아니라 달라집니다. 다음 개발자는 이미 열린 파일의 어디에 endpoint를 둘지가 아니라, 어느 파일에 둘지부터 정해야 합니다.
- 첫 endpoint가 한 줄짜리 diff로 끝납니다. 파일이 없으면 새 파일을 만드는 diff가 됩니다.
자주 만나는 빈 형태
파일설명
signal.ts
builder callback은 아무것도 반환하지 않는 것이 아니라 빈 객체를 반환합니다.
service.ts
method가 없는 루트 컨테이너도 service는 선언합니다.
store.ts
// state와 // action 주석 두 줄만 있고, 각 절반이 들어갈 자리를 표시합니다.apps/akan/lib/_akan이 정확히 이 상태입니다. service, signal, store가 모두 비어 있습니다. apps/minimal/lib/_minimal도 벤치마크 endpoint 옆에 같은 빈 store를 두고 있고, 둘 다 정리 대상이 아닙니다.Model module인가, service module인가
질문 하나로 정해집니다. 나열하고 걸러 보고, 다음 주에도 다시 찾을 행이 있습니까?
- 있다면 model module입니다.
lib/<model>에 두고, 쓰려던 service module은 그 module의 service method 하나가 됩니다. - 없다면 service module입니다.
lib/_<service>에 둡니다.
Model module
lib/<model>document 파일, filter, slice, 생성된 CRUD, 다섯 가지 UI 역할을 갖춘 저장 테이블입니다.
user · file · banner · notificationService module
lib/_<service>테이블도 document 파일도 slice도 없습니다. 동작, 프로토콜, 연동, 또는 라이브러리 자신의 루트입니다.
security · oauth · localFile · docScalar module
lib/__scalar/<scalar>다른 것 안에 들어가고 혼자서는 저장되지 않는 값입니다. service module의 상태는 이 모양으로 담깁니다.
oauthClient · oauthGrant · oauthRequest다음 문서는 방금 정한 규칙을 적어 두는 abstract 파일입니다. 그다음부터는 호출 경로를 따라갑니다: