외부 메일 서비스인 Resend를 이용해 이메일 발송 기능을 구현하고 배포 환경에서 동작을 검토하는 방법을 확인합니다

구분 학습 내용
메일 어댑터 실제 메일 발송 구현을 감싸는 교체 가능한 구조
실제 발송 어댑터 Resend API를 이용해 메일을 발송하는 구현
실패 어댑터 local 또는 설정 누락 상황에서 발송하지 않고 실패 결과를 반환하는 구현
환경 표식 preview 메일 제목 앞에 [preview]를 붙이는 정책
결과 분리 데이터 저장 성공과 메일 발송 성공을 분리해서 처리하는 정책
동작 검토 local, preview, production에서 각각 기대 동작을 확인하는 방법

[준비] 사용자의 숙제 → 계정과 권한 세팅하기

Resend · Email for developers

<aside> 💡

Resend 계정·발신 준비

  1. resend.com 가입 → 대시보드 API Keys → Create API Key로 키 발급(한 번만 보이므로 복사).

  2. 발신 주소(EMAIL_FROM_ADDRESS) 선택:

    우리 권장 가이드라인 → 발신주소는 Vercel 환경변수에!

  3. Resend 에 도메인 인증하기 (기존 Vercel 도메인 연결 방식도 수정 필요 → ~~Vercel 버그 때문~~)

    Vercel 앱 배포 & Resend 메일 서비스 도메인 연결 수정하기

</aside>


1단계. 메일 발송은 본 작업과 분리해서 생각한다

메일 발송은 보통 다음 흐름에서 사용됩니다.

상황 메일 예시
로그인 매직링크 발송
결제 결제 완료 안내
강의 수강 신청 완료 안내
관리자 공지 메일 발송
알림 과제 제출 확인, 승인 안내

하지만 메일 발송은 외부 서비스에 의존합니다. 네트워크 문제, API 키 누락, 도메인 설정 문제, 수신자 주소 문제로 실패할 수 있습니다.

따라서 중요한 원칙은 다음과 같습니다.

데이터 저장과 메일 발송 결과를 분리한다.

예를 들어 수강 신청 기능을 생각해 보겠습니다.

작업 실패 시 영향
수강 신청 데이터 저장 핵심 작업이므로 실패하면 요청 실패
수강 신청 완료 메일 발송 알림 작업이므로 실패해도 저장 결과를 되돌리지 않음
%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#ECFDF5",
    "primaryBorderColor": "#10B981",
    "primaryTextColor": "#064E3B",
    "lineColor": "#64748B",
    "secondaryColor": "#E0F2FE",
    "tertiaryColor": "#FEF3C7",
    "fontFamily": "Pretendard, Inter, Arial"
  }
}}%%
flowchart TD
    A["사용자 요청<br/>예: 수강 신청"] --> B["핵심 작업 수행<br/>DB에 신청 데이터 저장"]
    B --> C{"저장 성공?"}
    C -- "아니오" --> X["요청 실패<br/>핵심 데이터가 저장되지 않음"]
    C -- "예" --> D["알림 메일 발송 시도<br/>외부 메일 서비스 호출"]
    D --> E{"메일 발송 성공?"}
    E -- "성공" --> F["저장 성공 + 알림 성공<br/>정상 완료"]
    E -- "실패" --> G["저장 성공 + 알림 실패<br/>실패 로그만 남기고 본 작업은 유지"]

    classDef request fill:#E0F2FE,stroke:#0284C7,stroke-width:1.5px,color:#0C4A6E;
    classDef db fill:#DCFCE7,stroke:#16A34A,stroke-width:1.5px,color:#14532D;
    classDef mail fill:#FEF3C7,stroke:#F59E0B,stroke-width:1.5px,color:#78350F;
    classDef fail fill:#FEE2E2,stroke:#DC2626,stroke-width:1.5px,color:#7F1D1D;

    class A request;
    class B,C db;
    class D,E,F,G mail;
    class X fail;

2단계. 메일 발송 케이스를 분리해서 구조화하기