Skip to content

[Template+Project] 템플릿 카탈로그 API + templateType 실재 검증 [ #318 ] - #320

Merged
dldnsgkr merged 1 commit into
developfrom
unhak/template-catalog-api
Sep 10, 2026
Merged

[Template+Project] 템플릿 카탈로그 API + templateType 실재 검증 [ #318 ]#320
dldnsgkr merged 1 commit into
developfrom
unhak/template-catalog-api

Conversation

@dldnsgkr

Copy link
Copy Markdown
Collaborator

템플릿을 골라도 조용히 무시된다. startMode/templateType 은 검증·저장되기만 하고 읽는 코드가 없었다. 검증도 슬러그 형식만 봐서 존재하지 않는 템플릿이 200 으로 통과했고, 고른 템플릿은 무시된 채 백지 생성됐다. 사용자가 선택한 것이 아무 데도 반영되지 않는데 성공으로 보이는 형태다.

#318 PR-3. 설계는 docs/template-architecture-design.md(#319), 템플릿 저장소는 Dvely/qeploy-templates.

무엇

GET /api/v1/templates, /{id} 카탈로그 서빙. 인증 불필요 — 내용이 이미 공개 Pages 에 있는 정적 목록이고 사용자 데이터가 없다
프로젝트 생성 정규화된 templateType 을 카탈로그와 대조. 없으면 400
카탈로그 취득 10분 주기. 실패 시 직전 목록 유지(stale-while-error)

카탈로그 정본은 이 서버가 아니다. 템플릿 저장소가 발행하는 catalog.json 을 읽어 나를 뿐 소스를 들지 않는다 — jar 가 이미 158MB 고, 템플릿 하나 고치자고 백엔드를 재배포하는 구조를 만들지 않기 위해서다.

sourceUrl(씨앗 tarball)은 응답에 담지 않는다. 서버가 컨테이너에 풀 때만 쓰는 내부 경로다.

판단이 갈릴 만한 곳

검증이 도메인이 아니라 애플리케이션 계층에 있다. 정규화 이후에 물어야 하기 때문이다 — "E Commerce"e-commerce 로 정규화된 뒤 조회돼야 카탈로그와 맞는다. 형식 정규화는 도메인이 끝내고, 네트워크가 필요한 실재 확인만 위로 올렸다. 테스트가 이 순서를 붙잡는다(정규화 전 값으로 물으면 스텁이 비어 실패한다).

갱신 실패 시 낡은 목록으로 계속 응답한다. 카탈로그는 정적 문서라 잠깐 낡은 목록이 프로젝트 생성을 막는 것보다 낫다. 다만 WARN 을 남긴다 — 조용히 넘기면 며칠째 갱신이 안 되는 것을 아무도 모른다. 503한 번도 읽지 못한 경우에만 나간다. 그때 통과시키면 없는 템플릿이 저장되고 씨딩 시점에 가서야 깨진다.

아직 안 되는 것

씨딩이 없다. 카탈로그에 있는 ID 만 통과할 뿐, 통과한 뒤의 동작은 여전히 백지 생성이다. tarball 을 /workspace/app 에 푸는 경로는 PR-4 다. state.md §4.22 에 이 상태 그대로 적어뒀다.

검증

  • 전체 테스트 1342건 통과
  • 신규: 카탈로그 파싱·캐시·stale-while-error·최초 실패·빈 카탈로그 거절 / guard 3종 / 생성 시 미등록 템플릿 거절
  • 마이그레이션 없음

문서(api.md · connection.md · state.md)에 실제와 어긋나 있던 서술도 고쳤다 — "생성 직후 CODE task 제출"은 그 경로가 제거된 뒤로 사실이 아니었다.

🤖 Generated with Claude Code

https://claude.ai/code/session_013y8USoCXTsRTATAhy88M93

템플릿을 골라도 조용히 무시되던 것을 끊는다.

지금까지 startMode/templateType 은 검증·저장되기만 하고 읽는 코드가 없었다. 검증도
슬러그 형식만 봐서 존재하지 않는 템플릿이 200 으로 통과했고, 고른 템플릿은 무시된 채
백지 생성됐다. 사용자가 선택한 것이 아무 데도 반영되지 않는데 성공으로 보이는 형태다.

카탈로그 정본은 이 서버가 아니다. 템플릿 저장소가 Pages 로 발행하는 catalog.json 을
읽어 나를 뿐, 소스를 들지 않는다 — jar 가 이미 158MB 고, 템플릿 하나 고치자고 백엔드를
재배포하는 구조를 만들지 않기 위해서다.

- GET /api/v1/templates, /{id} — 인증 불필요. 내용이 이미 공개 Pages 에 있는 정적
  목록이고 사용자 데이터가 없다. 로그인 전 갤러리를 띄울 수 있게 열어둔다
- sourceUrl(씨앗 tarball)은 응답에 담지 않는다. 서버가 컨테이너에 풀 때만 쓰는 내부 경로다
- 프로젝트 생성 시 정규화된 templateType 을 카탈로그와 대조한다. 없으면 400

정규화 이후에 묻는 것이 요점이다. "E Commerce" 는 e-commerce 로 정규화된 뒤 조회돼야
하고, 그래서 검증이 도메인이 아니라 애플리케이션 계층에 있다 — 형식 정규화는 도메인이
끝내고, 네트워크가 필요한 실재 확인만 위로 올렸다.

갱신에 실패하면 직전 목록으로 계속 응답한다(stale-while-error). 카탈로그는 정적 문서라
잠깐 낡은 목록이 기능을 멈추는 것보다 낫다. 다만 낡은 것을 쓰는 사실은 WARN 으로 남긴다
— 조용히 넘기면 며칠째 갱신이 안 되는 것을 아무도 모른다. 503 은 한 번도 읽지 못한
경우에만 나간다. 그때 통과시키면 없는 템플릿이 저장되고 씨딩 시점에 가서야 깨진다.

카탈로그 전용 RestClient 를 설정 빈으로 분리했다. 타임아웃이 없으면 카탈로그가 멈출 때
프로젝트 생성이 함께 매달린다. 클라이언트가 빌더가 아니라 완성품을 받는 이유는 테스트다
— 빌더를 받으면 MockRestServiceServer 가 심은 요청 팩토리를 설정이 덮어쓴다.

문서(api.md · connection.md · state.md)에 실제와 어긋나 있던 서술도 함께 고쳤다.
"생성 직후 CODE task 제출"은 그 경로가 제거된 뒤로 사실이 아니었다.

테스트 1342건 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013y8USoCXTsRTATAhy88M93
@dldnsgkr
dldnsgkr merged commit d9223f0 into develop Sep 10, 2026
1 check passed
@dldnsgkr
dldnsgkr deleted the unhak/template-catalog-api branch September 10, 2026 03:28
dldnsgkr added a commit that referenced this pull request Sep 10, 2026
카탈로그 검증(#320)까지는 "없는 템플릿을 거절" 하는 데까지였고, 통과한 뒤의 동작은
여전히 백지 생성이었다. 이 커밋이 그 마지막 조각이다.

첫 CODE 스텝에서 씨앗 tarball 을 /workspace/app 에 푼다. 그러면 CODE 프롬프트가
"만들어라" 에서 "고쳐라" 로 바뀌고, 그 수정분이 diff API(#317)로 그대로 보인다.

순서가 중요하다. 저장소 clone 이 씨딩보다 먼저다 — 반대면 clone 이 씨앗을 덮거나
비어 있지 않은 디렉터리에 clone 하려다 실패한다. 씨딩은 작업 디렉터리가 비었을
때만 일어나므로, 저장소가 있는 프로젝트나 두 번째 요청에서는 자연히 건너뛴다.
덮는 순간 사용자 작업물이 사라지는 자리라 조건을 파일 존재 여부로 잡았다.

실패하면 조용히 넘어가지 않는다. 씨딩에 실패했는데 그냥 진행하면 사용자가 고른
템플릿과 전혀 다른 결과물이 "성공" 으로 나온다 — 고치려던 그 버그를 형태만 바꿔
재현하는 셈이다. tar 종료코드만 믿지 않고 푼 결과도 확인한다(빈 입력에 0 이 나올 수
있다). "성공했는데 아무것도 없는" 상태가 가장 나쁘다.

지시문 앞에 템플릿 맥락을 붙인다. 시스템 프롬프트가 "프로젝트가 없으면 스캐폴드" 로
시작하므로 모델이 알아서 수정 경로를 타는 것이 정상이지만, 그 판단을 추측에 맡기지
않는다 — 스캐폴더가 한 번이라도 돌면 고른 템플릿이 통째로 덮인다. contentHints 를
함께 넘기는 것도 같은 이유다. 어디가 내용이고 어디가 구조인지 모델이 스스로 판단하면
"내용만 바꾸기" 요청에도 레이아웃을 건드린다.

sourceUrl 은 셸 명령에 들어간다. 카탈로그는 우리 저장소가 발행하지만 거기 실린 값을
그대로 셸에 넘기는 구조 자체를 두지 않는다 — 형식을 먼저 검증한다.

컨테이너 이미지(node:20-alpine)에 wget·tar 가 있고 https 로 받아 풀리는 것,
프리뷰 네트워크가 외부 통신 가능한 bridge 라는 것은 dev 서버에서 실측했다.

테스트 1350건 통과.


Claude-Session: https://claude.ai/code/session_013y8USoCXTsRTATAhy88M93

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant