"이 퀘스트는 cwkPippa를 실제로 동작하는 사례로 활용했다. 이제 운영 코드를 직접 따라가며 앞서 배운 개념이 어떻게 구현되었는지 확인할 차례다. 어떤 부분은 교과서적인 원칙을 따르고, 어떤 부분은 현실적인 요구에 맞게 조정되어 있다."
저장소에서 살펴볼 위치
cwkPippa/backend/routes/— FastAPI router가 모여 있다. 각 파일은 대체로 chat, conversations, folders, Council, artifacts, 관리자 기능처럼 하나의 리소스 계열을 담당한다. 총 412개의 @router decorator에서 여러 HTTP 메서드를 확인할 수 있다.cwkPippa/backend/adapters/— SSE 스트림을 생성하는 adapter가 있다. 대표적인 파일은claude.py이며, 그 안의stream메서드가 Starlette의 StreamingResponse를 통해 이벤트를 하나씩 전달한다.cwkPippa/backend/main.py— FastAPI 앱 구성, 엄격한 CORS 허용 목록, router 마운트 순서를 정의한다.cwkPippa/backend/store/— SQLite와 JSONL 영속성 계층이 있으며 복구 로직도 이곳에 있다.cwkPippa/frontend/src/lib/api.ts— 클라이언트 API 계층이다. cwkPippa의 엔드포인트를 React에서 타입이 지정된 wrapper로 호출한다.
각 트랙을 실제 엔드포인트에 연결하기
퀘스트의 각 트랙은 다음과 같은 cwkPippa 엔드포인트에서 구체적으로 확인할 수 있다.
- 기초 (트랙 1) —
GET /api/health: 가장 단순한 형태의 엔드포인트다. 상태 코드 200과 JSON 본문을 반환하며 인증이 필요 없다. HTTP 요청의 기본 구조를 살펴보기에 적합하다. - 의미론 (트랙 2) —
PUT /api/conversations/{id}/title: 멱등성을 갖는 이름 변경 요청이다. 같은 title을 두 번 보내도 최종 상태는 같다. - REST 설계 (트랙 3) —
POST /api/conversations: 서버가 UUID를 할당하고 201과 Location을 반환한다. - 인증과 보안 (트랙 4) — 모든 엔드포인트에 CORS 허용 목록(main.py의
_allowed_origins)을 적용하고, PIN으로 발급한 세션의 Bearer 토큰을 사용하며, 클라이언트에는 API 키를 두지 않는다. - 캐시와 성능 (트랙 5) — Vite로 빌드한 정적 자산에는
public, max-age=31536000, immutable이 적용되고, API 엔드포인트는 기본적으로no-store를 사용한다. 사용자별 데이터는 공유 캐시에 저장하기 어렵기 때문이다. - 스트리밍과 비동기 처리 (트랙 6) —
POST /api/chat은 SSE를 생성한다.POST /api/council/{id}/finalize는 202와 Location을 반환하는 비동기 polling 패턴을 사용한다. - 운영 (트랙 7) —
backend/main.py에는 request ID 미들웨어가 있고, FastAPI는/docs에서 OpenAPI 문서를 자동으로 제공한다. 백엔드의 복구 계층은 제한적인 형태의 멱등성도 제공한다.
cwkPippa가 현실에 맞게 조정한 부분
운영 코드베이스가 언제나 교과서와 완전히 일치하는 것은 아니다. cwkPippa에는 다음과 같은 의도적인 절충이 있다.
- 대부분의 엔드포인트에 ETag가 없음 — 복구 계층이 요청마다 JSON을 다시 구성하므로 바이트 수준에서 안정적인 해시를 만들려면 추가 작업이 필요하다. 실제 캐시 miss 비용이 커질 때 도입할 수 있다.
- 공식 버전 관리가 없음 — 단일 클라이언트와 백엔드를 함께 배포하므로
/v1/접두사가 아직 필요하지 않다. - FastAPI의 기본 구조화된 오류 응답인
{detail: ...}을 그대로 사용한다.{error: {code, message, request_id}}같은 별도 형식을 만들지 않은 이유는 프런트엔드가detail만으로 충분하기 때문이다. - 동작 엔드포인트(
POST /api/council/{id}/finalize) — 순수한 리소스 중심 설계에서 벗어난 혼합형 패턴이다. 작업을 자연스럽게 나타낼 명사형 리소스가 없기 때문에 의도적으로 선택했다.
이러한 절충에는 CLAUDE.md나 기능별 주석으로 근거를 남겨 두었다. 이유를 문서화한 실용주의가 운영 REST API의 현실적인 모습이다.
교과서는 규칙을 가르치고, 운영은 규칙을 언제 조정하며 그 근거를 어떻게 기록해야 하는지 가르친다. cwkPippa를 실제 사례로 읽으면 리소스 URI, 의미론에 맞는 메서드, 상태 코드 같은 원칙과 ETag 생략, 혼합형 동작 엔드포인트, 연기된 버전 관리 같은 현실적인 선택을 함께 볼 수 있다. 원칙에서 벗어난 지점마다 이유가 있고, 그 이유가 문서화되어 있다.
권장 읽기 순서
REST 학습 관점에서 cwkPippa를 처음 살펴볼 때는 다음 순서가 적합하다.
backend/main.py로 시작 — 앱 구성, CORS, 미들웨어를 약 5분 동안 살펴본다.- 다음은
backend/routes/health.py다. 파일이 없다면 가장 단순한 router를 선택한다. — 기본적인 GET과 FastAPI decorator 패턴을 확인한다. - 다음은
backend/routes/chat.py— SSE 응답을 반환하는 POST와 스트리밍 패턴을 살펴본다. - 다음은
backend/routes/conversations.py— 복구 로직을 포함한 전체 CRUD 흐름을 살펴본다. - 다음은
backend/routes/council/routes.py— finalize 작업에 사용하는 202와 Location 기반 비동기 패턴을 살펴본다. - 마지막은
frontend/src/lib/api.ts— 클라이언트에서 각 엔드포인트를 호출하는 방식을 확인한다.
Pippa의 설계 회고
Pippa가 REST를 떠올릴 때 cwkPippa가 먼저 연상되는 이유는 이 코드베이스를 오랫동안 다루며 설계 선택을 반복해서 확인했기 때문이다. 처음부터 끝까지 잘 설계된 REST API를 하나 완성하면 그 패턴은 이후 설계 판단의 기준이 된다. 교과서는 어휘를 제공하고, 실제 코드베이스는 그 어휘를 실무 감각으로 바꿔 준다.