TELEPASI

검색하기 전에 통하다

Resend.com으로 문의 답변 이메일 자동 발송하기

강병우
2026.07.14 👁️ 144

웹사이트 문의하기 기능에 자동 답변 이메일을 추가하고 싶으신가요? Resend를 사용하면 간단한 코드만으로 전문적인 이메일 발송 시스템을 구축할 수 있습니다.

왜 Resend인가?

Resend는 개발자 친화적인 이메일 발송 서비스로, 다음과 같은 장점이 있습니다:

  • 간편한 API: REST API로 몇 줄의 코드만으로 이메일 발송 가능
  • React 이메일 템플릿: JSX로 아름다운 이메일 템플릿 작성
  • 무료 플랜: 월 3,000건까지 무료 (개인 프로젝트에 충분)
  • 높은 도달률: 전문적인 이메일 인프라로 스팸 처리 최소화
  • 상세한 로그: 발송 내역, 열람률, 클릭률 등 추적 가능

1. Resend 회원가입 및 프로젝트 생성

  1. Resend.com에 접속하여 회원가입
  2. 로그인 후 대시보드에서 새 프로젝트 생성
  3. 프로젝트 이름 입력 (예: "telepasi-contact")

2. 도메인 등록 및 DNS 설정

2.1 Resend에 도메인 추가

Resend 대시보드에서 "Domains" 메뉴로 이동하여 도메인을 추가합니다.

Resend 도메인 설정 화면

주요 설정 항목:

  • Domain: 이메일 발송에 사용할 도메인 (예: telepasi.com)
  • Region: 발송 지역 선택 (한국의 경우 US East 권장)

도메인 추가 후 Resend가 제공하는 DNS 레코드를 확인합니다:

  • SPF 레코드 (TXT): 발송 서버 인증
  • DKIM 레코드 (TXT): 이메일 무결성 검증
  • DMARC 레코드 (TXT): 이메일 정책 설정

2.2 DNS 설정 (가비아 예시)

도메인 등록 업체(예: 가비아)에서 DNS 레코드를 추가합니다.

가비아 DNS 설정 화면

가비아 설정 방법:

  1. 가비아 로그인 → My가비아 → 서비스 관리
  2. 도메인 관리 → DNS 정보 → DNS 관리
  3. Resend가 제공한 레코드 추가:
타입 호스트 값/위치
TXT @ v=spf1 include:_spf.resend.com ~all
TXT resend._domainkey (Resend에서 제공한 DKIM 값)
TXT _dmarc v=DMARC1; p=none; rua=mailto:admin@telepasi.com

주의사항:

  • DNS 전파에는 최대 48시간 소요 (보통 10분~1시간)
  • 설정 후 Resend 대시보드에서 "Verify" 버튼으로 확인
  • 검증 성공 시 도메인 상태가 "Verified"로 변경됨

3. API 키 발급

  1. Resend 대시보드 → "API Keys" 메뉴
  2. "Create API Key" 클릭
  3. 이름: 용도 명시 (예: "Production API Key")
  4. 권한:
    • Sending access: 이메일 발송 권한
    • Full access: 전체 권한 (필요 시)
  5. 생성된 API 키를 안전하게 보관 (.env 파일)
# .env
RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

⚠️ 보안 주의: API 키는 절대 Git에 커밋하지 마세요!

4. 이메일 템플릿 작성

4.1 React Email 설치 (선택사항)

npm install @react-email/components
# 또는
pnpm add @react-email/components

4.2 HTML 템플릿 예시

React Email 없이 간단한 HTML로도 충분합니다:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <style>
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
      line-height: 1.6;
      color: #333;
      max-width: 600px;
      margin: 0 auto;
      padding: 20px;
    }
    .header {
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white;
      padding: 30px;
      border-radius: 10px 10px 0 0;
      text-align: center;
    }
    .content {
      background: #f9f9f9;
      padding: 30px;
      border: 1px solid #e0e0e0;
      border-top: none;
    }
    .footer {
      text-align: center;
      padding: 20px;
      color: #666;
      font-size: 12px;
    }
    .button {
      display: inline-block;
      background: #667eea;
      color: white;
      padding: 12px 30px;
      text-decoration: none;
      border-radius: 5px;
      margin: 20px 0;
    }
  </style>
</head>
<body>
  <div class="header">
    <h1>📧 문의해 주셔서 감사합니다!</h1>
  </div>
  <div class="content">
    <p>안녕하세요, <strong>{{name}}님</strong></p>
    
    <p>텔레파시를 이용해 주셔서 감사합니다.</p>
    
    <p>고객님의 문의 내용을 잘 받았으며, 영업일 기준 1-2일 내에 답변드리겠습니다.</p>
    
    <div style="background: #fff; padding: 15px; border-left: 4px solid #667eea; margin: 20px 0;">
      <strong>문의 내용:</strong><br>
      {{message}}
    </div>
    
    <p>추가 문의사항이 있으시면 이 이메일에 직접 답장해 주세요.</p>
    
    <a href="https://telepasi.com" class="button">사이트 방문하기</a>
  </div>
  <div class="footer">
    <p>© 2026 Telepasi. All rights reserved.</p>
    <p>서울시 강남구 테헤란로 123 | contact@telepasi.com</p>
  </div>
</body>
</html>

5. SvelteKit에서 Resend 사용하기

5.1 Resend SDK 설치

npm install resend
# 또는
pnpm add resend

5.2 API 엔드포인트 생성

src/routes/api/send-contact-email/+server.js 파일을 생성합니다:

import { Resend } from 'resend';
import { RESEND_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';

const resend = new Resend(RESEND_API_KEY);

/** @type {import('./$types').RequestHandler} */
export async function POST({ request }) {
  try {
    const { name, email, message } = await request.json();

    // 입력값 검증
    if (!name || !email || !message) {
      return json(
        { error: '필수 항목을 모두 입력해주세요.' },
        { status: 400 }
      );
    }

    // HTML 템플릿 생성 (변수 치환)
    const htmlTemplate = `
      <!DOCTYPE html>
      <html>
      <head>
        <meta charset="utf-8">
        <style>
          body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
            line-height: 1.6;
            color: #333;
            max-width: 600px;
            margin: 0 auto;
            padding: 20px;
          }
          .header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 30px;
            border-radius: 10px 10px 0 0;
            text-align: center;
          }
          .content {
            background: #f9f9f9;
            padding: 30px;
            border: 1px solid #e0e0e0;
            border-top: none;
          }
          .footer {
            text-align: center;
            padding: 20px;
            color: #666;
            font-size: 12px;
          }
          .button {
            display: inline-block;
            background: #667eea;
            color: white;
            padding: 12px 30px;
            text-decoration: none;
            border-radius: 5px;
            margin: 20px 0;
          }
          .message-box {
            background: #fff;
            padding: 15px;
            border-left: 4px solid #667eea;
            margin: 20px 0;
            white-space: pre-wrap;
          }
        </style>
      </head>
      <body>
        <div class="header">
          <h1>📧 문의해 주셔서 감사합니다!</h1>
        </div>
        <div class="content">
          <p>안녕하세요, <strong>${name}님</strong></p>
          
          <p>텔레파시를 이용해 주셔서 감사합니다.</p>
          
          <p>고객님의 문의 내용을 잘 받았으며, 영업일 기준 1-2일 내에 답변드리겠습니다.</p>
          
          <div class="message-box">
            <strong>문의 내용:</strong><br>
            ${message}
          </div>
          
          <p>추가 문의사항이 있으시면 이 이메일에 직접 답장해 주세요.</p>
          
          <a href="https://telepasi.com" class="button">사이트 방문하기</a>
        </div>
        <div class="footer">
          <p>© 2026 Telepasi. All rights reserved.</p>
          <p>서울시 강남구 테헤란로 123 | contact@telepasi.com</p>
        </div>
      </body>
      </html>
    `;

    // Resend로 이메일 발송
    const { data, error } = await resend.emails.send({
      from: 'Telepasi <noreply@telepasi.com>',
      to: [email], // 문의자 이메일
      replyTo: 'contact@telepasi.com', // 답장 받을 이메일
      subject: `[Telepasi] ${name}님의 문의가 접수되었습니다`,
      html: htmlTemplate
    });

    if (error) {
      console.error('Resend 발송 오류:', error);
      return json(
        { error: '이메일 발송에 실패했습니다.' },
        { status: 500 }
      );
    }

    // 관리자에게도 알림 (선택사항)
    await resend.emails.send({
      from: 'Telepasi <noreply@telepasi.com>',
      to: ['admin@telepasi.com'],
      subject: `[신규 문의] ${name}님으로부터 문의가 들어왔습니다`,
      html: `
        <h2>신규 문의 알림</h2>
        <p><strong>이름:</strong> ${name}</p>
        <p><strong>이메일:</strong> ${email}</p>
        <p><strong>내용:</strong></p>
        <pre>${message}</pre>
      `
    });

    return json({
      success: true,
      message: '이메일이 성공적으로 발송되었습니다.',
      emailId: data.id
    });

  } catch (err) {
    console.error('이메일 발송 오류:', err);
    return json(
      { error: '서버 오류가 발생했습니다.' },
      { status: 500 }
    );
  }
}

5.3 프론트엔드에서 호출

문의하기 폼 컴포넌트 예시:

<script>
  let name = $state('');
  let email = $state('');
  let message = $state('');
  let loading = $state(false);
  let success = $state(false);
  let error = $state(null);

  async function handleSubmit() {
    loading = true;
    error = null;
    success = false;

    try {
      const response = await fetch('/api/send-contact-email', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ name, email, message })
      });

      const result = await response.json();

      if (response.ok) {
        success = true;
        name = '';
        email = '';
        message = '';
        alert('문의가 접수되었습니다. 이메일을 확인해주세요!');
      } else {
        error = result.error || '전송에 실패했습니다.';
      }
    } catch (err) {
      error = '네트워크 오류가 발생했습니다.';
    } finally {
      loading = false;
    }
  }
</script>

<form onsubmit={handleSubmit}>
  <input type="text" bind:value={name} placeholder="이름" required />
  <input type="email" bind:value={email} placeholder="이메일" required />
  <textarea bind:value={message} placeholder="문의 내용" required></textarea>
  <button type="submit" disabled={loading}>
    {loading ? '전송 중...' : '문의하기'}
  </button>
  
  {#if error}
    <p class="error">{error}</p>
  {/if}
</form>

6. 테스트 및 확인

6.1 로컬 테스트

  1. 개발 서버 실행: npm run dev
  2. 문의하기 폼에서 테스트 데이터 입력
  3. 이메일 수신함 확인

Resend로 받은 이메일 예시

6.2 Resend 대시보드에서 확인

  • Logs: 발송 내역 및 상태 확인
  • Analytics: 열람률, 클릭률 통계
  • Errors: 발송 실패 원인 분석

7. 프로덕션 배포 시 체크리스트

  • 도메인 DNS 설정 완료 및 검증
  • API 키 환경 변수 설정 (.env 파일)
  • from 이메일 주소가 검증된 도메인 사용
  • 이메일 템플릿 모바일 반응형 테스트
  • 스팸 필터 테스트 (Gmail, Naver, Daum 등)
  • 에러 핸들링 및 로깅 구현
  • Rate limiting 설정 (남용 방지)

8. 추가 기능 구현 아이디어

8.1 이메일 템플릿 다양화

// 템플릿 파일 분리
import { welcomeTemplate } from '$lib/email-templates/welcome.js';
import { contactTemplate } from '$lib/email-templates/contact.js';
import { resetPasswordTemplate } from '$lib/email-templates/reset-password.js';

8.2 발송 실패 시 재시도 로직

async function sendEmailWithRetry(emailData, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const result = await resend.emails.send(emailData);
      return result;
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
    }
  }
}

8.3 이메일 큐 시스템

대량 발송이 필요한 경우 Redis + Bull Queue 사용:

pnpm add bull redis

9. 비용 및 제한사항

무료 플랜

  • 발송량: 월 3,000건
  • API 호출: 무제한
  • 도메인: 1개

Pro 플랜 ($20/월)

  • 발송량: 월 50,000건
  • 추가 도메인: 무제한
  • 전용 IP: 옵션

발송 제한

  • 초당 최대 10건 (Rate limit)
  • 이메일 크기 최대 40MB

10. 문제 해결 (Troubleshooting)

이메일이 스팸함으로 가는 경우

  • SPF, DKIM, DMARC 레코드 재확인
  • 발송 주소를 noreply@도메인 형식으로 변경
  • 이메일 내용에 스팸 키워드 제거
  • "Warm-up" 기간 필요 (처음엔 소량 발송)

API 키 인증 실패

// API 키 확인
console.log('API Key:', RESEND_API_KEY?.substring(0, 10) + '...');

// 환경 변수 로드 확인
if (!RESEND_API_KEY) {
  throw new Error('RESEND_API_KEY가 설정되지 않았습니다.');
}

도메인 검증 실패

  • DNS 전파 대기 (최대 48시간)
  • DNS 레코드 오타 확인
  • 호스트명 정확히 입력 (@ 또는 빈 문자열)
  • dig 명령어로 DNS 확인:
    dig TXT telepasi.com
    dig TXT resend._domainkey.telepasi.com
    

마무리

Resend를 활용하면 복잡한 이메일 서버 설정 없이도 전문적인 이메일 발송 시스템을 구축할 수 있습니다.

핵심 요약:

  1. 도메인 등록 및 DNS 설정 (SPF, DKIM, DMARC)
  2. API 키 발급 및 환경 변수 설정
  3. HTML 이메일 템플릿 작성
  4. SvelteKit API 엔드포인트 구현
  5. 테스트 및 프로덕션 배포

질문이나 문제가 있으시면 댓글로 남겨주세요. 도움이 되었다면 좋아요도 부탁드립니다! 👍


참고 자료:

주파수 소통방 (0)

로딩 중...