EmailVerify LogoEmailVerify

React

Email checker for React. Real-time email verification in React forms and components.

React 애플리케이션에 실시간 이메일 검증을 추가하세요. 사용자가 입력할 때 이메일을 검증하고, 잘못된 제출을 방지하며, 폼 UX를 개선합니다.

설치

npm install @emailverify/react @emailverify/node
yarn add @emailverify/react @emailverify/node
pnpm add @emailverify/react @emailverify/node

빠른 시작

유효성 검사가 포함된 기본 이메일 입력

import { useState } from 'react';
import { useEmailVerification } from '@emailverify/react';

function SignupForm() {
  const [email, setEmail] = useState('');
  const { verify, result, isLoading, error } = useEmailVerification();

  const handleEmailBlur = async () => {
    if (email) {
      await verify(email);
    }
  };

  return (
    <form>
      <div className="form-group">
        <label htmlFor="email">이메일</label>
        <input
          id="email"
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          onBlur={handleEmailBlur}
          className={result?.status === 'invalid' ? 'error' : ''}
        />

        {isLoading && <span className="loading">검증 중...</span>}

        {result?.status === 'invalid' && (
          <span className="error-message">
            유효한 이메일 주소를 입력해 주세요
          </span>
        )}

        {result?.status === 'valid' && (
          <span className="success-message">✓ 이메일 검증됨</span>
        )}

        {result?.result?.disposable && (
          <span className="warning-message">
            영구적인 이메일 주소를 사용해 주세요
          </span>
        )}
      </div>

      <button type="submit" disabled={result?.status !== 'valid'}>
        가입하기
      </button>
    </form>
  );
}

프로바이더 설정

앱을 EmailVerify 프로바이더로 감싸세요:

// App.jsx 또는 main.jsx
import { EmailVerifyProvider } from '@emailverify/react';

function App() {
  return (
    <EmailVerifyProvider
      config={{
        // API 호출은 백엔드를 통해 진행
        apiEndpoint: '/api/verify-email',
      }}
    >
      <YourApp />
    </EmailVerifyProvider>
  );
}

클라이언트 측 코드에 API 키를 노출하지 마세요. 항상 백엔드를 통해 이메일을 검증하세요.

백엔드 API 라우트

이메일 검증을 위한 보안 API 엔드포인트 생성:

// pages/api/verify-email.js (Next.js) 또는 Express 라우트
import { EmailVerify } from '@emailverify/node';

const client = new EmailVerify({
  apiKey: process.env.EMAILVERIFY_API_KEY,
});

export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  const { email } = req.body;

  if (!email) {
    return res.status(400).json({ error: 'Email is required' });
  }

  try {
    const result = await client.verify(email);
    return res.json(result);
  } catch (error) {
    console.error('Verification error:', error);
    return res.status(500).json({ error: 'Verification failed' });
  }
}

훅 레퍼런스

useEmailVerification

이메일 검증을 위한 메인 훅:

const {
  verify,      // 이메일 검증 함수
  result,      // 검증 결과 객체
  isLoading,   // 로딩 상태
  error,       // 검증 실패 시 에러 객체
  reset,       // 상태 초기화
} = useEmailVerification(options);

옵션:

interface UseEmailVerificationOptions {
  // 디바운스 지연 시간 (밀리초, 기본값: 500)
  debounceMs?: number;

  // 값 변경 시 자동 검증
  autoVerify?: boolean;

  // 이 기간 동안 결과 캐시 (ms)
  cacheDuration?: number;

  // 커스텀 API 엔드포인트
  apiEndpoint?: string;
}

useEmailInput

입력 상태와 검증을 결합한 편리한 훅:

import { useEmailInput } from '@emailverify/react';

function EmailField() {
  const {
    value,
    onChange,
    onBlur,
    verification,
    isValid,
    errorMessage,
  } = useEmailInput({
    debounceMs: 300,
    validateOnBlur: true,
  });

  return (
    <div>
      <input
        type="email"
        value={value}
        onChange={onChange}
        onBlur={onBlur}
      />
      {errorMessage && <span className="error">{errorMessage}</span>}
    </div>
  );
}

컴포넌트

EmailInput 컴포넌트

검증이 내장된 이메일 입력 컴포넌트:

import { EmailInput } from '@emailverify/react';

function MyForm() {
  const handleVerified = (result) => {
    console.log('검증 결과:', result);
  };

  return (
    <EmailInput
      name="email"
      label="이메일 주소"
      placeholder="you@example.com"
      onVerified={handleVerified}
      showValidationStatus
      blockDisposable
      blockRoleBased={false}
      className="custom-input"
    />
  );
}

Props:

Prop타입기본값설명
onVerifiedfunction-검증 완료 시 콜백
showValidationStatusbooleantrue유효성 검사 표시기 표시
blockDisposablebooleantrue일회용 이메일에 대해 에러 표시
blockRoleBasedbooleanfalse역할 기반 이메일에 대해 에러 표시
verifyOnBlurbooleantrue필드가 포커스를 잃을 때 검증
debounceMsnumber500디바운스 지연

폼 라이브러리 통합

React Hook Form

import { useForm } from 'react-hook-form';
import { useEmailVerification } from '@emailverify/react';

function SignupForm() {
  const {
    register,
    handleSubmit,
    setError,
    clearErrors,
    formState: { errors },
  } = useForm();

  const { verify, isLoading } = useEmailVerification();

  const validateEmail = async (email) => {
    const result = await verify(email);

    if (result.status === 'invalid') {
      setError('email', {
        type: 'validation',
        message: '유효한 이메일 주소를 입력해 주세요',
      });
      return false;
    }

    if (result.result?.disposable) {
      setError('email', {
        type: 'validation',
        message: '일회용 이메일은 허용되지 않습니다',
      });
      return false;
    }

    clearErrors('email');
    return true;
  };

  const onSubmit = async (data) => {
    const isValid = await validateEmail(data.email);
    if (!isValid) return;

    // 폼 제출 진행
    console.log('제출:', data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        type="email"
        {...register('email', {
          required: '이메일을 입력해 주세요',
          pattern: {
            value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
            message: '잘못된 이메일 형식입니다',
          },
        })}
        onBlur={(e) => validateEmail(e.target.value)}
      />
      {errors.email && <span>{errors.email.message}</span>}
      {isLoading && <span>검증 중...</span>}

      <button type="submit">제출</button>
    </form>
  );
}

Formik

import { Formik, Form, Field, ErrorMessage } from 'formik';
import * as Yup from 'yup';
import { useEmailVerification } from '@emailverify/react';

function SignupForm() {
  const { verify } = useEmailVerification();

  const validationSchema = Yup.object({
    email: Yup.string()
      .email('잘못된 이메일 형식입니다')
      .required('이메일을 입력해 주세요')
      .test('email-verification', '유효하지 않은 이메일 주소입니다', async (value) => {
        if (!value) return false;
        const result = await verify(value);
        return result.status === 'valid';
      }),
  });

  return (
    <Formik
      initialValues={{ email: '' }}
      validationSchema={validationSchema}
      onSubmit={(values) => console.log(values)}
    >
      {({ isSubmitting, isValidating }) => (
        <Form>
          <Field type="email" name="email" />
          <ErrorMessage name="email" component="span" className="error" />
          {isValidating && <span>검증 중...</span>}

          <button type="submit" disabled={isSubmitting || isValidating}>
            제출
          </button>
        </Form>
      )}
    </Formik>
  );
}

Zod + React Hook Form

import { z } from 'zod';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { useEmailVerification } from '@emailverify/react';

const schema = z.object({
  email: z.string().email('잘못된 이메일 형식입니다'),
  name: z.string().min(2, '이름은 최소 2자 이상이어야 합니다'),
});

type FormData = z.infer<typeof schema>;

function SignupForm() {
  const { verify, result } = useEmailVerification();

  const {
    register,
    handleSubmit,
    formState: { errors },
    setError,
  } = useForm<FormData>({
    resolver: zodResolver(schema),
  });

  const onSubmit = async (data: FormData) => {
    // 제출 전 이메일 검증
    const verification = await verify(data.email);

    if (verification.status !== 'valid') {
      setError('email', {
        type: 'manual',
        message: '유효한 이메일 주소를 입력해 주세요',
      });
      return;
    }

    // 폼 제출
    console.log('제출:', data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input type="email" {...register('email')} />
      {errors.email && <span>{errors.email.message}</span>}

      <input type="text" {...register('name')} />
      {errors.name && <span>{errors.name.message}</span>}

      <button type="submit">제출</button>
    </form>
  );
}

디바운싱과 성능

내장 디바운싱

훅에는 기본적으로 디바운싱이 포함되어 있습니다:

const { verify } = useEmailVerification({
  debounceMs: 500, // 사용자가 입력을 멈춘 후 500ms 대기
});

// 모든 키 입력에서 검증 (내부적으로 디바운싱됨)
<input
  type="email"
  onChange={(e) => verify(e.target.value)}
/>

수동 디바운싱

더 세밀한 제어를 위해 직접 디바운싱 사용:

import { useDeferredValue, useEffect } from 'react';

function EmailField() {
  const [email, setEmail] = useState('');
  const deferredEmail = useDeferredValue(email);
  const { verify, result } = useEmailVerification();

  useEffect(() => {
    if (deferredEmail && deferredEmail.includes('@')) {
      verify(deferredEmail);
    }
  }, [deferredEmail]);

  return (
    <input
      type="email"
      value={email}
      onChange={(e) => setEmail(e.target.value)}
    />
  );
}

결과 캐싱

중복 API 호출을 피하기 위해 검증 결과를 캐싱:

const { verify } = useEmailVerification({
  cacheDuration: 3600000, // 1시간 동안 캐시
});

스타일링

CSS 클래스

EmailInput 컴포넌트는 다음 클래스를 적용합니다:

/* 컨테이너 */
.bv-email-input { }

/* 입력 필드 */
.bv-email-input__field { }
.bv-email-input__field--valid { }
.bv-email-input__field--invalid { }
.bv-email-input__field--loading { }

/* 상태 표시기 */
.bv-email-input__status { }
.bv-email-input__status--valid { }
.bv-email-input__status--invalid { }
.bv-email-input__status--warning { }

/* 에러 메시지 */
.bv-email-input__error { }

커스텀 스타일링 예제

.bv-email-input__field {
  padding: 12px 16px;
  border: 2px solid #e2e8f0;
  border-radius: 8px;
  font-size: 16px;
  transition: border-color 0.2s;
}

.bv-email-input__field:focus {
  outline: none;
  border-color: #3182ce;
}

.bv-email-input__field--valid {
  border-color: #48bb78;
}

.bv-email-input__field--invalid {
  border-color: #f56565;
}

.bv-email-input__status {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 14px;
  margin-top: 4px;
}

.bv-email-input__status--valid {
  color: #48bb78;
}

.bv-email-input__status--invalid {
  color: #f56565;
}

Tailwind CSS

<EmailInput
  className="w-full px-4 py-3 border-2 rounded-lg focus:ring-2 focus:ring-blue-500"
  inputClassName="data-[valid]:border-green-500 data-[invalid]:border-red-500"
  statusClassName="text-sm mt-1"
/>

TypeScript 지원

내보내기된 타입을 포함한 완전한 TypeScript 지원:

import type {
  VerificationResult,
  EmailVerificationOptions,
  UseEmailVerificationReturn,
} from '@emailverify/react';

interface FormState {
  email: string;
  verification: VerificationResult | null;
}

function useSignupForm(): FormState {
  const [email, setEmail] = useState('');
  const { result } = useEmailVerification();

  return {
    email,
    verification: result,
  };
}

타입 정의

interface VerificationResult {
  email: string;
  status: 'valid' | 'invalid' | 'unknown' | 'accept_all';
  result: {
    deliverable: boolean;
    valid_format: boolean;
    valid_domain: boolean;
    valid_mx: boolean;
    disposable: boolean;
    role: boolean;
    catchall: boolean;
    free: boolean;
    smtp_valid: boolean;
  };
  score: number;
  reason: string | null;
}

interface UseEmailVerificationReturn {
  verify: (email: string) => Promise<VerificationResult>;
  result: VerificationResult | null;
  isLoading: boolean;
  error: Error | null;
  reset: () => void;
}

모범 사례

1. 키 입력마다가 아닌 Blur 시 검증

// ✅ 좋음 - 사용자가 입력을 마쳤을 때 검증
<input
  type="email"
  onBlur={(e) => verify(e.target.value)}
/>

// ❌ 피해야 함 - API 호출이 너무 많음
<input
  type="email"
  onChange={(e) => verify(e.target.value)} // 디바운싱 없이
/>

2. 명확한 피드백 표시

function EmailStatus({ result, isLoading }) {
  if (isLoading) {
    return <span className="text-gray-500">이메일 확인 중...</span>;
  }

  if (!result) return null;

  const messages = {
    valid: { text: '✓ 유효한 이메일', className: 'text-green-600' },
    invalid: { text: '✗ 유효하지 않은 이메일', className: 'text-red-600' },
    unknown: { text: '? 확인할 수 없음', className: 'text-yellow-600' },
  };

  const { text, className } = messages[result.status] || {};

  return <span className={className}>{text}</span>;
}

3. 폼 제출을 차단하지 않기

검증이 보류 중이더라도 제출 허용:

function handleSubmit(data) {
  if (verificationResult?.status === 'invalid') {
    // 에러를 표시하지만 차단하지 않음
    showWarning('이메일이 유효하지 않을 수 있습니다');
  }

  // 제출 진행
  submitForm(data);
}

4. 네트워크 오류 처리

function EmailField() {
  const { verify, result, error, isLoading } = useEmailVerification();

  return (
    <div>
      <input type="email" onBlur={(e) => verify(e.target.value)} />

      {error && (
        <span className="text-yellow-600">
          이메일을 확인할 수 없습니다. 그래도 진행합니다.
        </span>
      )}
    </div>
  );
}

관련 리소스

On this page