React
Email checker for React. Real-time email verification in React forms and components.
React 애플리케이션에 실시간 이메일 검증을 추가하세요. 사용자가 입력할 때 이메일을 검증하고, 잘못된 제출을 방지하며, 폼 UX를 개선합니다.
설치
npm install @emailverify/react @emailverify/nodeyarn add @emailverify/react @emailverify/nodepnpm 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 | 타입 | 기본값 | 설명 |
|---|---|---|---|
onVerified | function | - | 검증 완료 시 콜백 |
showValidationStatus | boolean | true | 유효성 검사 표시기 표시 |
blockDisposable | boolean | true | 일회용 이메일에 대해 에러 표시 |
blockRoleBased | boolean | false | 역할 기반 이메일에 대해 에러 표시 |
verifyOnBlur | boolean | true | 필드가 포커스를 잃을 때 검증 |
debounceMs | number | 500 | 디바운스 지연 |
폼 라이브러리 통합
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>
);
}