Skip to content

0. 구현 상태 (요약)

다음은 2026-04 기준 코드베이스와의 정합 요약이다. 세부 계약은 prego-zuplo OpenAPI와 prego-control-plane 핸들러를 우선한다.

영역내용
prego-control-planeD1 trial_runtime_free_domains(마이그레이션 0026), 정적 분류 후 런타임 사전, 선택적 Abstract phase-2, reasonCode / isCompanyEmail / outcome / pipeline, 내부 CRUD·trial-signup-attempts 로그·external preview, 관리자 cp-trial-email-filter.
prego-zuplo공개 POST /api/trial/email/check 응답 스키마와 문서 정합.
Prego apps/wwwtrial-gateway, TrialEmailGate에서 isCompanyEmail·reasonCode 기반 카피.
운영D1 마이그레이션 적용, 선택 시 ABSTRACT_EMAIL_VALIDATION_API_KEY, CP·Zuplo 배포.

운영 적용: prego-control-plane README Setup 단계 4–5와 동일 — D1에 0026_trial_runtime_free_domains 포함해 npm run db:migrate (원격) / npm run db:migrate:local (로컬), 선택 시 ABSTRACT_EMAIL_VALIDATION_API_KEY, 배포는 npm run deploy 또는 CI. Zuplo는 해당 리포지토리 배포 파이프라인을 따른다.

기획서 본문의 남은 항목: /trial 카드 레이아웃·m.pregoi.com preset·정책 문구 등은 제품 일정에 따라 별도 적용.


1. 배경·문제 정의

1.1 관측 증상

  • 공용 이메일 예: kinybaik@gmail.com 입력 시, 기대에 맞는 차단/안내 흐름이 보인다.
  • 회사 도메인 이메일 예: marco@gokiri.com 입력 시에도 공용 메일과 동일한 메시지·톤으로 보인다.
  • Zuplo 로그상 POST /api/trial/email/checkHTTP 200이며, 라우트는 정상 매칭된다.

1.2 문제를 두 층으로 나눔

설명
A. 제품/카피API가 분류 결과를 구분해 주더라도, 프론트가 같은 문구만 쓰면 사용자에게는 “똑같다”로 느껴진다.
B. 분류 정확도gokiri.com이 실제로는 회사 도메인인데, 서버 분류가 free_public / disposable / unknown_needs_review 등으로 떨어져 의도와 다른 정책이 적용되는 경우.

기획·구현 시 A만으로는 B를 가릴 수 없고, B만 고치면 여전히 A 때문에 UX가 동일할 수 있다. 양쪽을 분리해 검증한다.

1.3 목표 플로우 (제품 요구)

  • 회사 메일로 검증이 완료된 경우(API 상으로 자동 트라이얼 허용·또는 기획에서 정의하는 “회사로 확정” 상태), 사용자는 다음 단계로 아래 URL로 이동하여 이메일 인증(verify) 절차를 시작해야 한다.
    • 랜딩 URL(예시): https://m.pregoi.com/?plan=free&region=sg — 쿼리에 plan=free리전(예: region=sg) 을 포함한다. 리전 값은 제품/배포 정책에 맞게 정한다.
  • www/trial(또는 동일 출처 이메일 입력 UX)은 게이트 역할로 남고, 실제 인증·프리 플랜 온보딩m.pregoi.com(Tenant Onboarding 등) 쪽 플로우와 연결된다.
  • m.pregoi.com 해당 진입 화면에서의 UX (필수):
    • 이메일 주소는 preset(고정 표시) 되어, 사용자가 input 필드를 통해 이메일을 변경할 수 없다(읽기 전용·비활성·마스킹 등 구현 형태는 앱에서 결정).
    • 사용자는 Send verification code(또는 동일 의미의 CTA)만으로 다음 단계(인증 코드 수신·입력)를 진행한다.
    • 목적: www에서 이미 확정한 회사 메일과 동일 주소로만 온보딩·OTP를 이어가게 하고, 다른 주소로 바꿔 우회하는 것을 방지한다.
  • 구현 시 확인할 점: 이동 URL 쿼리(plan, region 등), preset 이메일 전달 방식(쿼리 email·해시·세션·짧은 토큰 등 — 길이·노출·PII는 보안 검토), trace 연계, CORS/리다이렉트tenant·게이트웨이 경계와 맞는지.

2. 현행 동작 (코드베이스 기준 요약)

  • 경로: 브라우저 → (동일 출처) POST /api/trial/email/check → Zuplo → prego-control-plane POST /v1/trial/email/check.
  • 응답 조립: Control Plane의 buildCheckResponse는 대략 다음과 같다.
    • classification === 'business' 이면서 allowedForAutoTrial === true 인 경우에만 수락 메시지 분기.
    • 그 외 모든 분류(free_public, disposable, invalid, unknown_needs_review 등)는 동일한 차단 카피(TRIAL_BLOCKED_COPY)와 showManualApproval: true 를 쓴다.

따라서 gmailgokiri.com이 둘 다 “자동 트라이얼 불가”이면, API 본문의 message는 구조상 동일할 수 있다. 이는 버그라기보다 현재 제품 규칙에 가깝다.


3. 원인 가설 (우선순위 순)

3.1 가설 1 — 의도된 동일 카피 (층 A)

  • 두 이메일 모두 allowedForAutoTrial === false 이면 항상 같은 message.
  • 검증: 네트워크 탭 또는 POST /internal/trial-email-check-preview(운영자·Bearer)로 원시 JSON을 비교한다.
    • classification, reasonCode, domain, matchedSources / preview 필드가 서로 다른지 확인.

3.2 가설 2 — gokiri.com 분류가 회사가 아님 (층 B)

  • 정책: 알 수 없는 도메인은 unknown_needs_review, 자동 트라이얼 불가.
  • 오분류: 생성 블록리스트·커레이션 리스트에 gokiri.com무료/일회성으로 잘못 포함된 경우.
  • 검증: Preview API 또는 로그로 reasonCode, matchedSources 확인. 필요 시 도메인 오버라이드(D1 domain_overrides) 로 임시 허용해 A/B 비교.

3.3 가설 3 — 프론트가 구분 필드를 무시

  • API가 classification 등을 내려주어도 UI가 message만 표시하면 체감은 동일.
  • 검증: apps/www trial 관련 컴포넌트에서 어떤 필드로 문구를 고르는지 코드 리뷰.

4. 조사 체크리스트 (구현 전 필수)

  1. 동일 시각·동일 환경에서 두 이메일의 API JSON 전문 저장 (classification, domain, allowedForAutoTrial, reasonCode, 있으면 matchedSources).
  2. 운영자용 Preview 엔드포인트로 동일 도메인 재현 시 mxCheck 등 부가 필드 확인 (이미 구현된 경우).
  3. gokiri.com블록리스트/생성 리스트에 있는지 저장소에서 검색 (prego-control-plane email-domain-classifier, data/email-domains/).
  4. 필요 시 D1 domain_overrides 에서 해당 도메인 수동 허용 후 재테스트(운영 절차 준수).

5. 해결 방향 (택·조합)

5.1 제품 카피·UX (층 A)

  • 분류별 메시지 분기: 예) free_public / disposable → “개인·무료 메일은 사용할 수 없습니다”; unknown_needs_review → “도메인 확인이 필요합니다. 수동 승인을 요청할 수 있습니다” 등.
  • 동일 출처 API 응답userFacingReason 같은 안정적인 enum을 두어 i18n과 매핑 (OpenAPI·Zuplo·CP 순 변경 순서 준수).

5.1b 회사 메일 검증 성공 → 다음 단계 (필수)

  • 조건: POST /api/trial/email/check(및 CP v1) 응답이 회사 메일로 검증됨을 나타낼 때(현행 스키마에서는 보통 allowedForAutoTrial === true 및 수락 분기와 정합).
  • 행동: 사용자를 https://m.pregoi.com/?plan=free&region=sg 형태( plan=free, region= — 실제 값은 환경에 맞게)로 안내하거나 클라이언트 네비게이션하여, 이메일 인증 플로우를 시작한다.
  • m.pregoi.com 온보딩 페이지 동작 (필수):
    • 이메일은 preset — www에서 통과한 주소가 화면에 반영되며, 사용자는 해당 필드로 이메일을 변경할 수 없다.
    • 진행은 Send verification code 버튼(또는 동일 역할 CTA)만으로 이어진다(코드 입력·검증 단계는 기존 온보딩 규칙 따름).
  • 크로스 앱: 변경 범위는 www의 CTA·리다이렉트·쿼리 조립, admin-web(apps/admin-web 등) / m.pregoi.com 온보딩 화면의 이메일 필드 편집 가능 여부·preset 소스. prego-zuplo는 공개 계약이 바뀌면 OpenAPI 동기화.
  • 수동 승인 경로: 회사로 보이지 않아 수동 승인만 가능한 경우, 기획상 동일 진입 URL·동일 preset 규칙인지·별도 딥링크인지 구분해 두면 구현 충돌을 줄인다.

5.2 분류 정확도 (층 B)

  • 오탐 도메인: 리스트/생성 파이프라인에서 제외 또는 denylist 정정.
  • 알 수 없는 도메인: MX/DNS 기반 보조(비용·지연·오탐 리스크 기획에 명시). 기존 mxCheckskipped인 정책과 충돌하지 않게 정의.
  • 운영: domain_overrides allowlist로 긴급 완화 + 장기적으로 리스트/알고리즘 수정.

5.3 계약·게이트웨이

  • 공개 응답 스키마 변경 시: prego-zuplo OpenAPI → 라우트 → CP → www 순 (How to add an API 및 플랫폼 허브).

5.4 www /trial — 레이아웃·카드 UI (m.pregoi.com 스타일 정렬)

대상 URL: https://www.pregoi.com/trialStart your free trial 페이지.

목표: 상단 글로벌/메인 메뉴 아래본문(콘텐츠) 영역을, m.pregoi.com 온보딩에 쓰이는 카드형 UI(참고: Account Setup 류 — 흰 배경 카드, 라운드 코너, 내부 패딩, 라벨+이메일 필드, 초록색 Send verification code pill CTA, Or continue with 구분선, Singpass / Google 등 보조 버튼)과 동일한 시각 언어·박스 스케일로 맞춘다.

항목요구
카드 컨테이너본문 콘텐츠를 단일 카드 박스 안에 배치(테두리·라운드·배경·내부 여백은 m.pregoi.com 참조 화면과 동일 계열).
width / height카드(및 그 안의 폼 열)의 가로·세로 느낌첨부 기준 이미지(m.pregoi.com Account Setup 카드)같은 크기·비율이 되도록 한다. 구현 시에는 참조 페이지에서 computed width / min-height / max-width(예: 카드 최대 너비 약 380–450px 대역 등)를 측정해 토큰·픽셀 값을 고정하고, www에 동일하게 적용한다.
수직 정렬뷰포트 높이를 기준으로, 헤더(메인 메뉴) 아래 남은 영역에서 카드 블록이 PC·모바일 모두 세로 방향 중앙(vertically middle) 에 오도록 한다. 일반적으로 min-height100dvh(또는 100vh)에서 헤더·여백을 뺀 값으로 잡고, flex/grid + items-center / justify-center 등으로 카드 열을 중앙 정렬(스크롤이 필요한 짧은 뷰포트에서는 상단 여백·하단 스크롤 규칙을 QA로 확정).
범위메뉴는 기존 www 헤더를 유지하고, 변경 범위는 메뉴 하단 본문에 한정한다.
구현 위치(참고)Prego 모노레포 apps/www 의 trial 라우트·레이아웃(구체 파일은 구현 시 확정).

비고: 시각 스펙의 단일 기준 소스로 m.pregoi.com 해당 화면(또는 디자인 시스템에 등록된 동일 카드 컴포넌트)을 두고, www는 복제가 아니라 동일 토큰/스펙 공유를 목표로 한다.


6. 수용 기준 (예시)

  • …@gmail.com…@gokiri.com(회사로 간주되는 케이스)에 대해, 사용자에게 보이는 문구 또는 다음 액션이 요구사항에 맞게 구분된다.
  • 회사 메일 검증에 성공한 사용자https://m.pregoi.com/?plan=free&region=sg 형태로 진입할 수 있으며(리전 값은 배포 정책에 맞게), 그 화면에서 이메일은 preset·변경 불가이고 Send verification code 로만 이메일 인증(verify) 단계가 진행된다(수동 승인 전용 경로가 다르면 기획서·QA 시나리오에 명시).
  • 직접 m.pregoi.com에 들어온 사용자www에서 넘어온 사용자의 이메일 편집 정책이 다를 수 있으므로, “preset·잠금”은 해당 진입 경로(쿼리 플래그·토큰 등) 에 바인딩하는지 QA에 포함한다.
  • API는 최소한 classification(또는 후속 userFacingReason) 으로 클라이언트가 분기 가능하다.
  • /trial: 메뉴 하단 본문이 m.pregoi.com 카드와 정렬된 박스 크기·스타일이며, PC·모바일에서 뷰포트 기준 세로 중앙에 배치된다(짧은 화면·키보드 오픈 등 예외는 QA 시나리오에 명시).
  • 회귀: 기존 수동 승인 플로우·Zuplo 경로가 깨지지 않는다.

7. 범위 밖·리스크

  • 게이트웨이에서 도메인 비즈니스 규칙 추가는 하지 않는다 (플랫폼 원칙).
  • 분류 강화만으로 스팸·무료 메일 우회가 늘 수 있어, 수동 승인·로그와 함께 설계한다.

8. 관리자 도구 확장 — 2단계 필터·dictionary·로그

아래는 코드 생성 없이 구현 범위를 고정하기 위한 기획 보강이다. 참고 UI는 운영자용 Trial email filter (test) 화면(예: POST /internal/trial-email-check-preview 와 동일 규칙)을 전제로 한다.

8.1 파이프라인 개요 (1차 → 2차 → 최종 판정)

단계역할비고
1차 — 내부 dictionary알려진 무료·공용·일회성 도메인/패턴을 내부 리스트로 걸러 낸다.여기서 매칭되면 “무료/공용 가능성 높음”으로 처리하고, 제품 정책에 따라 자동 트라이얼 불가 등으로 이어질 수 있다.
2차 — 외부 API library1차를 통과한(내부 리스트에 없는) 주소 중, 여전히 무료·소비자 메일일 가능성이 있는 경우 외부 검증 API(제품에서 채택한 라이브러리·SaaS)로 추가 판정한다.API별 비용·지연·쿼터·오탐/미탐은 기획·운영 문서에 명시.
최종내부 dictionary + 외부 API를 모두 거친 뒤 회사 이메일로 볼지, 수동 승인(manual) 으로 안내할지 등을 단일 결과로 확정한다.프론트·운영자 도구는 이 최종 판정중간 단계 메타데이터(어느 단계에서 걸렸는지)를 함께 볼 수 있게 한다.

원칙: 게이트웨이(Zuplo)에는 도메인 비즈니스 규칙을 두지 않는다(§7). 분류·dictionary·외부 API 호출·로그 저장은 prego-control-plane(및 합의된 저장소)에서 수행한다.

8.2 1차 — 내부 “무료 이메일” dictionary 관리 UI

  • 목적: 운영자가 등록된 무료·공용 도메인(및 필요 시 패턴) 목록을 직접 조회·검색·추가·삭제할 수 있게 하여, 오탐(회사 도메인이 무료 리스트에 들어간 경우 등)을 빠르게 수정한다.
  • 화면 위치: 기존 Trial email filter (test) 관리자 화면에 섹션 또는 하위 페이지로 추가(동일 인증·내부 라우트 범위 내).
  • 기능 요구:
    • 목록 조회·페이지네이션(또는 가상 스크롤) 및 키워드 검색(도메인 부분 일치 등).
    • 단건/다건 삭제, 추가(도메인 정규화 규칙: 소문자·FQDN 등 — 구현 시 단일 함수로 고정).
    • 변경 시 감사 가능성: 누가·언제·무엇을 바꿨는지는 §8.5와 연계해 최소 메타데이터를 남길지 기획에서 결정(내부 Bearer만 접근한다고 해도 운영 분쟁 대비 권장).

8.3 2차 — 외부 API library 연동 및 관리자에서의 “API별 테스트”

  • 목적: 1차를 통과한 주소에 대해, 외부 서비스 A, B, …동일 입력을 보내 결과를 비교할 수 있게 하여, 라이브러리 교체·가중치·fallback 순서를 검증한다.
  • 관리자 화면:
    • 통합 미리보기: 한 번의 이메일 입력으로 최종 파이프라인 결과(회사 여부·manual 여부·reasonCode 등)를 본다(기존 preview JSON 확장).
    • 외부 API별 테스트: 선택한 단일 외부 연동만 호출하는 dry-run/미리보기 모드(비용·쿼터 보호를 위해 일일 한도·운영자 전용 여부를 명시).
  • 응답에 포함할 정보(기획):
    • 최종: 회사 이메일로 판정되었는지(예: isCompanyEmail 또는 기존 classification / allowedForAutoTrial 과 정합되는 필드).
    • 단계별: matchedSources 를 확장하거나 별도 배열로 1차 dictionary 히트 여부, 2차 어떤 API가 어떤 라벨을 반환했는지(성공/스킵/실패/타임아웃)를 구분해 표시.

8.4 Preview·공개 API 응답에 “회사 메일 여부” 명시

  • 사용자·운영자 모두 혼동을 줄이기 위해, 필터링 결과 JSON에 다음을 일관되게 포함한다(이름은 OpenAPI·CP 구현 시 확정).
    • 최종 판정: 회사 이메일로 확인되었는지, 아닌지(및 수동 승인 안내가 필요한지).
    • 파이프라인 투명성: 1차만으로 결정 났는지, 2차 외부 API까지 갔는지, mxCheck 등 기존 필드와 모순 없이 병기.

공개 계약을 바꿀 경우: prego-zuplo OpenAPI → 라우트 → CP → www 순(§5.3).

8.5 이메일 주소 감사 로그 · 리포팅

  • 목적: 사용자가 입력한 이메일 주소(또는 정규화된 도메인)에 대해, 나중에 “manual 신청으로 안내된 주소”“회사 이메일로 확정된 주소”관리자 화면에서 목록·필터로 확인한다.
  • 수집 시점: 실제 공개 trial check API 호출 시(필요 시 preview 전용은 제외하거나 플래그로 구분) — 중복·샘플링 정책(동일 세션 반복 입력 등)은 구현 전에 정한다.
  • 관리자 UI:
    • 테이블/내보내기(CSV 등): 기간·최종 판정(manual vs company)·도메인·reasonCode 필터.
    • 개인정보: 이메일은 PII이므로 보관 기간·접근 역할·마스킹(로컬파트) 여부를 §7 및 GDPR/내부 정책과 정합되게 별도 한 줄로 명시한다.

8.6 구현 순서 제안 (기획)

  1. 1차 dictionary 저장소·API·관리자 CRUD(읽기 우선 후 쓰기).
  2. 파이프라인 응답 필드 정리(Preview → 공개 API 정합).
  3. 2차 외부 API 연동 및 관리자 “API별 테스트”.
  4. 감사 로그 저장 및 관리자 리포트.

9. 참고 링크

Help