Google Forms
Email checker for Google Forms. Verify submitted emails with Apps Script integration.
EmailVerify와 Google Forms를 통합하여 응답이 제출될 때 이메일 주소를 자동으로 검증하세요.
통합 방법
| 방법 | 적합 대상 | 복잡도 |
|---|---|---|
| Apps Script | 자동화된 검증 | 낮음 |
| Sheets 부가기능 | 수동 검증 | 낮음 |
| Zapier | 노코드 워크플로 | 낮음 |
방법 1: Google Apps Script
Google Apps Script를 사용하여 자동화된 검증 워크플로를 생성하세요.
단계 1: Apps Script 프로젝트 생성
- Google Form 열기
- ⋮ 메뉴 → 스크립트 편집기 클릭
- Apps Script 편집기가 열립니다
단계 2: 검증 코드 추가
기본 코드를 다음으로 교체하세요:
// Configuration
const CONFIG = {
API_KEY: 'YOUR_EMAILVERIFY_API_KEY',
EMAIL_FIELD_INDEX: 1, // Adjust based on your form (0-indexed)
BLOCK_DISPOSABLE: true,
NOTIFY_ON_INVALID: true,
NOTIFICATION_EMAIL: 'admin@yourcompany.com',
};
/**
* Trigger function - runs when form is submitted
*/
function onFormSubmit(e) {
const response = e.response;
const email = getEmailFromResponse(response);
if (!email) {
Logger.log('No email found in response');
return;
}
// Verify email
const result = verifyEmail(email);
// Store result
storeVerificationResult(response, result);
// Handle invalid emails
if (!result.isValid) {
handleInvalidEmail(response, email, result);
}
}
/**
* Extract email from form response
*/
function getEmailFromResponse(response) {
const itemResponses = response.getItemResponses();
// Try to find email field by type
for (const itemResponse of itemResponses) {
const item = itemResponse.getItem();
const itemType = item.getType();
// Check if it's an email validation text item
if (itemType === FormApp.ItemType.TEXT) {
const textItem = item.asTextItem();
const validation = textItem.getValidation();
// If validation requires email format
if (validation) {
return itemResponse.getResponse();
}
}
}
// Fallback: use configured index
if (CONFIG.EMAIL_FIELD_INDEX < itemResponses.length) {
return itemResponses[CONFIG.EMAIL_FIELD_INDEX].getResponse();
}
return null;
}
/**
* Call EmailVerify API to verify email
*/
function verifyEmail(email) {
const url = 'https://api.emailverify.ai/v1/verify';
const options = {
method: 'post',
contentType: 'application/json',
headers: {
'Authorization': 'Bearer ' + CONFIG.API_KEY,
},
payload: JSON.stringify({ email: email }),
muteHttpExceptions: true,
};
try {
const response = UrlFetchApp.fetch(url, options);
const data = JSON.parse(response.getContentText());
const isValid = data.status === 'valid' &&
!(CONFIG.BLOCK_DISPOSABLE && data.result?.disposable);
return {
email: email,
status: data.status,
score: data.score,
isDisposable: data.result?.disposable || false,
isRole: data.result?.role || false,
isValid: isValid,
raw: data,
};
} catch (error) {
Logger.log('Verification error: ' + error.message);
return {
email: email,
status: 'error',
isValid: true, // Allow on error
error: error.message,
};
}
}
/**
* Store verification result in linked spreadsheet
*/
function storeVerificationResult(response, result) {
const form = FormApp.getActiveForm();
const destinationId = form.getDestinationId();
if (!destinationId) {
Logger.log('No linked spreadsheet found');
return;
}
const spreadsheet = SpreadsheetApp.openById(destinationId);
const sheet = spreadsheet.getSheets()[0];
// Find the response row
const responseRow = findResponseRow(sheet, response);
if (responseRow === -1) {
Logger.log('Could not find response row');
return;
}
// Add verification columns if they don't exist
ensureVerificationColumns(sheet);
// Get column indices
const headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
const statusCol = headers.indexOf('Email Status') + 1;
const scoreCol = headers.indexOf('Email Score') + 1;
const disposableCol = headers.indexOf('Is Disposable') + 1;
// Update row
if (statusCol > 0) {
sheet.getRange(responseRow, statusCol).setValue(result.status);
}
if (scoreCol > 0 && result.score !== undefined) {
sheet.getRange(responseRow, scoreCol).setValue(result.score);
}
if (disposableCol > 0) {
sheet.getRange(responseRow, disposableCol).setValue(result.isDisposable ? 'Yes' : 'No');
}
// Apply conditional formatting
if (result.status === 'invalid' || result.isDisposable) {
sheet.getRange(responseRow, 1, 1, sheet.getLastColumn())
.setBackground('#ffcccb');
}
}
/**
* Find the row number for a response
*/
function findResponseRow(sheet, response) {
const timestamp = response.getTimestamp();
const data = sheet.getDataRange().getValues();
for (let i = 1; i < data.length; i++) {
const rowTimestamp = data[i][0];
if (rowTimestamp instanceof Date &&
Math.abs(rowTimestamp.getTime() - timestamp.getTime()) < 5000) {
return i + 1; // 1-indexed
}
}
return -1;
}
/**
* Add verification columns to spreadsheet
*/
function ensureVerificationColumns(sheet) {
const headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
const columnsToAdd = ['Email Status', 'Email Score', 'Is Disposable'];
for (const column of columnsToAdd) {
if (!headers.includes(column)) {
const newCol = sheet.getLastColumn() + 1;
sheet.getRange(1, newCol).setValue(column);
}
}
}
/**
* Handle invalid email submissions
*/
function handleInvalidEmail(response, email, result) {
if (!CONFIG.NOTIFY_ON_INVALID) {
return;
}
const form = FormApp.getActiveForm();
const formTitle = form.getTitle();
const subject = `[${formTitle}] Invalid Email Submission`;
const body = `
An invalid email was submitted to your form.
Form: ${formTitle}
Email: ${email}
Status: ${result.status}
${result.isDisposable ? 'Note: Disposable email detected' : ''}
Score: ${result.score || 'N/A'}
Timestamp: ${response.getTimestamp()}
`.trim();
MailApp.sendEmail({
to: CONFIG.NOTIFICATION_EMAIL,
subject: subject,
body: body,
});
}
/**
* Set up form submit trigger
* Run this function once to install the trigger
*/
function installTrigger() {
const form = FormApp.getActiveForm();
// Remove existing triggers
const triggers = ScriptApp.getUserTriggers(form);
for (const trigger of triggers) {
if (trigger.getHandlerFunction() === 'onFormSubmit') {
ScriptApp.deleteTrigger(trigger);
}
}
// Install new trigger
ScriptApp.newTrigger('onFormSubmit')
.forForm(form)
.onFormSubmit()
.create();
Logger.log('Trigger installed successfully');
}
/**
* Test verification (run manually)
*/
function testVerification() {
const result = verifyEmail('test@example.com');
Logger.log(JSON.stringify(result, null, 2));
}단계 3: 트리거 설치
- Apps Script 편집기에서 함수 드롭다운에서
installTrigger선택 - 실행 클릭
- 프롬프트가 나타나면 필요한 권한 부여
단계 4: 통합 테스트
- 폼에 테스트 응답 제출
- 연결된 스프레드시트에서 검증 결과 확인
- Apps Script 로그 보기: 왼쪽 사이드바의 실행 메뉴
방법 2: 스프레드시트 부가기능
커스텀 사이드바로 Google Sheets에서 직접 이메일을 검증하세요.
부가기능 코드 생성
// Code.gs
function onOpen() {
const ui = SpreadsheetApp.getUi();
ui.createMenu('EmailVerify')
.addItem('Verify Selected Emails', 'verifySelected')
.addItem('Verify All Emails', 'verifyAll')
.addItem('Settings', 'showSettings')
.addToUi();
}
function verifySelected() {
const sheet = SpreadsheetApp.getActiveSheet();
const range = sheet.getActiveRange();
const values = range.getValues();
const results = [];
for (let i = 0; i < values.length; i++) {
const email = values[i][0];
if (!email || !isValidEmailFormat(email)) {
results.push(['Invalid format']);
continue;
}
const result = verifyEmail(email);
results.push([result.status + (result.isDisposable ? ' (disposable)' : '')]);
}
// Write results to next column
const startCol = range.getColumn() + range.getNumColumns();
sheet.getRange(range.getRow(), startCol, results.length, 1).setValues(results);
}
function verifyAll() {
const sheet = SpreadsheetApp.getActiveSheet();
const ui = SpreadsheetApp.getUi();
// Ask for email column
const response = ui.prompt(
'Verify All Emails',
'Enter the column letter containing emails (e.g., B):',
ui.ButtonSet.OK_CANCEL
);
if (response.getSelectedButton() !== ui.Button.OK) {
return;
}
const column = response.getResponseText().toUpperCase();
const colIndex = column.charCodeAt(0) - 64; // A=1, B=2, etc.
const lastRow = sheet.getLastRow();
const emailRange = sheet.getRange(2, colIndex, lastRow - 1, 1);
const emails = emailRange.getValues().flat().filter(e => e);
ui.alert(`Starting verification of ${emails.length} emails...`);
// Verify in batches
const batchSize = 50;
let processed = 0;
for (let i = 0; i < emails.length; i += batchSize) {
const batch = emails.slice(i, i + batchSize);
for (let j = 0; j < batch.length; j++) {
const email = batch[j];
const rowIndex = i + j + 2; // +2 for header and 0-index
if (!isValidEmailFormat(email)) {
sheet.getRange(rowIndex, colIndex + 1).setValue('Invalid format');
continue;
}
const result = verifyEmail(email);
// Write status
sheet.getRange(rowIndex, colIndex + 1).setValue(result.status);
// Write score
if (result.score !== undefined) {
sheet.getRange(rowIndex, colIndex + 2).setValue(result.score);
}
// Highlight invalid
if (result.status === 'invalid' || result.isDisposable) {
sheet.getRange(rowIndex, 1, 1, sheet.getLastColumn()).setBackground('#ffcccb');
}
}
processed += batch.length;
SpreadsheetApp.flush();
// Avoid rate limits
if (i + batchSize < emails.length) {
Utilities.sleep(1000);
}
}
ui.alert(`Verification complete! Processed ${processed} emails.`);
}
function isValidEmailFormat(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
function showSettings() {
const html = HtmlService.createHtmlOutputFromFile('Settings')
.setWidth(400)
.setHeight(300);
SpreadsheetApp.getUi().showModalDialog(html, 'EmailVerify Settings');
}
function saveSettings(apiKey) {
PropertiesService.getUserProperties().setProperty('EMAILVERIFY_API_KEY', apiKey);
return { success: true };
}
function getApiKey() {
return PropertiesService.getUserProperties().getProperty('EMAILVERIFY_API_KEY') || '';
}설정 HTML
<!-- Settings.html -->
<!DOCTYPE html>
<html>
<head>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input[type="text"] {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
button {
background-color: #4285f4;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #357abd;
}
.success {
color: #28a745;
margin-top: 10px;
}
</style>
</head>
<body>
<div class="form-group">
<label for="apiKey">EmailVerify API Key</label>
<input type="text" id="apiKey" placeholder="bv_live_xxx" />
</div>
<button onclick="saveSettings()">Save Settings</button>
<div id="message" class="success" style="display: none;"></div>
<script>
// Load existing settings
google.script.run.withSuccessHandler(function(apiKey) {
document.getElementById('apiKey').value = apiKey;
}).getApiKey();
function saveSettings() {
const apiKey = document.getElementById('apiKey').value;
google.script.run.withSuccessHandler(function(result) {
if (result.success) {
document.getElementById('message').textContent = 'Settings saved!';
document.getElementById('message').style.display = 'block';
setTimeout(function() {
google.script.host.close();
}, 1500);
}
}).saveSettings(apiKey);
}
</script>
</body>
</html>방법 3: Zapier 통합
Zapier를 사용하여 Google Forms를 EmailVerify에 연결하세요.
Zap 설정
- 트리거: Google Forms → New Response in Spreadsheet
- 액션 1: Webhooks by Zapier → POST to EmailVerify
- 액션 2: Google Sheets → Update Spreadsheet Row
웹훅 구성
URL: https://api.emailverify.ai/v1/verify
Method: POST
Headers:
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
Data:
{
"email": "{{email_field_value}}"
}행 업데이트
검증 결과를 스프레드시트에 매핑하세요:
- 열: Email Status →
{{status}} - 열: Email Score →
{{score}} - 열: Is Disposable →
{{result__disposable}}
대량 검증 스크립트
기존 대규모 데이터셋 검증을 위한 스크립트:
/**
* Bulk verify emails from a column
* Processes in batches with progress tracking
*/
function bulkVerifyEmails() {
const sheet = SpreadsheetApp.getActiveSheet();
const ui = SpreadsheetApp.getUi();
// Configuration
const EMAIL_COL = 2; // Column B (1-indexed)
const STATUS_COL = 3; // Column C for status
const SCORE_COL = 4; // Column D for score
const START_ROW = 2; // Skip header
const BATCH_SIZE = 100;
const lastRow = sheet.getLastRow();
const totalEmails = lastRow - START_ROW + 1;
const proceed = ui.alert(
'Bulk Verification',
`This will verify ${totalEmails} emails. Continue?`,
ui.ButtonSet.YES_NO
);
if (proceed !== ui.Button.YES) {
return;
}
// Get all emails
const emailRange = sheet.getRange(START_ROW, EMAIL_COL, totalEmails, 1);
const emails = emailRange.getValues().flat();
// Get API key
const apiKey = PropertiesService.getUserProperties().getProperty('EMAILVERIFY_API_KEY');
if (!apiKey) {
ui.alert('Please configure your API key in Settings first.');
return;
}
// Process in batches
let processed = 0;
const stats = { valid: 0, invalid: 0, disposable: 0, errors: 0 };
for (let i = 0; i < emails.length; i++) {
const email = emails[i];
const row = START_ROW + i;
// Skip if already verified
const existingStatus = sheet.getRange(row, STATUS_COL).getValue();
if (existingStatus) {
processed++;
continue;
}
if (!email || !isValidEmailFormat(email)) {
sheet.getRange(row, STATUS_COL).setValue('invalid_format');
stats.errors++;
processed++;
continue;
}
// Verify
const result = verifyEmailWithKey(email, apiKey);
// Write results
sheet.getRange(row, STATUS_COL).setValue(result.status);
if (result.score !== undefined) {
sheet.getRange(row, SCORE_COL).setValue(result.score);
}
// Update stats
if (result.status === 'valid') stats.valid++;
else if (result.status === 'invalid') stats.invalid++;
if (result.isDisposable) stats.disposable++;
if (result.error) stats.errors++;
// Highlight invalid rows
if (result.status === 'invalid' || result.isDisposable) {
sheet.getRange(row, 1, 1, sheet.getLastColumn()).setBackground('#ffcccb');
}
processed++;
// Progress update every 50 emails
if (processed % 50 === 0) {
SpreadsheetApp.flush();
Logger.log(`Progress: ${processed}/${totalEmails}`);
}
// Rate limiting
Utilities.sleep(100);
}
// Final stats
const message = `
Verification Complete!
Total: ${processed}
Valid: ${stats.valid}
Invalid: ${stats.invalid}
Disposable: ${stats.disposable}
Errors: ${stats.errors}
`.trim();
ui.alert('Results', message, ui.ButtonSet.OK);
}
function verifyEmailWithKey(email, apiKey) {
const url = 'https://api.emailverify.ai/v1/verify';
const options = {
method: 'post',
contentType: 'application/json',
headers: {
'Authorization': 'Bearer ' + apiKey,
},
payload: JSON.stringify({ email: email }),
muteHttpExceptions: true,
};
try {
const response = UrlFetchApp.fetch(url, options);
const data = JSON.parse(response.getContentText());
return {
status: data.status,
score: data.score,
isDisposable: data.result?.disposable || false,
};
} catch (error) {
return {
status: 'error',
error: error.message,
};
}
}조건부 이메일 알림
검증 결과에 따라 다른 이메일을 보내세요:
/**
* Send confirmation email only to verified addresses
*/
function sendConditionalConfirmation(e) {
const response = e.response;
const email = getEmailFromResponse(response);
if (!email) return;
// Verify email first
const result = verifyEmail(email);
if (!result.isValid) {
// Send admin notification instead
notifyAdminOfInvalidSubmission(response, email, result);
return;
}
// Send confirmation to valid email
const formTitle = FormApp.getActiveForm().getTitle();
MailApp.sendEmail({
to: email,
subject: `Thank you for your submission to ${formTitle}`,
htmlBody: `
<h2>Thank you!</h2>
<p>We've received your submission and will be in touch soon.</p>
<p>Best regards,<br>The Team</p>
`,
});
}
function notifyAdminOfInvalidSubmission(response, email, result) {
const formTitle = FormApp.getActiveForm().getTitle();
MailApp.sendEmail({
to: CONFIG.NOTIFICATION_EMAIL,
subject: `[Alert] Invalid email submission - ${formTitle}`,
body: `
A submission with an invalid email was received.
Email: ${email}
Status: ${result.status}
Disposable: ${result.isDisposable}
Please review the submission in your spreadsheet.
`,
});
}모범 사례
1. API 키 안전하게 저장
// Use Properties Service instead of hardcoding
const apiKey = PropertiesService.getScriptProperties().getProperty('API_KEY');2. 속도 제한 처리
function verifyWithRetry(email, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const result = verifyEmail(email);
if (result.status !== 'error' || !result.error.includes('rate')) {
return result;
}
// Wait and retry
Utilities.sleep(2000 * (i + 1));
}
return { status: 'error', error: 'Max retries exceeded' };
}3. 우아한 오류 처리
// Never block form submissions on API errors
if (result.error) {
Logger.log('Verification failed, allowing submission');
return; // Don't block the submission
}