배경
prod 의 admin 페이지에서 raw_post reparse / source trigger 버튼이 작동 안 함:
```
Reprocess failed: AI_SERVER_URL is not configured
```
원인: web (Vercel) 의 server route 가 ai-server 의 내부 HTTP (port 10000) 를 직접 호출하는 구조. ai-server 는 internal docker network 에만 노출돼있어 Vercel 에서 도달 불가. dev (web/ai-server 같은 localhost) 에서만 작동했던 dead-on-prod 패턴.
추가 발견 — 현재 ai-server 가 두 wire protocol 동시 노출:
- gRPC :50051 — `AnalyzeImage`, `ProcessPostEditorial`, `ExtractPostContext` 등 (api-server 가 호출)
- HTTP :10000 — `/raw-posts/sources/{id}/trigger`, `/raw-posts/items/{id}/reparse` (web 이 호출 시도)
이 두 plane 분리가 design doc 에 명시된 적 없음. 우연한 inconsistency.
목표
ai-server 의 모든 외부 진입점 을 gRPC 로 통일. 새 흐름:
```
admin UI → web /api/admin/.../trigger → api-server /api/v1/admin/.../trigger → ai-server gRPC TriggerSource
```
- web 은 api-server 만 호출 (`API_BASE_URL`)
- api-server 가 ai-server 의 유일한 client (gRPC `DecodedAIGrpcClient`)
- ai-server 의 FastAPI HTTP API 는 deprecated — 후속 PR 에서 제거
효과
- ai-server 가 어떻게 노출되는지 외부 dependency 없음 (docker network 격리만으로 충분)
- wire format 일관성 — proto = single source of truth
- dev/prod 차이 사라짐 (현재는 dev 만 작동)
- admin 의 reparse/trigger 가 prod 에서도 작동
작업 분해
-
proto (`packages/ai-server/src/grpc/proto/inbound/inbound.proto`)
- `rpc TriggerSource(TriggerSourceRequest) returns (TriggerSourceResponse)`
- `rpc ReparseRawPost(ReparseRawPostRequest) returns (ReparseRawPostResponse)`
- 양쪽 codegen 재생성
-
ai-server
- 새 gRPC servicer 메서드 — 기존 HTTP handler (`raw_posts_controller.py`) 의 로직 service 레이어로 옮기고 양쪽이 공유
- HTTP route 는 유지 (deprecated 마크), 후속 PR 에서 제거
-
api-server
- `DecodedAIGrpcClient` (`packages/api-server/src/services/ai/`) 에 두 메서드 추가
- `domains/admin` 또는 `domains/raw_posts` 에 thin proxy handler 두 개:
- POST `/api/v1/admin/raw-post-sources/{id}/trigger`
- POST `/api/v1/admin/raw-posts/{id}/reparse`
- 인증: 기존 admin middleware 재사용
-
web
- `packages/web/app/api/admin/raw-post-sources/[id]/trigger/route.ts` → `API_BASE_URL` 호출
- `packages/web/app/api/admin/raw-posts/items/[id]/reparse/route.ts` → 동일
- `AI_SERVER_URL` 의존 제거 (`server-env.ts` 에서 export 빼도 OK)
Verification
Out of scope (별도 PR)
- ai-server 의 HTTP API (`raw_posts_controller.py`) 완전 제거
- ai-server 의 `port 10000` 자체 비활성화
- ADR / architecture doc 작성 ("ai-server 는 gRPC only")
변경 라벨
backend (Rust + Python) + frontend (TypeScript) 양쪽. `bump:patch` (런타임 wire 변경 — minor 도 고려 가능).
배경
prod 의 admin 페이지에서 raw_post reparse / source trigger 버튼이 작동 안 함:
```
Reprocess failed: AI_SERVER_URL is not configured
```
원인: web (Vercel) 의 server route 가 ai-server 의 내부 HTTP (port 10000) 를 직접 호출하는 구조. ai-server 는 internal docker network 에만 노출돼있어 Vercel 에서 도달 불가. dev (web/ai-server 같은 localhost) 에서만 작동했던 dead-on-prod 패턴.
추가 발견 — 현재 ai-server 가 두 wire protocol 동시 노출:
이 두 plane 분리가 design doc 에 명시된 적 없음. 우연한 inconsistency.
목표
ai-server 의 모든 외부 진입점 을 gRPC 로 통일. 새 흐름:
```
admin UI → web /api/admin/.../trigger → api-server /api/v1/admin/.../trigger → ai-server gRPC TriggerSource
```
효과
작업 분해
proto (`packages/ai-server/src/grpc/proto/inbound/inbound.proto`)
ai-server
api-server
web
Verification
Out of scope (별도 PR)
변경 라벨
backend (Rust + Python) + frontend (TypeScript) 양쪽. `bump:patch` (런타임 wire 변경 — minor 도 고려 가능).