SMTP ์ด๋ฉ์ผ ๊ฒ์ฆ์ ์ด๋ฉ์ผ ์ฃผ์๊ฐ ์ค์ ๋ก ๋ฉ์์ง๋ฅผ ์์ ํ ์ ์๋์ง ํ์ธํ๋ ์ต๊ณ ์ ํ์ค์ ๋๋ค. ๊ธฐ๋ณธ์ ์ธ ๊ตฌ๋ฌธ ๊ฒ์ฆ์ด๋ ๋๋ฉ์ธ ํ์ธ๊ณผ ๋ฌ๋ฆฌ, SMTP ๊ฒ์ฆ์ ์์ ์์ ๋ฉ์ผ ์๋ฒ์ ์ง์ ํต์ ํ์ฌ ํน์ ๋ฉ์ผํจ์ด ์กด์ฌํ๋ฉฐ ์ด๋ฉ์ผ์ ์์ ํ ์ ์๋์ง ํ์ธํฉ๋๋ค. ์ด ๊ฐ๋ ฅํ ์ด๋ฉ์ผ ์ ํจ์ฑ ๊ฒ์ฌ ๊ธฐ์ ์ ์ ๋ฌธ ์ด๋ฉ์ผ ๊ฒ์ฆ ์๋น์ค์ ํต์ฌ์ ํ์ฑํ๋ฉฐ, ๊ธฐ์ ์ด ๊นจ๋ํ ์ด๋ฉ์ผ ๋ชฉ๋ก์ ์ ์งํ๊ณ ๋ฐ์ ์ ํํ์ ๋ณดํธํ๋ฉฐ ์ด๋ฉ์ผ ์ ๋ฌ๋ฅ ์ ๊ฐ์ ํ๋ ๋ฐ ๋์์ ์ค๋๋ค.
SMTP ์ด๋ฉ์ผ ๊ฒ์ฆ ์ดํดํ๊ธฐ
SMTP๋ Simple Mail Transfer Protocol์ ์ฝ์๋ก, ์ด๋ฉ์ผ ์ ์ก์ ์ํ ์ธํฐ๋ท ํ์ค์ ๋๋ค. ์ด๋ฉ์ผ์ ๋ณด๋ผ ๋๋ง๋ค ๋ฉ์ผ ํด๋ผ์ด์ธํธ๋ ์๋ฒ๋ SMTP๋ฅผ ์ฌ์ฉํ์ฌ ์์ ์์ ๋ฉ์ผ ์๋ฒ๋ก ๋ฉ์์ง๋ฅผ ์ ๋ฌํฉ๋๋ค. SMTP ์ด๋ฉ์ผ ๊ฒ์ฆ์ ์ด์ ๋์ผํ ํ๋กํ ์ฝ์ ํ์ฉํ์ฌ ์ด๋ฉ์ผ ์ฃผ์๊ฐ ์กด์ฌํ๋์ง ํ์ธํ์ง๋ง, ์ค์ ๋ก ๋ฉ์์ง๋ฅผ ๋ณด๋ด์ง๋ ์์ต๋๋ค.
SMTP ๊ฒ์ฆ์ ์ฅ์ ์ ์์ค์์ ์ง์ ์ด๋ฉ์ผ ์ฃผ์๋ฅผ ํ์ธํ ์ ์๋ค๋ ์ ์ ๋๋ค. ํ์์ด๋ ๋๋ฉ์ธ์ ๊ธฐ๋ฐ์ผ๋ก ์ฃผ์๊ฐ ์ ํจํ์ง ์ถ์ธกํ๋ ๋์ , SMTP ๊ฒ์ฆ์ ๋ฉ์ผ ์๋ฒ์ ์ง์ "์ด ์ฃผ์๋ก ๋ฉ์ผ์ ๋ฐ์ ์ ์์ต๋๊น?"๋ผ๊ณ ๋ฌป์ต๋๋ค. ์๋ฒ์ ์๋ต์ ๋ฉ์ผํจ์ด ์กด์ฌํ๋์ง, ๊ฐ๋ ์ฐจ ์๋์ง, ๋นํ์ฑํ๋์๋์ง๋ฅผ ์๋ ค์ค๋๋ค.
SMTP ์ด๋ฉ์ผ ๊ฒ์ฆ ์๋ ์๋ฆฌ
SMTP ๊ฒ์ฆ ํ๋ก์ธ์ค๋ ์ด๋ฉ์ผ ์ ๋ฌ์ ์์์ ๋ชจ๋ฐฉํ์ง๋ง ์ค์ ๋ฉ์์ง ๋ด์ฉ์ ๋ณด๋ด๊ธฐ ์ ์ ์ค๋จ๋๋ ํน์ ๋ช ๋ น์ด ์์๋ฅผ ๋ฐ๋ฆ ๋๋ค. ๋จ๊ณ๋ณ ์ค๋ช ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
1๋จ๊ณ: MX ๋ ์ฝ๋์ ๋ํ DNS ์กฐํ
๋ฉ์ผ ์๋ฒ์ ์ฐ๊ฒฐํ๊ธฐ ์ ์ ๊ฒ์ฆ ํ๋ก์ธ์ค๋ ๋๋ฉ์ธ์ ์ด๋ฉ์ผ์ ์ฒ๋ฆฌํ๋ ์๋ฒ๋ฅผ ์๋ณํด์ผ ํฉ๋๋ค. ์ด๋ DNS์์ Mail Exchange (MX) ๋ ์ฝ๋๋ฅผ ์ฟผ๋ฆฌํ๋ ๊ฒ์ ํฌํจํฉ๋๋ค. ๋๋ฉ์ธ์ ์ฐ์ ์์๊ฐ ๋ค๋ฅธ ์ฌ๋ฌ MX ๋ ์ฝ๋๋ฅผ ๊ฐ์ง ์ ์์ผ๋ฉฐ, ์ฃผ ์๋ฒ๋ฅผ ์ฌ์ฉํ ์ ์๋ ๊ฒฝ์ฐ ๋์ฒด ์๋ฒ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
2๋จ๊ณ: TCP ์ฐ๊ฒฐ ์๋ฆฝ
๋ฉ์ผ ์๋ฒ๊ฐ ์๋ณ๋๋ฉด ๊ฒ์ฆ๊ธฐ๋ ํฌํธ 25(ํ์ค SMTP ํฌํธ) ๋๋ 587์ด๋ 465์ ๊ฐ์ ๋์ฒด ํฌํธ์์ TCP ์ฐ๊ฒฐ์ ์๋ฆฝํฉ๋๋ค.
3๋จ๊ณ: SMTP ํธ๋์ ฐ์ดํฌ (HELO/EHLO)
์ฐ๊ฒฐ์ ์ธ์ฌ๋ง๋ก ์์๋ฉ๋๋ค. ๊ฒ์ฆ๊ธฐ๋ EHLO (Extended HELO) ๋๋ HELO ๋ช ๋ น์ ๋ณด๋ด ๋ฉ์ผ ์๋ฒ์ ์์ ์ ์๊ฐํฉ๋๋ค. ์๋ฒ๋ ์์ ์ ๊ธฐ๋ฅ์ผ๋ก ์๋ตํ๊ณ ์งํํ ์ค๋น๊ฐ ๋์์์ ํ์ธํฉ๋๋ค.
4๋จ๊ณ: MAIL FROM ๋ช ๋ น
๊ฒ์ฆ๊ธฐ๋ MAIL FROM ๋ช ๋ น์ ์ฌ์ฉํ์ฌ ๋ฐ์ ์ ์ฃผ์๋ฅผ ์ง์ ํฉ๋๋ค. ์ด ์ฃผ์๊ฐ ์ต์ข ๋ฐ์ ์์ผ ํ์๋ ์์ง๋ง, MAIL FROM ๋๋ฉ์ธ์ ์ ์ ํ DNS ๋ ์ฝ๋๊ฐ ์๊ฑฐ๋ ์์ฌ์ค๋ฌ์ ๋ณด์ด๋ฉด ๋ฉ์ผ ์๋ฒ๊ฐ ๊ฒ์ฆ ์๋๋ฅผ ๊ฑฐ๋ถํ ์ ์์ต๋๋ค.
5๋จ๊ณ: RCPT TO ๋ช ๋ น (์ค์ํ ๋จ๊ณ)
์ค์ ๊ฒ์ฆ์ด ์ด๋ฃจ์ด์ง๋ ๊ณณ์ ๋๋ค. ๊ฒ์ฆ๊ธฐ๋ ํ์ธ ์ค์ธ ์ด๋ฉ์ผ ์ฃผ์์ ํจ๊ป RCPT TO ๋ช ๋ น์ ๋ณด๋ ๋๋ค. ์ด ๋ช ๋ น์ ๋ํ ๋ฉ์ผ ์๋ฒ์ ์๋ต์ ๋ฉ์ผํจ์ด ์กด์ฌํ๋์ง ์ฌ๋ถ๋ฅผ ๋ํ๋ ๋๋ค.
- 250 OK: ๋ฉ์ผํจ์ด ์กด์ฌํ๋ฉฐ ๋ฉ์ผ์ ๋ฐ์ ์ ์์
- 550 User unknown: ๋ฉ์ผํจ์ด ์กด์ฌํ์ง ์์
- 551 User not local: ์๋ฒ๊ฐ ์ฌ์ฉ์๋ฅผ ์๊ณ ์์ง๋ง ๋ค๋ฅธ ์ฃผ์๋ฅผ ์ ์ํจ
- 552 Mailbox full: ๋ฉ์ผํจ์ด ์กด์ฌํ์ง๋ง ๋ฉ์์ง๋ฅผ ๋ฐ์ ์ ์์
- 553 Mailbox name not allowed: ์ฃผ์ ๊ตฌ๋ฌธ์ด ๊ฑฐ๋ถ๋จ
6๋จ๊ณ: QUIT
RCPT TO ์๋ต์ ๋ฐ์ ํ ๊ฒ์ฆ๊ธฐ๋ QUIT์ ๋ณด๋ด ์ค์ ๋ก ๋ฉ์์ง๋ฅผ ๋ณด๋ด์ง ์๊ณ ์ฐ๊ฒฐ์ ์ ์์ ์ผ๋ก ๋ซ์ต๋๋ค.
SMTP ์๋ต ์ฝ๋ ์ค๋ช
SMTP ์๋ต ์ฝ๋๋ฅผ ์ดํดํ๋ ๊ฒ์ ์ ํํ ์ด๋ฉ์ผ ์ ํจ์ฑ ๊ฒ์ฌ ์์คํ ์ ๊ตฌ์ถํ๋ ๋ฐ ํ์์ ์ ๋๋ค. ์ด ์ธ ์๋ฆฌ ์ฝ๋๋ ๊ฒ์ฆ ๊ฒฐ๊ณผ๋ฅผ ๊ฒฐ์ ํ๋ ํน์ ์๋ฏธ๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค.
2xx ์ฑ๊ณต ์ฝ๋
- 250: ์์ฒญํ ์์ ์ด ์ฑ๊ณต์ ์ผ๋ก ์๋ฃ๋จ (์ด๋ฉ์ผ ์กด์ฌ)
- 251: ์ฌ์ฉ์๊ฐ ๋ก์ปฌ์ ์์; ์ง์ ๋ ๊ฒฝ๋ก๋ก ์ ๋ฌํจ
4xx ์์ ์คํจ ์ฝ๋
- 421: ์๋น์ค๋ฅผ ์ฌ์ฉํ ์ ์์, ์ ์ก ์ฑ๋ ๋ซ๋ ์ค
- 450: ์์ฒญํ ๋ฉ์ผ ์์ ์ด ์ํ๋์ง ์์: ๋ฉ์ผํจ์ ์ฌ์ฉํ ์ ์์ (์ฌ์ฉ ์ค/์์ ์ฐจ๋จ)
- 451: ์์ฒญํ ์์ ์ด ์ค๋จ๋จ: ์ฒ๋ฆฌ ์ค ๋ก์ปฌ ์ค๋ฅ
- 452: ์์ฒญํ ์์ ์ด ์ํ๋์ง ์์: ์์คํ ์ ์ฅ ๊ณต๊ฐ ๋ถ์กฑ
5xx ์๊ตฌ ์คํจ ์ฝ๋
- 550: ์์ฒญํ ์์ ์ด ์ํ๋์ง ์์: ๋ฉ์ผํจ์ ์ฌ์ฉํ ์ ์์ (์กด์ฌํ์ง ์์)
- 551: ์ฌ์ฉ์๊ฐ ๋ก์ปฌ์ ์์; ๋ค๋ฅธ ๊ฒฝ๋ก๋ฅผ ์๋ํ์ธ์
- 552: ์์ฒญํ ๋ฉ์ผ ์์ ์ด ์ค๋จ๋จ: ์ ์ฅ ๊ณต๊ฐ ํ ๋น ์ด๊ณผ
- 553: ์์ฒญํ ์์ ์ด ์ํ๋์ง ์์: ๋ฉ์ผํจ ์ด๋ฆ์ด ํ์ฉ๋์ง ์์
- 554: ํธ๋์ญ์ ์คํจ
4xx์ 5xx ์ฝ๋์ ๊ตฌ๋ถ์ ๋งค์ฐ ์ค์ํฉ๋๋ค. 4xx ์๋ต์ ์์ ๋ฌธ์ ๋ฅผ ๋ํ๋ ๋๋ค. ๋ฉ์ผํจ์ด ๋์ค์ ์ฌ์ฉ ๊ฐ๋ฅํด์ง ์ ์์ต๋๋ค. 5xx ์๋ต์ ์๊ตฌ์ ์ธ ์คํจ๋ฅผ ๋ํ๋ ๋๋ค. ์ฃผ์๋ ์ ํจํ์ง ์์ ๊ฒ์ผ๋ก ๊ฐ์ฃผ๋์ด์ผ ํฉ๋๋ค.
SMTP ์ด๋ฉ์ผ ๊ฒ์ฆ ๊ตฌํํ๊ธฐ
SMTP ๊ฒ์ฆ ์์คํ ์ ๊ตฌ์ถํ๋ ค๋ฉด ํ๋กํ ์ฝ ์ธ๋ถ ์ฌํญ, ์ค๋ฅ ์ฒ๋ฆฌ ๋ฐ ์๋ ์ ํ์ ์ธ์ฌํ ์ฃผ์๊ฐ ํ์ํฉ๋๋ค. ๋ค์์ ์ค์ฉ์ ์ธ ๊ตฌํ ๊ฐ์ด๋์ ๋๋ค.
๊ธฐ๋ณธ SMTP ๊ฒ์ฆ ํ๋ฆ
๋ค์ ์์ฌ ์ฝ๋๋ ํต์ฌ ๊ฒ์ฆ ๋ก์ง์ ๋ณด์ฌ์ค๋๋ค.
function verifyEmail(email):
domain = extractDomain(email)
// Step 1: Get MX records
mxRecords = getMXRecords(domain)
if mxRecords is empty:
return INVALID_DOMAIN
// Sort by priority (lowest number = highest priority)
sortByPriority(mxRecords)
// Try each MX server
for mx in mxRecords:
try:
// Step 2: Connect
connection = connectSMTP(mx.host, 25)
// Step 3: EHLO
response = sendCommand("EHLO verifier.example.com")
if response.code != 250:
continue // Try next MX
// Step 4: MAIL FROM
response = sendCommand("MAIL FROM:<verify@example.com>")
if response.code != 250:
continue
// Step 5: RCPT TO
response = sendCommand("RCPT TO:<" + email + ">")
// Step 6: QUIT
sendCommand("QUIT")
closeConnection()
// Interpret result
if response.code == 250:
return VALID
else if response.code >= 500:
return INVALID
else:
return UNKNOWN
catch ConnectionError:
continue // Try next MX
return UNABLE_TO_VERIFY
Node.js ๊ตฌํ
๋ค์์ ๋ค์ดํฐ๋ธ net ๋ฐ dns ๋ชจ๋์ ์ฌ์ฉํ ์ค์ฉ์ ์ธ Node.js ๊ตฌํ์
๋๋ค.
const dns = require('dns');
const net = require('net');
async function verifyEmailSMTP(email) {
const domain = email.split('@')[1];
// Get MX records
const mxRecords = await new Promise((resolve, reject) => {
dns.resolveMx(domain, (err, addresses) => {
if (err) reject(err);
else resolve(addresses.sort((a, b) => a.priority - b.priority));
});
});
if (!mxRecords || mxRecords.length === 0) {
return { valid: false, reason: 'No MX records found' };
}
// Try each MX server
for (const mx of mxRecords) {
try {
const result = await checkMailbox(mx.exchange, email);
return result;
} catch (err) {
continue; // Try next MX server
}
}
return { valid: null, reason: 'Unable to verify' };
}
function checkMailbox(mxHost, email) {
return new Promise((resolve, reject) => {
const socket = net.createConnection(25, mxHost);
let step = 0;
let response = '';
socket.setTimeout(10000);
socket.on('data', (data) => {
response = data.toString();
const code = parseInt(response.substring(0, 3));
switch (step) {
case 0: // Server greeting
if (code === 220) {
socket.write('EHLO verifier.example.com\r\n');
step++;
} else {
socket.end();
reject(new Error('Server rejected connection'));
}
break;
case 1: // EHLO response
if (code === 250) {
socket.write('MAIL FROM:<verify@example.com>\r\n');
step++;
} else {
socket.end();
reject(new Error('EHLO failed'));
}
break;
case 2: // MAIL FROM response
if (code === 250) {
socket.write(`RCPT TO:<${email}>\r\n`);
step++;
} else {
socket.end();
reject(new Error('MAIL FROM rejected'));
}
break;
case 3: // RCPT TO response - the verification result
socket.write('QUIT\r\n');
socket.end();
if (code === 250) {
resolve({ valid: true, reason: 'Mailbox exists' });
} else if (code >= 500) {
resolve({ valid: false, reason: 'Mailbox does not exist', code });
} else {
resolve({ valid: null, reason: 'Unable to determine', code });
}
break;
}
});
socket.on('timeout', () => {
socket.end();
reject(new Error('Connection timeout'));
});
socket.on('error', (err) => {
reject(err);
});
});
}
Python ๊ตฌํ
Python์ smtplib ๋ชจ๋์ ์ฌ์ฉํ์ฌ ๊น๋ํ SMTP ๊ฒ์ฆ์ ์ ๊ณตํฉ๋๋ค.
import dns.resolver
import smtplib
import socket
def verify_email_smtp(email):
domain = email.split('@')[1]
# Get MX records
try:
mx_records = dns.resolver.resolve(domain, 'MX')
mx_hosts = sorted([(r.preference, str(r.exchange).rstrip('.'))
for r in mx_records])
except dns.resolver.NXDOMAIN:
return {'valid': False, 'reason': 'Domain does not exist'}
except dns.resolver.NoAnswer:
return {'valid': False, 'reason': 'No MX records found'}
# Try each MX server
for priority, mx_host in mx_hosts:
try:
result = check_mailbox(mx_host, email)
if result['valid'] is not None:
return result
except Exception as e:
continue
return {'valid': None, 'reason': 'Unable to verify'}
def check_mailbox(mx_host, email):
try:
# Connect to SMTP server
smtp = smtplib.SMTP(timeout=10)
smtp.connect(mx_host, 25)
# EHLO
code, message = smtp.ehlo('verifier.example.com')
if code != 250:
smtp.quit()
return {'valid': None, 'reason': 'EHLO failed'}
# MAIL FROM
code, message = smtp.mail('verify@example.com')
if code != 250:
smtp.quit()
return {'valid': None, 'reason': 'MAIL FROM rejected'}
# RCPT TO - the verification step
code, message = smtp.rcpt(email)
smtp.quit()
if code == 250:
return {'valid': True, 'reason': 'Mailbox exists'}
elif code >= 500:
return {'valid': False, 'reason': 'Mailbox does not exist', 'code': code}
else:
return {'valid': None, 'reason': 'Temporary failure', 'code': code}
except socket.timeout:
return {'valid': None, 'reason': 'Connection timeout'}
except smtplib.SMTPServerDisconnected:
return {'valid': None, 'reason': 'Server disconnected'}
except Exception as e:
return {'valid': None, 'reason': str(e)}
SMTP ์ด๋ฉ์ผ ๊ฒ์ฆ์ ๊ณผ์
SMTP ๊ฒ์ฆ์ ๊ฐ๋ ฅํ์ง๋ง, ์ฌ๋ฌ ๊ฐ์ง ๊ณผ์ ๊ฐ ๊ตฌํ์ ๋ณต์กํ๊ฒ ๋ง๋ค๊ณ ์ ํ์ฑ์ ์ํฅ์ ๋ฏธ์น ์ ์์ต๋๋ค.
์บ์น์ฌ ๋๋ฉ์ธ
์ผ๋ถ ๋ฉ์ผ ์๋ฒ๋ ์บ์น์ฌ๋ก ๊ตฌ์ฑ๋์ด ์์ด ํน์ ๋ฉ์ผํจ์ด ์กด์ฌํ๋์ง ์ฌ๋ถ์ ๊ด๊ณ์์ด ๋๋ฉ์ธ์ ๋ชจ๋ ์ฃผ์๋ก ๋ฉ์ผ์ ์๋ฝํฉ๋๋ค. ๋ฌด์์ ๋ฌธ์๋ฅผ ํฌํจํ ๋ชจ๋ ์ฃผ์์ ๋ํด RCPT TO๋ฅผ ๋ณด๋ด๋ ์๋ฒ๋ 250 OK๋ก ์๋ตํฉ๋๋ค.
์บ์น์ฌ ๊ตฌ์ฑ์ผ๋ก ์ธํด SMTP ๊ฒ์ฆ์ ํด๋น ๋๋ฉ์ธ์์ ์ ํจํ ์ฃผ์์ ์ ํจํ์ง ์์ ์ฃผ์๋ฅผ ๊ตฌ๋ณํ ์ ์์ต๋๋ค. BillionVerify์ ๊ฐ์ ์ ๋ฌธ ์ด๋ฉ์ผ ๊ฒ์ฆ ์๋น์ค๋ ์ด๋ฌํ ๋๋ฉ์ธ์ ์๋ณํ๊ณ ์ ์ ํ ์ ๋ขฐ๋ ์ ์๋ฅผ ์ ๊ณตํ๊ธฐ ์ํด ํน์ํ ์บ์น์ฌ ๊ฐ์ง ์๊ณ ๋ฆฌ์ฆ์ ๊ตฌํํฉ๋๋ค.
๊ทธ๋ ์ด๋ฆฌ์คํ
๊ทธ๋ ์ด๋ฆฌ์คํ ์ ๋ฉ์ผ ์๋ฒ๊ฐ ์๋ ค์ง์ง ์์ ๋ฐ์ ์์ ์ด๋ฉ์ผ์ ์์๋ก ๊ฑฐ๋ถํ๋ ์คํธ ๋ฐฉ์ง ๊ธฐ์ ์ ๋๋ค. ์ฒซ ๋ฒ์งธ SMTP ์ฐ๊ฒฐ ์๋๋ 4xx ์์ ์ค๋ฅ๋ฅผ ๋ฐํํฉ๋๋ค. ํฉ๋ฒ์ ์ธ ๋ฉ์ผ ์๋ฒ๋ ์ ๋ฌ์ ์ฌ์๋ํ์ง๋ง ๋ง์ ์คํธ ์์คํ ์ ๊ทธ๋ ์ง ์์ต๋๋ค.
์ด๋ฉ์ผ ๊ฒ์ฆ์ ๊ฒฝ์ฐ ๊ทธ๋ ์ด๋ฆฌ์คํ ์ ์์ ์คํจ๋ก ๋ํ๋ฉ๋๋ค. ์ ์ ํ ๊ตฌํ์๋ ๋ค์์ด ํ์ํฉ๋๋ค.
- ๊ทธ๋ ์ด๋ฆฌ์คํ ์๋ต ์ธ์ (์ฃผ๋ก 450 ๋๋ 451)
- ์ ์ ํ ์ง์ฐ ์๊ฐ์ ๊ฐ์ง ์ฌ์๋ ๋ก์ง ๊ตฌํ
- ๊ทธ๋ ์ด๋ฆฌ์คํ ์ ์ฌ์ฉํ๋ ์๋ฒ ์ถ์
์๋ ์ ํ ๋ฐ ์ฐจ๋จ
๋ฉ์ผ ์๋ฒ๋ ์ฐ๊ฒฐ์ ์๋ ์ ํํ์ฌ ๋จ์ฉ์ผ๋ก๋ถํฐ ์ค์ค๋ก๋ฅผ ๋ณดํธํฉ๋๋ค. ๋จ์ผ IP ์ฃผ์์์ ์งง์ ์๊ฐ์ ๋๋ฌด ๋ง์ ๊ฒ์ฆ ์๋๋ฅผ ํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
- ์์ ์ฐจ๋จ (4xx ์๋ต)
- ์๊ตฌ ๋ธ๋๋ฆฌ์คํธ ๋ฑ๋ก
- ์ฐ๊ฒฐ ์๊ฐ ์ด๊ณผ
- CAPTCHA ๋๋ ์ฑ๋ฆฐ์ง
์ ๋ฌธ ์ด๋ฉ์ผ ๊ฒ์ฆ ์๋น์ค๋ ๋ง์ IP ์ฃผ์์ ๊ฒ์ฆ ์์ฒญ์ ๋ถ์ฐํ๊ณ ์ด๋ฌํ ๋ณดํธ ์กฐ์น๋ฅผ ํธ๋ฆฌ๊ฑฐํ์ง ์๋๋ก ์ ๊ตํ ์๋ ์ ํ์ ๊ตฌํํฉ๋๋ค.
๊ฑฐ์ง ์์ฑ ๋ฐ ๊ฑฐ์ง ์์ฑ
SMTP ๊ฒ์ฆ์ 100% ์ ํํ์ง ์์ต๋๋ค. ์ฌ๋ฌ ์๋๋ฆฌ์ค์์ ์๋ชป๋ ๊ฒฐ๊ณผ๊ฐ ์์ฑ๋ ์ ์์ต๋๋ค.
๊ฑฐ์ง ์์ฑ (์ ํจํ์ง ์์ ๊ฒ์ ์ ํจํ ๊ฒ์ผ๋ก ๋ณด๊ณ )
- ๋ชจ๋ ๊ฒ์ ์๋ฝํ๋ ์บ์น์ฌ ๋๋ฉ์ธ
- SMTP ์ค์๋ ์๋ฝํ์ง๋ง ๋์ค์ ๋ฐ์กํ๋ ์๋ฒ
- ์ฌ์ ํ ์ฐ๊ฒฐ์ ์๋ฝํ๋ ๊ฐ๋ ์ฐฌ ๋ฉ์ผํจ
๊ฑฐ์ง ์์ฑ (์ ํจํ ๊ฒ์ ์ ํจํ์ง ์์ ๊ฒ์ผ๋ก ๋ณด๊ณ )
- ์ฒซ ๋ฒ์งธ ์๋๋ฅผ ๊ฑฐ๋ถํ๋ ๊ทธ๋ ์ด๋ฆฌ์คํ
- ํฉ๋ฒ์ ์ธ ํ์ธ์ ์ฐจ๋จํ๋ ์๋ ์ ํ
- ์๋ฒ ๊ตฌ์ฑ ์ค๋ฅ
- ์์ ์ค๋จ
SMTP ์๋ฒ ๋ณํ
๋ค๋ฅธ ๋ฉ์ผ ์๋ฒ๋ค์ ๊ฒ์ฆ์ ์ํฅ์ ๋ฏธ์น๋ ๋ค์ํ ๋ฐฉ์์ผ๋ก SMTP๋ฅผ ๊ตฌํํฉ๋๋ค.
Microsoft Exchange/Office 365
- ์์ธํ ์๋ต์ ์ํด ์ธ์ฆ์ด ํ์ํ ๊ฒฝ์ฐ๊ฐ ๋ง์
- SMTP ์ค์๋ ์๋ฝํ์ง๋ง ๋์ค์ ์ ๋ฌ์ ๊ฑฐ๋ถํ ์ ์์
- ์ ๊ตํ ์คํธ ๋ฐฉ์ง ์กฐ์น ๊ตฌํ
Gmail/Google Workspace
- ์ผ๋ฐ์ ์ผ๋ก ์๋ฝ/๊ฑฐ๋ถ์ ๋ํด ์ ๋ขฐํ ์ ์์
- ๊ณต๊ฒฉ์ ์ธ ๊ฒ์ฆ ์๋๋ฅผ ์๋ ์ ํํ ์ ์์
- ์ผ๊ด๋ ์๋ต ๋ฐํ
Yahoo Mail
- ์๊ฒฉํ ์๋ ์ ํ์ผ๋ก ์๋ ค์ง
- ์ฑ๋ฆฐ์ง ํด๊ฒฐ์ด ํ์ํ ์ ์์
- ๊ทธ๋ ์ด๋ฆฌ์คํ ๊ตฌํ
์ปค์คํ ๋ฉ์ผ ์๋ฒ
- ๋์์ด ํฌ๊ฒ ๋ค๋ฆ
- ๋นํ์ค ๊ตฌ์ฑ์ ๊ฐ์ง ์ ์์
- ๋ณด์ ์ค์ ์ด ๊ฒ์ฆ ์ ํ๋์ ์ํฅ์ ๋ฏธ์นจ
SMTP ์ด๋ฉ์ผ ๊ฒ์ฆ ๋ชจ๋ฒ ์ฌ๋ก
์ ๋ขฐํ ์ ์๋ SMTP ๊ฒ์ฆ์ ๊ตฌ์ถํ๋ ค๋ฉด ๊ฒ์ฆ๋ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๋ฐ๋ผ์ผ ํฉ๋๋ค.
์ ์ ํ EHLO/HELO ๊ตฌ์ฑ
EHLO ํธ์คํธ๋ช ์ ๋ค์๊ณผ ๊ฐ์์ผ ํฉ๋๋ค.
- ๊ฒ์ฆ ์๋ฒ์ IP๋ก ํด์๋จ
- ์ ํจํ ์ญ๋ฐฉํฅ DNS (PTR ๋ ์ฝ๋) ๋ณด์
- ๋ธ๋๋ฆฌ์คํธ์ ํฌํจ๋์ง ์์
- ์ ์ดํ๋ ํฉ๋ฒ์ ์ธ ๋๋ฉ์ธ
EHLO verify.yourdomain.com
์คํธ ํํฐ๋ฅผ ํธ๋ฆฌ๊ฑฐํ๋ ์ผ๋ฐ์ ์ด๊ฑฐ๋ ์์ฌ์ค๋ฌ์ด ํธ์คํธ๋ช ์ ํผํ์ธ์.
MAIL FROM ์ฃผ์ ์ ํ
MAIL FROM ์ฃผ์๋ ๊ฒ์ฆ ์๋ฝ์ ์ค์ํฉ๋๋ค.
- ์ ํจํ MX ๋ ์ฝ๋๊ฐ ์๋ ์ค์ ๋๋ฉ์ธ ์ฌ์ฉ
- SPF ๋ ์ฝ๋๊ฐ ๊ฒ์ฆ ์๋ฒ๋ฅผ ํ์ฉํ๋์ง ํ์ธ
- ๊ฒ์ฆ ์ ์ฉ ๋๋ฉ์ธ ์ฌ์ฉ ๊ณ ๋ ค
- ์๋ ค์ง ์คํธ ํธ๋ฉ ๋๋ฉ์ธ ํผํ๊ธฐ
์ฐ๊ฒฐ ๊ด๋ฆฌ
ํจ์จ์ ์ธ ์ฐ๊ฒฐ ๊ด๋ฆฌ๋ ๊ฒ์ฆ ์๋์ ์ ๋ขฐ์ฑ์ ํฅ์์ํต๋๋ค.
// Connection pooling example
class SMTPConnectionPool {
constructor(maxConnections = 10) {
this.pools = new Map(); // Domain -> connections
this.maxConnections = maxConnections;
}
async getConnection(mxHost) {
if (!this.pools.has(mxHost)) {
this.pools.set(mxHost, []);
}
const pool = this.pools.get(mxHost);
// Reuse existing connection if available
if (pool.length > 0) {
return pool.pop();
}
// Create new connection
return await this.createConnection(mxHost);
}
releaseConnection(mxHost, connection) {
const pool = this.pools.get(mxHost);
if (pool && pool.length < this.maxConnections) {
pool.push(connection);
} else {
connection.end();
}
}
}
ํ์์์ ๊ตฌ์ฑ
์๋ตํ์ง ์๋ ์๋ฒ์์ ์ค๋จ๋๋ ๊ฒ์ ํผํ๊ธฐ ์ํด ์ ์ ํ ํ์์์์ ์ค์ ํ์ธ์.
const TIMEOUT_CONFIG = {
connection: 10000, // 10 seconds to establish connection
greeting: 30000, // 30 seconds for server greeting
command: 30000, // 30 seconds per command response
total: 60000 // 60 seconds maximum per verification
};
์ค๋ฅ ์ฒ๋ฆฌ ๋ฐ ์ฌ์๋ ๋ก์ง
์ง๋ฅ์ ์ธ ์ฌ์๋๋ฅผ ํตํ ๊ฐ๋ ฅํ ์ค๋ฅ ์ฒ๋ฆฌ๋ฅผ ๊ตฌํํ์ธ์.
async function verifyWithRetry(email, maxRetries = 3) {
const delays = [1000, 5000, 15000]; // Exponential backoff
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const result = await verifyEmailSMTP(email);
// Don't retry if we got a definitive answer
if (result.valid !== null) {
return result;
}
// Check if error is retryable
if (isGreylisting(result) || isTemporaryError(result)) {
await sleep(delays[attempt]);
continue;
}
return result;
} catch (err) {
if (attempt === maxRetries - 1) {
return { valid: null, reason: err.message };
}
await sleep(delays[attempt]);
}
}
}
function isGreylisting(result) {
return result.code === 450 || result.code === 451;
}
function isTemporaryError(result) {
return result.code >= 400 && result.code < 500;
}
์๋ ์ ํ ๊ตฌํ
๊ฒ์ฆ ์ธํ๋ผ๋ฅผ ๋ณดํธํ๊ณ ์ข์ ํํ์ ์ ์งํ์ธ์.
class RateLimiter {
constructor() {
this.domainLimits = new Map();
this.globalCounter = 0;
this.globalLimit = 100; // per second
this.domainLimit = 10; // per second per domain
}
async waitForSlot(domain) {
// Check global limit
while (this.globalCounter >= this.globalLimit) {
await sleep(100);
}
// Check domain limit
const domainCount = this.domainLimits.get(domain) || 0;
while (domainCount >= this.domainLimit) {
await sleep(100);
}
// Reserve slot
this.globalCounter++;
this.domainLimits.set(domain, domainCount + 1);
// Release after 1 second
setTimeout(() => {
this.globalCounter--;
this.domainLimits.set(domain,
(this.domainLimits.get(domain) || 1) - 1);
}, 1000);
}
}
SMTP ๊ฒ์ฆ๊ณผ ๋ค๋ฅธ ๋ฐฉ๋ฒ ๋น๊ต
SMTP ๊ฒ์ฆ์ด ๋ค๋ฅธ ์ด๋ฉ์ผ ์ ํจ์ฑ ๊ฒ์ฌ ๊ธฐ์ ๊ณผ ์ด๋ป๊ฒ ๋น๊ต๋๋์ง ์ดํดํ๋ฉด ์ฌ๋ฐ๋ฅธ ์ ๊ทผ ๋ฐฉ์์ ์ ํํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
๊ตฌ๋ฌธ ์ ํจ์ฑ ๊ฒ์ฌ
๊ตฌ๋ฌธ ์ ํจ์ฑ ๊ฒ์ฌ๋ ์ ๊ท์ ํจํด์ ์ฌ์ฉํ์ฌ ์ด๋ฉ์ผ์ด ์ฌ๋ฐ๋ฅธ ํ์์ ๋ฐ๋ฅด๋์ง ํ์ธํฉ๋๋ค. ๋น ๋ฅด๊ณ ํด๋ผ์ด์ธํธ ์ธก์์ ์ํํ ์ ์์ง๋ง ๋ช ๋ฐฑํ ํ์ ์ค๋ฅ๋ง ์ก์๋ ๋๋ค.
์ฅ์ :
- ์ฆ๊ฐ์ ์ธ ๊ฒฐ๊ณผ
- ๋คํธ์ํฌ ์์ฒญ ์์
- ์คํ ๊ฐ์ง
์ ํ์ฌํญ:
- ์กด์ฌ ์ฌ๋ถ๋ฅผ ํ์ธํ ์ ์์
- ๋ง์ ์ ํจํ์ง ์์ ์ด๋ฉ์ผ์ด ๊ตฌ๋ฌธ ๊ฒ์ฌ๋ฅผ ํต๊ณผํจ
๋๋ฉ์ธ/MX ๊ฒ์ฆ
MX ๋ ์ฝ๋ ๊ฒ์ฆ์ ๋ฉ์ผ ์๋ฒ ๋ ์ฝ๋๋ฅผ ํ์ธํ์ฌ ๋๋ฉ์ธ์ด ์ด๋ฉ์ผ์ ๋ฐ์ ์ ์๋์ง ํ์ธํฉ๋๋ค.
์ฅ์ :
- ์กด์ฌํ์ง ์๋ ๋๋ฉ์ธ ๊ฐ์ง
- ๋น ๋ฅธ DNS ์กฐํ
- SMTP ์ฐ๊ฒฐ ๋ถํ์
์ ํ์ฌํญ:
- ํน์ ๋ฉ์ผํจ์ ํ์ธํ ์ ์์
- ๋๋ฉ์ธ์ MX๊ฐ ์์ง๋ง ์ ํจํ ์ฌ์ฉ์๊ฐ ์์ ์ ์์
SMTP ๊ฒ์ฆ
SMTP ๊ฒ์ฆ์ ํน์ ๋ฉ์ผํจ์ด ์กด์ฌํ๊ณ ๋ฉ์ผ์ ๋ฐ์ ์ ์๋์ง ํ์ธํฉ๋๋ค.
์ฅ์ :
- ๋ฉ์ผํจ ์กด์ฌ์ ๋ํ ์ต๊ณ ์ ํ๋
- ๋ฉ์ผ ์๋ฒ์์ ์ง์ ํต์
- ๋ง์ ์ ํจํ์ง ์์ ์ฃผ์ ๊ฐ์ง
์ ํ์ฌํญ:
- ๋ค๋ฅธ ๋ฐฉ๋ฒ๋ณด๋ค ๋๋ฆผ
- ์บ์น์ฌ ๋๋ฉ์ธ์ ์ํฅ์ ๋ฐ์
- ์๋ ์ ํ์ผ๋ก ์ฐจ๋จ๋ ์ ์์
๊ฒ์ฆ ๊ณ์ธต ๊ตฌ์กฐ
ํฌ๊ด์ ์ธ ์ด๋ฉ์ผ ์ ํจ์ฑ ๊ฒ์ฌ ์ ๋ต์ ์ด๋ฌํ ๋ฐฉ๋ฒ์ ๊ณ์ธตํํฉ๋๋ค.
- ๊ตฌ๋ฌธ ์ ํจ์ฑ ๊ฒ์ฌ - ๋ช ๋ฐฑํ ์ ํจํ์ง ์์ ํ์ ํํฐ๋ง
- ๋๋ฉ์ธ ๊ฒ์ฆ - ๋๋ฉ์ธ์ด ์กด์ฌํ๊ณ MX ๋ ์ฝ๋๊ฐ ์๋์ง ํ์ธ
- SMTP ๊ฒ์ฆ - ํน์ ๋ฉ์ผํจ ํ์ธ
- ์ถ๊ฐ ํ์ธ - ์ผํ์ฉ ์ด๋ฉ์ผ ๊ฐ์ง, ์ญํ ๊ธฐ๋ฐ ๊ฐ์ง, ์บ์น์ฌ ๊ฐ์ง
BillionVerify์ ๊ฐ์ ์ ๋ฌธ ์ด๋ฉ์ผ ๊ฒ์ฆ ์๋น์ค๋ ์ด๋ฌํ ์์ ํ ๊ณ์ธต ๊ตฌ์กฐ๋ฅผ ๊ตฌํํ์ฌ SMTP ๊ฒ์ฆ์ ๋ณต์ก์ฑ์ ์ฒ๋ฆฌํ๋ฉด์ ์ด๋ฉ์ผ ํ์ง์ ๋ํ ์ถ๊ฐ ์ธํ ๋ฆฌ์ ์ค๋ฅผ ์ ๊ณตํฉ๋๋ค.
์ ๋ฌธ SMTP ๊ฒ์ฆ ์๋น์ค ์ฌ์ฉํ๊ธฐ
์์ฒด SMTP ๊ฒ์ฆ ์์คํ ์ ๊ตฌ์ถํ๋ ๊ฒ์ ๊ต์ก์ ์ด์ง๋ง, ํ๋ก๋์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ข ์ข ๋ณต์ก์ฑ์ ์ฒ๋ฆฌํ๋ ์ ๋ฌธ ์ด๋ฉ์ผ ๊ฒ์ฆ API์ ์ด์ ์ ๋๋ฆฝ๋๋ค.
์ ๋ฌธ ์๋น์ค์ ์ด์
์ธํ๋ผ
- ์ ์ธ๊ณ์ ๋ถ์ฐ๋ ๊ฒ์ฆ ์๋ฒ
- ๊นจ๋ํ IP ํํ ๊ด๋ฆฌ
- ๋์ ๊ฐ์ฉ์ฑ ๋ฐ ์ค๋ณต์ฑ
์ธํ ๋ฆฌ์ ์ค
- ์บ์น์ฌ ๋๋ฉ์ธ ๊ฐ์ง
- ์ผํ์ฉ ์ด๋ฉ์ผ ์๋ณ
- ์ญํ ๊ธฐ๋ฐ ์ฃผ์ ํ๋๊ทธ ์ง์
- ์คํธ ํธ๋ฉ ๊ฐ์ง
๊ท์ ์ค์
- ๊ฐ์ธ์ ๋ณด ๋ณดํธ ์ค์ ์ฒ๋ฆฌ
- ์์ ํ ๋ฐ์ดํฐ ์ฒ๋ฆฌ
- ๊ฐ์ฌ ์ถ์
BillionVerify API ํตํฉ
BillionVerify๋ SMTP ํ์ธ์ ํฌํจํ ํฌ๊ด์ ์ธ ์ด๋ฉ์ผ ๊ฒ์ฆ์ ์ ๊ณตํฉ๋๋ค.
async function verifyWithBillionVerify(email) {
const response = await fetch('https://api.billionverify.com/v1/verify', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({ email })
});
const result = await response.json();
return {
isValid: result.is_valid,
isDeliverable: result.is_deliverable,
isCatchAll: result.is_catch_all,
isDisposable: result.is_disposable,
isRoleBased: result.is_role_based,
smtpCheck: result.smtp_check,
mxRecords: result.mx_records,
riskScore: result.risk_score
};
}
API๋ ๋ชจ๋ SMTP ๋ณต์ก์ฑ์ ๋ด๋ถ์ ์ผ๋ก ์ฒ๋ฆฌํ๋ฉด์ ๋ณต์ ํ๊ธฐ ์ํด ์๋นํ ์ธํ๋ผ๊ฐ ํ์ํ ์ถ๊ฐ ์ธํ ๋ฆฌ์ ์ค๋ฅผ ์ ๊ณตํฉ๋๋ค.
๋๋ SMTP ๊ฒ์ฆ
๋๋์ ์ด๋ฉ์ผ ๋ชฉ๋ก์ ๊ฒ์ฆํ๋ ๊ฒฝ์ฐ, ๋๋ ๊ฒ์ฆ์ ํ๋ก์ธ์ค๋ฅผ ์ต์ ํํฉ๋๋ค.
async function bulkVerify(emails) {
// Upload file for batch processing
const formData = new FormData();
formData.append('file', createCSV(emails));
const uploadResponse = await fetch('https://api.billionverify.com/v1/bulk/upload', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY'
},
body: formData
});
const { jobId } = await uploadResponse.json();
// Poll for completion
let status = 'processing';
while (status === 'processing') {
await sleep(5000);
const statusResponse = await fetch(
`https://api.billionverify.com/v1/bulk/status/${jobId}`,
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const job = await statusResponse.json();
status = job.status;
}
// Download results
const resultsResponse = await fetch(
`https://api.billionverify.com/v1/bulk/download/${jobId}`,
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
return await resultsResponse.json();
}
๊ฐ์ ์์์ ์ํ ์ค์๊ฐ SMTP ๊ฒ์ฆ
์ฌ์ฉ์ ๋ฑ๋ก ์ค ์ค์๊ฐ ์ด๋ฉ์ผ ๊ฒ์ฆ์ ๊ตฌํํ๋ฉด ์ฒ์๋ถํฐ ๋ฐ์ดํฐ ํ์ง์ด ํฅ์๋ฉ๋๋ค.
ํ๋ก ํธ์๋ ๊ตฌํ
// Debounced email verification on input
const emailInput = document.getElementById('email');
let verificationTimeout;
emailInput.addEventListener('input', (e) => {
clearTimeout(verificationTimeout);
const email = e.target.value;
if (!isValidSyntax(email)) {
showError('Please enter a valid email format');
return;
}
verificationTimeout = setTimeout(async () => {
showLoading();
try {
const result = await verifyEmail(email);
if (result.isValid) {
showSuccess('Email verified');
} else if (result.isCatchAll) {
showWarning('Unable to fully verify this email');
} else {
showError('This email address appears invalid');
}
} catch (err) {
// Don't block signup on verification errors
clearStatus();
}
}, 500); // Wait 500ms after typing stops
});
async function verifyEmail(email) {
const response = await fetch('/api/verify-email', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email })
});
return response.json();
}
๋ฐฑ์๋ API ์๋ํฌ์ธํธ
// Express.js endpoint
app.post('/api/verify-email', async (req, res) => {
const { email } = req.body;
// Quick syntax check first
if (!isValidEmailSyntax(email)) {
return res.json({ isValid: false, reason: 'Invalid syntax' });
}
try {
// Call BillionVerify API for full verification
const result = await emailVerify.verify(email);
res.json({
isValid: result.is_valid && result.is_deliverable,
isCatchAll: result.is_catch_all,
isDisposable: result.is_disposable,
suggestion: result.did_you_mean // Typo suggestions
});
} catch (err) {
// Fail open - don't block signup on API errors
res.json({ isValid: true, verified: false });
}
});
SMTP ๊ฒ์ฆ์ ์ํ ๋ณด์ ๊ณ ๋ ค์ฌํญ
SMTP ๊ฒ์ฆ์ ๋ณด์ ์ธ์์ด ํ์ํ ๋คํธ์ํฌ ์ฐ๊ฒฐ์ ํฌํจํฉ๋๋ค.
์ธํ๋ผ ๋ณดํธ
๋ฐฉํ๋ฒฝ ๊ตฌ์ฑ
- ๊ฒ์ฆ ์๋ฒ์์ ์์๋ฐ์ด๋ SMTP ์ฐ๊ฒฐ๋ง ํ์ฉ
- ๋น์ ์์ ์ธ ์ฐ๊ฒฐ ํจํด ๋ชจ๋ํฐ๋ง
- ์๋ ค์ง ์ ์ฑ IP ๋ฒ์ ์ฐจ๋จ
TLS/SSL ์ฌ์ฉ
- ์ฌ์ฉ ๊ฐ๋ฅํ ๊ฒฝ์ฐ STARTTLS ์ฌ์ฉ
- ์๋ฒ ์ธ์ฆ์ ํ์ธ
- ์ธ์ฆ์ ์ค๋ฅ๋ฅผ ์ ์์ ์ผ๋ก ์ฒ๋ฆฌ
๋ธ๋๋ฆฌ์คํธ ๋ฑ๋ก ๋ฐฉ์ง
๊ฒ์ฆ ์๋ฒ๊ฐ ์คํธ์ ๋ณด๋ด๊ฑฐ๋ ๋ฉ์ผ ์๋ฒ๋ฅผ ๋จ์ฉํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ด๋ฉด ๋ธ๋๋ฆฌ์คํธ์ ๋ฑ๋ก๋ ์ ์์ต๋๋ค.
- ์๊ฒฉํ ์๋ ์ ํ ๊ตฌํ
- ๊ฒ์ฆ ์ ์ฉ IP ์ฌ์ฉ
- ์ ๊ธฐ์ ์ผ๋ก ๋ธ๋๋ฆฌ์คํธ ์ํ ๋ชจ๋ํฐ๋ง
- ์ ์ ํ ์ญ๋ฐฉํฅ DNS ์ ์ง
- ๋จ์ฉ ๋ถ๋ง์ ์ ์ํ๊ฒ ๋์
๋ฐ์ดํฐ ํ๋ผ์ด๋ฒ์
์ด๋ฉ์ผ ์ฃผ์๋ ๋ณดํธ๊ฐ ํ์ํ ๊ฐ์ธ ๋ฐ์ดํฐ์ ๋๋ค.
- ๋ถํ์ํ๊ฒ ์ ์ฒด ์ด๋ฉ์ผ ์ฃผ์๋ฅผ ๊ธฐ๋กํ์ง ์์
- ์ ์ฅ๋ ๊ฒ์ฆ ๊ฒฐ๊ณผ ์ํธํ
- ๋ฐ์ดํฐ ๋ณด์กด ์ ์ฑ ๊ตฌํ
- GDPR ๋ฐ ๊ธฐํ ๊ท์ ์ค์
- API ํธ์ถ์ ์์ ํ ์ฐ๊ฒฐ ์ฌ์ฉ
SMTP ๊ฒ์ฆ ์ฑ๋ฅ ์ธก์
๊ฒ์ฆ ์์คํ ์ด ์ ์๋ํ๋์ง ํ์ธํ๊ธฐ ์ํด ์ฃผ์ ๋ฉํธ๋ฆญ์ ์ถ์ ํ์ธ์.
์ฃผ์ ๋ฉํธ๋ฆญ
์ ํ๋ ๋ฉํธ๋ฆญ
- ์ง์์ฑ๋ฅ (์ ํจํ ๊ฒ์ด ์ฌ๋ฐ๋ฅด๊ฒ ์๋ณ๋จ)
- ๊ฑฐ์ง ์์ฑ๋ฅ (์ ํจํ์ง ์์ ๊ฒ์ด ์ ํจํ ๊ฒ์ผ๋ก ํ์๋จ)
- ์บ์น์ฌ ๊ฐ์ง ์ ํ๋
- ์ ์ ์์/ํ์ธํ ์ ์์ ๋น์จ
์ฑ๋ฅ ๋ฉํธ๋ฆญ
- ํ๊ท ๊ฒ์ฆ ์๊ฐ
- 95๋ฒ์งธ ๋ฐฑ๋ถ์์ ์๋ต ์๊ฐ
- ์ฐ๊ฒฐ ์ฑ๊ณต๋ฅ
- ๋๋ฉ์ธ๋ณ ํ์์์ ๋น์จ
์ด์ ๋ฉํธ๋ฆญ
- ์ผ์ผ ๊ฒ์ฆ ๋ณผ๋ฅจ
- ์ ํ๋ณ ์ค๋ฅ์จ
- ๋ธ๋๋ฆฌ์คํธ ์ฌ๊ณ
- API ๊ฐ์ฉ์ฑ
๋ชจ๋ํฐ๋ง ๋์๋ณด๋ ์์
class VerificationMetrics {
constructor() {
this.counters = {
total: 0,
valid: 0,
invalid: 0,
catchAll: 0,
unknown: 0,
errors: 0
};
this.timings = [];
}
record(result, duration) {
this.counters.total++;
this.timings.push(duration);
if (result.valid === true) this.counters.valid++;
else if (result.valid === false) this.counters.invalid++;
else if (result.isCatchAll) this.counters.catchAll++;
else this.counters.unknown++;
}
recordError() {
this.counters.errors++;
}
getStats() {
const sortedTimings = this.timings.sort((a, b) => a - b);
return {
counts: this.counters,
accuracy: {
validRate: this.counters.valid / this.counters.total,
unknownRate: this.counters.unknown / this.counters.total
},
performance: {
avgTime: average(this.timings),
p95Time: sortedTimings[Math.floor(sortedTimings.length * 0.95)],
errorRate: this.counters.errors / this.counters.total
}
};
}
}
๊ฒฐ๋ก
SMTP ์ด๋ฉ์ผ ๊ฒ์ฆ์ ์ด๋ฉ์ผ ์ฃผ์๊ฐ ๋ฉ์์ง๋ฅผ ์์ ํ ์ ์๋์ง ํ์ธํ๋ ๊ฐ์ฅ ์ ํํ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค. SMTP ํ๋กํ ์ฝ์ ์ฌ์ฉํ์ฌ ๋ฉ์ผ ์๋ฒ์ ์ง์ ํต์ ํจ์ผ๋ก์จ ์ค์ ์ด๋ฉ์ผ์ ๋ณด๋ด์ง ์๊ณ ๋ ๋ฉ์ผํจ์ ์กด์ฌ๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
ํจ๊ณผ์ ์ธ SMTP ๊ฒ์ฆ์ ๊ตฌ์ถํ๋ ค๋ฉด ํ๋กํ ์ฝ ์ธ๋ถ ์ฌํญ์ ์ดํดํ๊ณ , ์บ์น์ฌ ๋๋ฉ์ธ ๋ฐ ๊ทธ๋ ์ด๋ฆฌ์คํ ๊ณผ ๊ฐ์ ๋ค์ํ ๊ณผ์ ๋ฅผ ์ฒ๋ฆฌํ๋ฉฐ, ์ ์ ํ ์๋ ์ ํ ๋ฐ ์ค๋ฅ ์ฒ๋ฆฌ๋ฅผ ๊ตฌํํด์ผ ํฉ๋๋ค. ๋๋ถ๋ถ์ ํ๋ก๋์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฒฝ์ฐ, BillionVerify์ ๊ฐ์ ์ ๋ฌธ ์ด๋ฉ์ผ ๊ฒ์ฆ ์๋น์ค๋ ๊ฒ์ฆ ์ธํ๋ผ๋ฅผ ๊ตฌ์ถํ๊ณ ์ ์ง ๊ด๋ฆฌํ๋ ๋ณต์ก์ฑ ์์ด ํ์ํ ์ธํ๋ผ, ์ธํ ๋ฆฌ์ ์ค ๋ฐ ์ ๋ขฐ์ฑ์ ์ ๊ณตํฉ๋๋ค.
ํ์ต ๋ชฉ์ ์ผ๋ก ์์ฒด SMTP ๊ฒ์ฆ์ ๊ตฌํํ๋ ์ ๋ฌธ ์ด๋ฉ์ผ ๊ฒ์ฆ API๋ฅผ ํตํฉํ๋ , ์ด ๊ฐ์ด๋์์ ๋ค๋ฃฌ ์์น์ ๋๊ท๋ชจ๋ก ์ด๋ฉ์ผ ์ฃผ์๋ฅผ ํ์ธํ ๋ ๋ฌด์จ ์ผ์ด ์ผ์ด๋๋์ง ์ดํดํ๋ ๋ฐ ๋์์ด ๋ ๊ฒ์ ๋๋ค.
SMTP ๊ฒ์ฆ์ ํฌ๊ด์ ์ธ ์ด๋ฉ์ผ ์ ํจ์ฑ ๊ฒ์ฌ์ ํ ๊ตฌ์ฑ ์์์ผ ๋ฟ์ด๋ผ๋ ์ ์ ๊ธฐ์ตํ์ธ์. ๊ตฌ๋ฌธ ์ ํจ์ฑ ๊ฒ์ฌ, ๋๋ฉ์ธ ๊ฒ์ฆ, ์ผํ์ฉ ์ด๋ฉ์ผ ๊ฐ์ง ๋ฐ ์บ์น์ฌ ์๋ณ๊ณผ ๊ฒฐํฉํ๋ฉด ๋ฐ์ ์ ํํ์ ๋ณดํธํ๊ณ , ์ด๋ฉ์ผ ์ ๋ฌ๋ฅ ์ ๊ฐ์ ํ๋ฉฐ, ์ด๋ฉ์ผ ๋ชฉ๋ก์ ํ์ง์ ์ ์งํ๋ ์์ ํ ์ด๋ฉ์ผ ๊ฒ์ฆ ์ ๋ต์ด ๋ง๋ค์ด์ง๋๋ค.
์ฆ๊ฐ์ ์ธ ํผ๋๋ฐฑ์ ์ํด ๊ธฐ๋ณธ ๊ตฌ๋ฌธ ๋ฐ ๋๋ฉ์ธ ํ์ธ์ผ๋ก ์์ํ๊ณ , ์ฒ ์ ํ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ํด SMTP ๊ฒ์ฆ์ ๊ณ์ธตํํ๋ฉฐ, ์ ์ฉ ์ด๋ฉ์ผ ๊ฒ์ฆ ์ธํ๋ผ์์ ์ ๊ณต๋๋ ์ ๋ขฐ์ฑ, ์ ํ์ฑ ๋ฐ ์ถ๊ฐ ์ธํ ๋ฆฌ์ ์ค๊ฐ ํ์ํ ๋ BillionVerify์ ๊ฐ์ ์ ๋ฌธ ์๋น์ค๋ฅผ ๊ณ ๋ คํ์ธ์.