docs(design): 신청 접수 알림과 완료 안내 메일 설계 v0.1
랜딩 신청 모달은 남긴 연락처로 안내한다고 말하는데 보내는 코드가 없다. 운영자도 신청이 들어온 사실을 알 방법이 없다. discovery_leads 는 anon insert 만 열려 있고 폴러는 supporter_builds 만 본다. 메일을 셋으로 나눈다. 접수 확인(신청자)과 신규 신청 알림(운영자)은 insert 트리거로 자동 보내고, 완료 안내는 사람이 보낸다. 자동으로 보내면 사진 검토를 건너뛴 링크가 나간다. 발송은 Edge Function 에 두고 프런트에서 부르지 않는다(anon 키 스팸 경로). email_log 로 중복 발송을 막고 안 갔다는 문의에 답할 근거를 남긴다. 연락처가 전화번호일 수 있어 contact_kind 를 함께 저장한다. 발송 서비스 계정·발신 도메인·"리포트"의 정의는 결정 대기로 8절에 적었다. 개인 업무 문서(주간보고)는 저장소에 올리지 않는다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5cf28d9eef
commit
74c70d656e
4
.gitignore
vendored
4
.gitignore
vendored
@ -17,3 +17,7 @@ src/data/db/
|
||||
|
||||
# 파이썬 캐시
|
||||
__pycache__/
|
||||
|
||||
# 개인 업무 문서 (주간보고 등). 저장소에 올리지 않는다
|
||||
docs/*주간보고*
|
||||
docs/*weekly_report*
|
||||
|
||||
172
docs/DESIGN_신청알림메일_v0.1.md
Normal file
172
docs/DESIGN_신청알림메일_v0.1.md
Normal file
@ -0,0 +1,172 @@
|
||||
# 설계: 신청 접수 알림과 완료 안내 메일 v0.1
|
||||
|
||||
작성 2026-09-18 · 대상 AI Discovery 랜딩(`/discovery`) 신청 흐름
|
||||
|
||||
---
|
||||
|
||||
## 1. 지금 무엇이 비어 있나
|
||||
|
||||
랜딩 신청 모달은 이렇게 말합니다.
|
||||
|
||||
> 입력하신 URL을 실측 진단한 뒤, 남겨주신 연락처로 리포트를 안내드립니다.
|
||||
|
||||
**보내는 코드가 없습니다.** `workers/`, `scripts/`, `supabase/functions/` 전체에 메일 발송이 없습니다. 유일한 `mailto:`는 저장 실패 시 사람이 직접 메일을 쓰라고 띄우는 폴백입니다(`DiscoveryLeadModal.tsx:82`).
|
||||
|
||||
그래서 지금은 이렇게 됩니다.
|
||||
|
||||
```
|
||||
신청 → discovery_leads (status: new)
|
||||
→ supporter_builds 큐 → 워커가 빌드 → preview
|
||||
→ 여기서 끝. 신청자에게도, 운영자에게도 아무 연락이 없음
|
||||
```
|
||||
|
||||
2026-09-18 JK성형외과 신청이 정상 접수되고 10분 만에 빌드까지 끝났지만, 신청자가 직접 확인하기 전까지 아무도 몰랐습니다.
|
||||
|
||||
**문제는 두 갈래입니다.**
|
||||
|
||||
| | 문제 |
|
||||
|---|---|
|
||||
| 운영자 | 신청이 들어온 사실을 알 방법이 없습니다. `discovery_leads`는 anon insert만 열려 있고 조회는 service_role 전용이며, 폴러는 `supporter_builds`만 봅니다 |
|
||||
| 신청자 | 화면이 약속한 안내를 받지 못합니다 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 설계 원칙
|
||||
|
||||
**원칙 1. 자동 발송은 접수 알림까지만 한다.**
|
||||
|
||||
완료 안내를 자동으로 보내면 사람 검토를 건너뛴 결과물 링크가 나갑니다. 이번 JK 건에서 첫 화면 대표 사진이 사람 확인이 필요한 사진이었던 것을 생각하면, 자동 발송이 아니었던 것이 결과적으로 안전했습니다. 완료 안내는 **사람이 보내기를 누를 때** 나갑니다.
|
||||
|
||||
**원칙 2. 화면의 약속과 코드를 맞춘다.**
|
||||
|
||||
지금 모달 문구는 자동으로 갈 것처럼 읽힙니다. 실제 동작에 맞게 문구를 고치거나, 동작을 문구에 맞추거나 둘 중 하나를 해야 합니다. 이 설계는 후자에 가깝되 검토 단계를 명시하는 쪽입니다.
|
||||
|
||||
**원칙 3. "리포트"라는 말을 정확히 쓴다.**
|
||||
|
||||
현재 파이프라인이 만드는 것은 **서포터즈 사이트**입니다. 랜딩이 말하는 AEO/GEO 36항목 진단 리포트(`/discovery/viewclinic` 형태)는 이 빌드에서 생성되지 않습니다. 신청 한 건이 서로 다른 두 산출물로 갈라져 있어, 메일 문안에서 무엇을 보내는지 분명히 해야 합니다.
|
||||
|
||||
---
|
||||
|
||||
## 3. 보내는 메일 3종
|
||||
|
||||
### 3-1. 접수 확인 (신청자에게, 자동)
|
||||
|
||||
- **시점**: `discovery_leads` insert 직후
|
||||
- **받는 사람**: 신청자가 남긴 연락처
|
||||
- **내용**: 접수 사실, 어떤 URL로 접수됐는지, 언제쯤 연락이 가는지, 문의처(`o2oteam@o2o.kr`)
|
||||
- **보내지 않는 것**: 결과 링크, 진행률
|
||||
|
||||
### 3-2. 신규 신청 알림 (운영자에게, 자동)
|
||||
|
||||
- **시점**: 3-1과 동시
|
||||
- **받는 사람**: 운영 담당 주소 1개
|
||||
- **내용**: 병원명·URL·연락처, 빌드 큐 등록 여부, 확인 화면 링크(`/images/<clinic_id>`, `/supporters/<clinic_id>`)
|
||||
- **목적**: 신청을 놓치지 않는 최소 장치. 이것만 있어도 지금 상태보다 낫습니다
|
||||
|
||||
### 3-3. 완료 안내 (신청자에게, 사람이 보냄)
|
||||
|
||||
- **시점**: 운영자가 이미지 검토와 결과 확인을 마치고 **보내기를 눌렀을 때**
|
||||
- **받는 사람**: 신청자
|
||||
- **내용**: 무엇을 만들었는지, 미리보기 링크(noindex), 병원이 확인해야 할 것 두 가지(`/images/<id>` 사진 확인, `/supporters/<id>` 확인 항목), 색인 전환은 법무 확인 뒤라는 안내
|
||||
- **전제**: 발송 전에 사진 검토가 끝나 있어야 합니다
|
||||
|
||||
---
|
||||
|
||||
## 4. 무엇으로 보내나
|
||||
|
||||
### 4-1. 발송 수단
|
||||
|
||||
**현재 메일 발송 키가 하나도 없습니다.** `.env`에 Resend·SendGrid·SES·SMTP 어느 것도 없어 계정부터 필요합니다.
|
||||
|
||||
| 후보 | 장점 | 단점 |
|
||||
|---|---|---|
|
||||
| **Resend** (추천) | Edge Function에서 fetch 한 번. 도메인 인증이 단순. 무료 한도로 이 규모는 충분 | 계정·도메인 인증 필요 |
|
||||
| SendGrid | 전달률 기록이 길다 | 설정 항목이 많다 |
|
||||
| AWS SES | 단가가 가장 낮다 | 샌드박스 해제 심사가 필요 |
|
||||
|
||||
발신 도메인은 `o2o.kr` 계열을 쓰고, 신규 도메인은 초기 전달률이 낮으므로 SPF·DKIM·DMARC를 먼저 맞춥니다.
|
||||
|
||||
### 4-2. 발송 위치
|
||||
|
||||
**Supabase Edge Function `notify-lead` 를 새로 만듭니다.**
|
||||
|
||||
프런트에서 직접 부르지 않습니다. 프런트가 부르면 anon 키로 메일을 쏠 수 있게 되어 스팸 경로가 됩니다. DB 변경을 신호로 삼습니다.
|
||||
|
||||
```
|
||||
discovery_leads INSERT
|
||||
→ Postgres 트리거 (pg_net)
|
||||
→ Edge Function notify-lead (service_role 검증)
|
||||
→ Resend 호출 2건 (신청자 접수 확인 + 운영자 알림)
|
||||
→ email_log 기록
|
||||
```
|
||||
|
||||
완료 안내(3-3)는 같은 함수의 다른 진입점으로 두고, 운영자 화면의 버튼에서 호출합니다.
|
||||
|
||||
---
|
||||
|
||||
## 5. 필요한 DB 변경
|
||||
|
||||
### 5-1. `email_log` 신설
|
||||
|
||||
무엇을 언제 누구에게 보냈는지 남깁니다. 중복 발송을 막고, 안 갔다는 문의에 답할 근거가 됩니다.
|
||||
|
||||
| 열 | 용도 |
|
||||
|---|---|
|
||||
| `id` | |
|
||||
| `kind` | `lead_ack` · `lead_notify` · `build_done` |
|
||||
| `lead_id` / `clinic_id` | 대상 |
|
||||
| `to_addr` | 받는 주소 |
|
||||
| `status` | `sent` · `failed` |
|
||||
| `provider_id` | 발송 서비스가 준 id. 전달 추적용 |
|
||||
| `error` | 실패 사유 |
|
||||
| `created_at` | |
|
||||
|
||||
중복 발송 방지는 `(kind, lead_id)` 유니크로 겁니다.
|
||||
|
||||
### 5-2. `discovery_leads.status` 를 실제로 쓴다
|
||||
|
||||
지금은 모든 행이 `new`로 고정입니다. 흐름을 담도록 값을 정합니다.
|
||||
|
||||
```
|
||||
new → notified(접수 메일 발송) → building → review(사람 검토 중)
|
||||
→ delivered(완료 안내 발송) → closed
|
||||
```
|
||||
|
||||
### 5-3. 연락처가 이메일이 아닐 수 있다
|
||||
|
||||
모달은 "이메일 또는 전화번호"를 받습니다. 전화번호면 메일을 보낼 수 없습니다.
|
||||
|
||||
- 저장 시 형식을 판별해 `contact_kind`(`email` · `phone`)를 같이 남깁니다
|
||||
- `phone`이면 접수 메일을 건너뛰고 운영자 알림에 **전화로 연락 필요**를 표시합니다
|
||||
|
||||
---
|
||||
|
||||
## 6. 화면에서 고칠 것
|
||||
|
||||
| 위치 | 지금 | 바꿀 안 |
|
||||
|---|---|---|
|
||||
| 신청 모달 완료 문구 | "실측 진단한 뒤, 남겨주신 연락처로 리포트를 안내드립니다" | "접수됐습니다. 확인 메일을 보내 드렸습니다. 결과가 준비되면 남겨주신 연락처로 안내드립니다" |
|
||||
| 운영자 화면 | 없음 | 완료 안내 발송 버튼. 사진 검토가 끝나지 않았으면 버튼을 잠그고 이유를 표시 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 만들 순서
|
||||
|
||||
| 순서 | 할 일 | 비고 |
|
||||
|---|---|---|
|
||||
| 1 | 발송 서비스 계정과 도메인 인증 | **haewon 결정 필요.** 이것 없이는 아무것도 못 보냅니다 |
|
||||
| 2 | `email_log` 테이블, `contact_kind` 열 추가 | |
|
||||
| 3 | Edge Function `notify-lead` + 트리거 | 3-1·3-2 자동 발송 |
|
||||
| 4 | 모달 문구 수정 | 1~3과 무관하게 지금 할 수 있습니다 |
|
||||
| 5 | 완료 안내 발송 버튼 | 사진 검토 완료 조건과 묶습니다 |
|
||||
|
||||
**1번이 없으면 2~3번이 무의미하므로, 4번을 먼저 넣어 화면과 코드의 어긋남부터 없애는 방법도 있습니다.**
|
||||
|
||||
---
|
||||
|
||||
## 8. haewon 결정이 필요한 것
|
||||
|
||||
1. **발송 서비스**: Resend를 추천합니다. 계정과 발신 도메인(`o2o.kr` 하위 무엇으로 할지)이 필요합니다
|
||||
2. **운영자 알림 수신 주소**: 개인 주소인지 공용 주소인지
|
||||
3. **완료 안내를 사람이 보내는 방식으로 할지**: 자동 발송으로 하면 사진 검토를 건너뛴 링크가 나갈 수 있습니다. 이 설계는 사람이 보내는 쪽을 전제합니다
|
||||
4. **"리포트"의 정의**: 완료 안내로 보내는 것이 서포터즈 사이트인지, AEO/GEO 진단 리포트인지, 둘 다인지. 지금 파이프라인은 앞의 것만 만듭니다
|
||||
Loading…
Reference in New Issue
Block a user