시스템 아키텍처 — 3-서비스 설계 확정
⚠︎ 지난 판 — 2026-07-22. 현재 판은 여기다. 이 문서는 그때의 기록이고 고치지 않는다.
ⓘ 이 판은 사이트 버저닝을 보이려고 채운 자리다. 그날 실제로 쓰인 문서가 아니고, 앞뒤 판과 그날의 결정 기록에서 끌어와 재구성했다. 지우거나 진짜 기록으로 갈아끼워도 된다.
갱신일 2026-07-22 · 기준:
2026-07-21-heymoa-service-contracts-design.md+ 그날의 결정 둘 이 문서가 갖는 것: 서비스를 왜 셋으로 갈랐고, 무엇이 무엇과 어떤 프로토콜로 붙는가. 아직 구현 실측이 아니다 — 계약과 결정만 있고 돌아가는 코드로 확인한 것은 다음 판부터다.
확인한 사실
서비스 셋
| 서비스 | 무엇을 진다 | 왜 따로 |
|---|---|---|
| web | 화면, 마이크 캡처 | 브라우저 능력이 필요하고 서버와 수명주기가 다르다 |
| server | 인증·권한·도메인 상태·외부 OAuth | public edge를 혼자 진다. 감사와 잠금이 한 곳에 모여야 한다 |
| ai | LLM 실행, 전사 분석 | 초 단위로 느리고 실패율이 다르다. 같은 프로세스면 요청 스레드를 묶는다 |
그날 정해진 것 둘
| 결정 | 무엇 | 원장 |
|---|---|---|
| 분석 실행 엔진 | 브로커를 두지 않고 in-process 큐 + LangGraph fan-out으로 돌린다. 잡 상태는 DB row가 갖는다 | ADR 0001 |
| 관측 | LLM 실행 추적은 Langfuse. 키가 있을 때만 붙는다 | ADR 0002 |
비동기를 어떻게 끊는가
분석은 동기로 기다리지 않는다. server가 ai에 던지면 ai는 202로 즉시 끊고, 끝나면 X-Internal-Token을 달고 server의 내부 API로 callback 한다. 채팅은 반대로 SSE를 변환 없이 통과시킨다 — server가 이벤트를 재해석하지 않으므로 web 구간과 ai 구간의 포맷이 같고, 이벤트 정의가 한 곳에만 산다.
스키마는 contracts/가 정본이다.
설계 전제
- server가 유일한 public edge다. 그래서 ai는 인증도 CORS도 구현하지 않는다. ai를 외부에 노출하는 순간 이 전제가 깨지고, ai에 인증 계층을 먼저 만들어야 한다.
- 브로커를 도입하지 않는다. 비동기는 DB row와 HTTP callback으로 충분하다. Kafka·SQS는 병목이 실측될 때 꺼낸다.
- DB를 둘로 가른다. 파생 데이터가 원본과 같은 database에 있으면 "지워도 되는 것"을 구분할 수 없다. cross-database 쿼리는 금지다.
미확인
| 무엇 | 지금 아는 것 |
|---|---|
| server → ai 구간의 인증 | 계약이 미결로 등재했다. 네트워크로 막을지 토큰을 붙일지 정하지 않았다 |
| 운영 배포 형상 | 어디에 어떻게 띄울지 아직 아무것도 없다 |
| 분석 완료를 web이 아는 경로 | 설계 스펙은 STOMP push를 적었다. 구현이 그렇게 될지는 모른다 |