junsobi

Menu

Close

Cloudflare Turnstile과 Resend를 이용한 로봇 체크 및 이메일 전송 구현

Cloudflare Turnstile을 사용하여 로봇을 검증하고, Resend 라이브러리를 이용해 안전하게 이메일을 전송하는 방법에 대해 알아봅니다.

List

Cloudflare Turnstile과 Resend로 구현하는 로봇 체크 및 이메일 전송

현대 웹 애플리케이션에서는 사용자와의 소통을 위한 이메일 기능이 필수적입니다.
하지만, 이를 구현할 때 로봇(봇)으로 인한 스팸이나 악성 요청을 막는 것이 중요한 과제입니다.
이번 블로그에서는 Cloudflare Turnstile을 이용해 로봇을 검증하고, Resend 라이브러리를 사용하여 이메일을 안전하게 전송하는 방법을 소개합니다.
이 방법은 제 포트폴리오 프로젝트에서도 실제로 사용하고 있는 로직입니다. ClouddFlare

Cloudflare Turnstile: 간편하고 강력한 CAPTCHA 솔루션

Turnstile이란?

Cloudflare Turnstile은 사용자의 불편을 최소화하면서 로봇 요청을 효과적으로 차단할 수 있는 CAPTCHA 솔루션입니다.
Google의 reCAPTCHA와 유사하지만, 사용자 경험을 개선한 것이 특징입니다. 사용자가 보통 이미지를 선택하거나 텍스트를 입력할 필요 없이 간단하게 동작합니다.

Turnstile의 검증 로직 구현

Turnstile 검증을 위해 서버에서는 사용자가 제출한 토큰을 Cloudflare의 검증 API를 통해 확인합니다. 아래는 검증 로직 코드입니다:

'use server';
import 'server-only';
 
interface CloudflareTurnstileResponse {
  success: boolean;
  'error-codes': string[];
  challenge_ts: string;
  hostname: string;
}
 
export async function validateTurnstileToken(
  token: string
): Promise<CloudflareTurnstileResponse> {
  const req = await fetch(
    'https://challenges.cloudflare.com/turnstile/v0/siteverify',
    {
      method: 'POST',
      body: `secret=${encodeURIComponent(process.env.TURNSTILE_SECRET_KEY!)}&response=${encodeURIComponent(token)}`,
      headers: {
        'content-type': 'application/x-www-form-urlencoded'
      }
    }
  );
 
  const res = (await req.json()) as CloudflareTurnstileResponse;
  return res;
}

위 코드에서 validateTurnstileToken 함수는 토큰을 API에 보내 검증 결과를 반환합니다. API 호출에는 TURNSTILE_SECRET_KEY 환경 변수를 사용하여 보안을 유지합니다.

resend

Resend: 간편한 이메일 전송 라이브러리

Resend란?

Resend는 개발자가 React 기반 이메일 템플릿을 사용하여 손쉽게 이메일을 전송할 수 있도록 도와주는 라이브러리입니다. 이메일 전송 과정에서 발생할 수 있는 복잡한 설정을 간소화해줍니다.

Resend를 사용한 이메일 전송 구현

아래는 Resend를 사용해 이메일을 전송하는 코드입니다:

'use server';
const EMAIL_FROM = process.env.EMAIL_FROM;
const EMAIL_TO = process.env.EMAIL_TO;
 
export const sendEmail = async (
  name: string,
  email: string,
  message: string
) => {
  const resend = new Resend(process.env.RESEND_API_KEY);
 
  if (!EMAIL_FROM || !EMAIL_TO) {
    throw new Error('Email 설정이 잘못되었습니다.');
  }
 
  const { data: res, error } = await resend.emails.send({
    from: EMAIL_FROM,
    to: EMAIL_TO,
    subject: `${name}의 Portfolio 로부터 보낸 메일`,
    react: ContactEmail({ name, email, message })
  });
 
  if (error) throw new Error(JSON.stringify(error));
 
  return res;
};

위 코드는 sendEmail 함수에서 Resend를 사용해 이메일을 전송합니다.
ContactEmail은 React 기반 템플릿으로, 이메일 내용을 동적으로 생성합니다.

이메일 폼 구현: CAPTCHA와 Resend의 통합

CAPTCHA와 Resend를 통합해 실제로 동작하는 이메일 폼을 구현하는 과정입니다.

프론트엔드 폼 구성

아래는 React를 사용한 폼 구현 코드입니다:

'use client';
const ContactForm = () => {
  const form = useForm<ContactFormType>({
    resolver: zodResolver(ContactFormSchema),
    defaultValues: {
      name: '',
      email: '',
      message: ''
    }
  });
 
  const { execute, result, status } = useAction(contactSubmit);
  const [isOpen, setIsOpen] = useState(false);
 
  const onSubmit = async (values: ContactFormType) => {
    setIsOpen(true);
  };
 
  const onVerify = async (token?: string) => {
    setIsOpen(false);
    if (!token) {
      toast.error('CAPTCHA 확인에 실패했습니다. 다시 시도해주세요.', {
        position: 'bottom-center'
      });
      return;
    }
    execute({ ...form.getValues(), token });
  };
 
  return (
    <div>
      <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
        <FormFields form={form} status={status} />
        <FormError message={result.serverError} />
        <FormSuccess message={result.data?.success} />
        <SubmitButton status={status} />
      </form>
      <TurnstileModal open={isOpen} callback={onVerify} />
    </div>
  );
};
 
export default ContactForm;

주요 포인트

사용자가 폼을 제출하면 Turnstile CAPTCHA를 열어 토큰을 검증합니다. 검증 후, contactSubmit 액션을 실행해 이메일 전송을 처리합니다.

결론

Cloudflare Turnstile과 Resend를 활용하면 로봇 검증과 이메일 전송 기능을 간편하고 효과적으로 구현할 수 있습니다.
이 로직은 사용자 경험을 해치지 않으면서도 보안을 강화할 수 있는 실용적인 방법입니다

웃긴짤방