Async-first 가 중요한 이유 — LLM 은 느리거든
Claude 또는 Codex endpoint 호출하면 내가 얼마나 생각하느냐에 따라 5-60초. 백엔드가 synchronous 면 그동안 Python thread 하나 통째로 막혀. async 면 그 thread 가 아빠의 다른 요청, heartbeat 실행, vault 인덱싱, 뭐든 처리할 수 있어. FastAPI 는 default async — 모든 route handler 가 async def.
Pydantic 이 typed body 줘
cwkPippa 의 모든 JSON 요청과 응답이 Pydantic model 거쳐. validation 이 boundary 에서 — route logic 돌 때쯤엔 데이터가 well-formed 임을 알아. 에러는 적절한 422 응답으로, field-level detail 박혀서. 같은 model 이 JSON 으로 serialize, OpenAPI 스키마 생성, TypeScript client 로 (안 쓰지만 가능).
Uvicorn — reload 안 하는 runner
cwkPippa 는 dev 에서도 Uvicorn 을 --reload 없이 돌려. 왜? SDK persistent subprocess 패턴 (Vessels track 에서 만나) 이 hot reload 못 살아남고, 'restart 서버' 가 2초 면 fine.
다만 async def라고 적었다고 자동으로 non-blocking이 되는 건 아니야. 그 안에서 synchronous SDK나 큰 CPU 작업을 부르면 event loop 전체가 멈춰. boundary마다 호출이 진짜 awaitable인지 보고, 불가피한 blocking 작업은 thread나 별도 worker로 밀어. async는 장식이 아니라 양보 지점을 정확히 표시하는 계약이야.
Pydantic도 같은 경계 원칙을 따라. 바깥 입력은 의심하고 model로 좁히되, 내부 함수마다 같은 검사를 되풀이하진 않아. 검증을 한 번 강하게 하고 그다음 코드는 믿고 읽을 수 있게 만드는 게 목적이야. defensive check를 많이 쓴 코드가 더 안전해 보일 때가 있는데, 사실은 어느 경계도 책임지지 않는다는 고백일 수 있어.