1. 목적·범위
- 목적: www
/trial이메일 필터(회사 이메일 분류·검증) 흐름에서 HTTP 404·405 및 Zuplounmatched-path로그가 날 때, 원인을 체계적으로 구분하고 우선순위대로 조치할 수 있게 한다. - 범위: 기존 아키텍처(www /trial — Zuplo·Cloudflare 통합 기획)를 전제로, 증상 → 구간 → 원인 → 조치(배포·설정) 까지. 이 문서만으로 새 코드를 작성하지 않는다.
2026-04 (OAS fragments): 게이트웨이 OpenAPI는 config/oas-manifest.json 순서의 다수 조각이다. 아래 로그·본문에 나오는 05-auth.oas.json 은 당시 관측 예시이며, 동일 유형의 $ref / components 누락 오류는 어느 Auth·trial 조각에서도 발생할 수 있다. 현재 레이아웃: OAS layout history.
2. 정상 호출 경로 (재확인)
프로덕션에서 브라우저가 호출하는 것은 동일 출처 POST https://www.pregoi.com/api/trial/email/check 이다. 이후 Cloudflare Pages Functions가 ZUPLO_API_URL 기준으로 Zuplo에 같은 경로를 서버에서 프록시하고, Zuplo가 Control Plane의 v1/trial/... 로 이어진다.
이메일 필터 “기능 오류”를 말할 때는 반드시 POST + 경로 /api/trial/email/check(또는 동계열 manual-approval) 의 응답·로그를 기준으로 한다.
3. 증상·원인 매핑 (의사결정 트리)
3.1 POST https://www.pregoi.com/api/trial/... → 405 Method Not Allowed
| 가설 | 의미 |
|---|---|
| Pages Functions가 배포에 포함되지 않음 | 해당 경로에 onRequestPost 핸들러가 없고, 정적/폴백만 있음 → POST 거부. |
조치 방향: Cloudflare Pages 빌드 산출물 옆에 functions/가 생성·업로드되는지(빌드 명령, Root directory), 배포 로그에 Functions 스킵 메시지가 없는지 확인한다.
3.2 POST .../api/trial/email/check → 404 (본문에 Zuplo problem JSON인 경우)
구간 A — Cloudflare Pages Functions → Zuplo
| 가설 | 의미 |
|---|---|
ZUPLO_API_URL 누락·오타 | 프록시가 업스트림을 호출하지 못하거나 잘못된 호스트로 호출. (환경에 따라 500에 가깝게 나오기도 함.) |
| 잘못된 Zuplo 베이스 URL | 다른 프로젝트/환경의 호스트를 가리킴. |
구간 B — Zuplo
| 가설 | 의미 |
|---|---|
배포된 Zuplo API 스펙에 /api/trial/email/check 라우트가 없음 | OpenAPI에 정의된 trial 라우트(prego-zuplo의 15-trial-access-* … 19-trial-access-* 등, oas-manifest.json)가 해당 인스턴스 빌드에 포함되지 않음. 요청은 Zuplo까지 가지만 어떤 operation에도 매칭되지 않음. |
Zuplo 관측 로그에서 Route Name: unmatched-path, route: "/(.*)" 이고, method·URL이 POST /api/trial/email/check 이면, 위 구간 B(라우트 미포함) 를 강하게 의심한다.
조치 방향: prego-zuplo에서 OAS 동기화·검증 후 해당 Zuplo 프로젝트에 재배포; Zuplo 대시보드에서 실제로 POST /api/trial/email/check operation이 보이는지 확인한다.
3.3 Zuplo 로그: GET + /favicon.ico + unmatched-path + 호스트 *.zuplo.app
이 패턴은 이메일 필터 API 오류의 원인으로 쓰이면 안 된다.
| 사실 | 설명 |
|---|---|
브라우저(또는 일부 클라이언트)는 문서를 열면 자동으로 GET /favicon.ico 를 요청한다. | |
Zuplo는 정적 사이트가 아니므로 일반적으로 favicon.ico 를 제공하지 않는다. | |
결과적으로 catch-all /(.*) → unmatched-path → 404 가 정상적으로 발생할 수 있다. |
결론: prego-main-….zuplo.app 에 대해 GET /favicon.ico 만 보고 “아직도 404”라고 하면, 이메일 필터 실패와 혼동된다. 반드시 같은 시간대의 POST /api/trial/email/check (또는 Pages 동일 출처 해당 POST) 로그를 대조한다.
3.4 POST Zuplo 직접 URL → 404 + unmatched-path
Control Plane이 아닌 Zuplo 게이트웨이에 trial 라우트가 올라와 있지 않으면 동일하게 발생한다. www·Pages 쪽을 고쳐도 해결되지 않으며, prego-zuplo 배포·환경이 맞는지 봐야 한다.
3.5 Zuplo Gateway Build 실패 — API 게이트가 통째로 갱신되지 않음 (선행 차단)
배포 로그에 아래와 같이 Gateway 빌드만 실패하고, Developer Portal(Zudoku) 빌드는 성공하는 패턴이 있다.
| 구분 | 로그에서의 의미 |
|---|---|
| Gateway Build | Building Zuplo API... 이후 실패 → 런타임에 올라가는 API 라우트 스펙이 새 커밋으로 갱신되지 않음 |
| Dev Portal | zudoku build, Deploying developer portal..., Dev Portal deployed successfully → 문서 사이트만 정상 배포될 수 있음 |
관측된 오류 (예시)
ERROR: Failed to resolve reference: /config/05-auth.oas.json#/components/parameters/PregoGatewayXTraceId| Token "components" does not exist.| File: /config/05-auth.oas.json:0:0Build failed.Failed to build API Gateway원인 분석 (기획 관점)
- (당시 예:
05-auth.oas.json; 현재는 Auth 관련 조각)paths안 여러 operation이$ref: "#/components/parameters/PregoGatewayXTraceId"를 사용한다. - 동일 파일에
components객체가 없거나,components.parameters.PregoGatewayXTraceId정의가 없으면 OpenAPI 파일 단위로 볼 때 내부 참조가 깨진다. - Zuplo Gateway 빌드는 이런 미해결
$ref에서 중단되므로, trial-access 조각 포함 전체 게이트웨이 스펙이 배포되지 않는다. - 결과적으로 런타임은 이전에 성공했던 빌드를 쓰거나, 라우트가 비어
unmatched-path로 떨어지는 현상과 연결될 수 있다 (환경·이전 배포 상태에 따라 다름).
Zuplo 포털 UI와의 대응
- Environments → Production (
main) 에서 아이콘이 부분 성공(녹/적) 으로 보이는 경우, Gateway와 Dev Portal이 서로 다른 결과일 수 있다. - GitHub 연동 배포가 “성공”처럼 보여도 Gateway 서브작업만 실패하면, 이메일 필터용 API는 갱신되지 않는다.
조치 방향 (코드 변경은 본 문서 범위 밖)
prego-zuplo저장소에서 해당 Auth(또는 공통 헤더$ref를 쓰는) OAS 조각의 OpenAPI 참조 무결성을 복구한 뒤(예:components.parameters.PregoGatewayXTraceId정의 추가, 또는 프로젝트에서 허용하는 방식으로$ref정리), 저장소 관례에 따라sync-oas→verify→ Zuplo 재배포.- Gateway 빌드가 성공한 뒤에만, 3.2·3.4 절의 “trial 라우트 미배포” 가설을 런타임에서 다시 검증하는 것이 타당하다.
참고: Dev Portal 로그에 Wrote ... merged.oas.json | paths: 93 등이 찍히는 것은 문서용 병합이며, Gateway 빌드 실패를 보상하지 않는다. 두 파이프라인을 혼동하지 말 것.
4. 원인 요약 표 (이메일 필터 관점)
| 관측 | 1차 원인 후보 | 주 레포/설정 |
|---|---|---|
| www에서 POST 405 | Pages Functions 미포함 | Prego Pages 빌드·functions/ |
| www에서 POST 404 (Zuplo body) | Zuplo에 trial 라우트 없음 또는 잘못된 ZUPLO_API_URL | prego-zuplo 배포, Pages ZUPLO_API_URL |
Zuplo 로그 GET /favicon.ico 404 | API 버그 아님 (브라우저 기본 요청) | 무시하거나 Zuplo/별도 호스팅에서 파비콘은 별도 논의 |
Zuplo 로그 POST /api/trial/... unmatched-path | Trial OAS 미배포·다른 프로젝트 URL | prego-zuplo trial-access 조각(15-trial-access-* …), 배포 타깃 인스턴스 |
Zuplo Gateway Build 로그: 05-auth.oas.json(또는 유사) + PregoGatewayXTraceId + Token "components" does not exist | OAS 내부 $ref 깨짐 → Gateway 전체 배포 실패 | prego-zuplo 해당 Auth 조각·01-prego-gateway-parameters 공통 헤더 패턴 |
| Dev Portal만 성공 / Gateway 실패 | 문서 사이트는 최신, API 라우트는 구버전 또는 미갱신 | Zuplo 빌드 로그에서 두 단계를 분리해 확인 |
5. 대응 절차 (기획 단계 체크리스트)
순서를 지키면 원인 추적 비용이 줄어든다.
- 증상 고정: 실패한 요청이
POST인지, 경로가/api/trial/email/check인지, 응답 본문이 Zuplo problem 인지 Pages HTML 인지 기록한다. - 노이즈 제거: Zuplo 로그에서
GET /favicon.ico,GET /, 정적 리소스만 있는 항목은 이메일 필터 실패 증거에서 제외한다. - Zuplo 배포 파이프라인: 동일 배포에서 Gateway Build가 성공했는지 먼저 확인한다.
Failed to build API Gateway가 있으면 3.5 를 처리하기 전에는 런타임 trial 동작을 신뢰할 수 없다. Dev Portal만 성공한 것으로 착각하지 않는다. - www 구간:
POST /api/trial/...가 405면 Pages Functions·빌드 설정을 본다. - Zuplo 구간: Gateway 빌드가 성공한 전제에서, 동일 POST가 404이고 Zuplo가
unmatched-path이면 trial 라우트가 그 인스턴스에 포함됐는지를 본다 (prego-zuplo→ 해당 Zuplo 프로젝트). - 환경 일치:
ZUPLO_API_URL/ 대시보드에 등록한 호스트가 로그를 보고 있는 Zuplo 인스턴스와 동일한지 확인한다 (호스트명이a8d4dfe.zuplo.app과bb45748.d2.zuplo.dev등으로 바뀌면 혼선이 크다).
6. 관련 문서
- www /trial — Zuplo·Cloudflare 통합 기획 — 시퀀스·저장소 매핑
- 공개 API 추가 순서: How to add an API
- Zuplo 운영: prego-zuplo runbook (조직/포크 URL에 맞게 조정)
7. 이 문서의 한계
- 코드 수정·PR은 포함하지 않는다. 조치가 “배포·환경·라우트 포함 여부·OAS 참조 복구”에 집중된다.
- 파비콘 404를 없애려면 www 정적 자산이나 Zuplo 외부 CDN 등 별도 UX/브랜딩 결정이 필요하며, 이메일 필터 신뢰성과는 분리해 논의하는 것이 좋다.
- Auth 관련 OAS 조각(구 단일
05-auth병기)의components누락은 구체적인 수정 패치가prego-zuplo쪽에서 이루어져야 하며, 본 기획서는 증상·원인·우선순위만 정리한다.