EmailVerify LogoEmailVerify

Shopify

Email checker for Shopify. Verify customer emails at checkout and in Shopify apps.

가짜 계정으로부터 Shopify 스토어를 보호하고, 장바구니 이탈 이메일 반송을 줄이고, 이메일 주소를 검증하여 고객 커뮤니케이션을 개선하세요.

Shopify에서 이메일을 검증하는 이유

과제영향솔루션
가짜 계정프로모션 남용, 사기등록 시 검증
장바구니 이탈반송된 복구 이메일발송 전 검증
주문 알림배송 업데이트 실패결제 시 검증
마케팅 캠페인낮은 전달률고객 목록 정리

통합 방법

방법적합 대상복잡도
Shopify Flow자동화된 워크플로낮음
Shopify Functions결제 검증중간
서드파티 앱완전한 솔루션낮음
커스텀 앱완전한 제어높음

방법 1: Shopify Flow (권장)

Shopify Flow를 사용하여 이메일을 자동으로 검증합니다.

새 고객 이메일 검증

고객이 등록할 때 이메일을 검증하는 워크플로 생성:

트리거: Customer created

조건: Customer email is not blank

액션:

  1. EmailVerify로 HTTP 요청 전송
  2. 결과에 따라 고객 태그 추가

Flow 구성

Workflow: Verify New Customer Email

Trigger:
  Event: Customer created

Condition:
  - Customer email is not blank

Action 1:
  Type: Send HTTP request
  URL: https://api.emailverify.ai/v1/verify
  Method: POST
  Headers:
    - Authorization: Bearer YOUR_API_KEY
    - Content-Type: application/json
  Body: {"email": "{{customer.email}}"}

Wait:
  Duration: 1 second

Action 2:
  Type: Add customer tags
  Tags:
    - email_verified (if status = valid)
    - email_invalid (if status = invalid)

장바구니 이탈 이메일 전송 전 검증

Workflow: Verify Before Abandonment Email

Trigger:
  Event: Checkout abandoned
  Delay: 1 hour

Condition:
  - Customer email is not blank
  - Customer does not have tag "email_invalid"

Action 1:
  Type: Send HTTP request to EmailVerify
  Body: {"email": "{{checkout.email}}"}

Action 2:
  Type: Branch
  If status = "valid":
    - Continue to abandonment email sequence
  If status = "invalid":
    - Add tag "email_invalid"
    - Do not send email

방법 2: Shopify Functions로 결제 검증

결제 중 이메일을 검증하는 Shopify Function을 생성합니다.

1단계: Cart Transform Function 생성

// extensions/email-validation/src/run.js
import { EmailVerify } from '@emailverify/node';

export function run(input) {
  const { cart } = input;
  const email = cart?.buyerIdentity?.email;

  if (!email) {
    return { operations: [] };
  }

  // Note: For real-time validation, use a pre-validated cache
  // or implement async validation via metafield
  return {
    operations: [],
  };
}

2단계: Checkout UI Extension 생성

// extensions/email-validation-ui/src/Checkout.jsx
import {
  useExtensionApi,
  render,
  Banner,
  BlockStack,
} from '@shopify/checkout-ui-extensions-react';
import { useState, useEffect } from 'react';

render('Checkout::Contact::RenderAfter', () => <EmailValidation />);

function EmailValidation() {
  const { buyerIdentity } = useExtensionApi();
  const [validationStatus, setValidationStatus] = useState(null);

  useEffect(() => {
    const email = buyerIdentity?.email?.current;
    if (email) {
      validateEmail(email).then(setValidationStatus);
    }
  }, [buyerIdentity?.email?.current]);

  if (!validationStatus) return null;

  if (validationStatus.status === 'invalid') {
    return (
      <Banner status="warning">
        Please check your email address. It appears to be invalid.
      </Banner>
    );
  }

  if (validationStatus.result?.disposable) {
    return (
      <Banner status="info">
        We recommend using a permanent email for order updates.
      </Banner>
    );
  }

  return null;
}

async function validateEmail(email) {
  const response = await fetch('/apps/email-verify/validate', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email }),
  });
  return response.json();
}

방법 3: 커스텀 Shopify 앱

완전한 이메일 검증 솔루션을 구축합니다.

앱 백엔드 (Node.js)

// server/index.js
import '@shopify/shopify-app-remix/adapters/node';
import { shopifyApp } from '@shopify/shopify-app-remix/server';
import { EmailVerify } from '@emailverify/node';

const shopify = shopifyApp({
  // ... Shopify config
});

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

// API route for email verification
export async function action({ request }) {
  const { email, customerId } = await request.json();

  try {
    const result = await emailVerify.verify(email);

    // Update customer metafield
    if (customerId) {
      await updateCustomerVerificationStatus(customerId, result);
    }

    return json(result);
  } catch (error) {
    return json({ error: error.message }, { status: 500 });
  }
}

async function updateCustomerVerificationStatus(customerId, result) {
  const { admin } = await shopify.authenticate.admin(request);

  await admin.graphql(`
    mutation updateCustomerMetafield($input: CustomerInput!) {
      customerUpdate(input: $input) {
        customer {
          id
        }
      }
    }
  `, {
    variables: {
      input: {
        id: `gid://shopify/Customer/${customerId}`,
        metafields: [
          {
            namespace: "email_verification",
            key: "status",
            value: result.status,
            type: "single_line_text_field"
          },
          {
            namespace: "email_verification",
            key: "score",
            value: String(result.score),
            type: "number_decimal"
          },
          {
            namespace: "email_verification",
            key: "verified_at",
            value: new Date().toISOString(),
            type: "date_time"
          }
        ]
      }
    }
  });
}

웹훅 핸들러

고객 생성 웹훅 처리:

// server/webhooks/customer-created.js
export async function handleCustomerCreated(topic, shop, body) {
  const customer = JSON.parse(body);
  const { id, email } = customer;

  if (!email) return;

  try {
    // Verify email
    const result = await emailVerify.verify(email);

    // Update customer with tags
    const tags = [];
    if (result.status === 'valid') {
      tags.push('email_verified');
    } else if (result.status === 'invalid') {
      tags.push('email_invalid');
    }
    if (result.result?.disposable) {
      tags.push('disposable_email');
    }

    await updateCustomerTags(shop, id, tags);

    // Store verification result
    await updateCustomerVerificationStatus(shop, id, result);

    console.log(`Verified ${email}: ${result.status}`);
  } catch (error) {
    console.error(`Failed to verify ${email}:`, error);
  }
}

테마 통합 (Liquid)

등록 폼에 검증 추가:

{% comment %} snippets/email-verification.liquid {% endcomment %}

<script>
document.addEventListener('DOMContentLoaded', function() {
  const emailInput = document.querySelector('input[type="email"]');
  const submitButton = document.querySelector('form[action="/account"] button[type="submit"]');

  let verificationResult = null;

  emailInput.addEventListener('blur', async function() {
    const email = this.value;
    if (!email) return;

    // Show loading state
    emailInput.classList.add('verifying');

    try {
      const response = await fetch('/apps/emailverify/verify', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email })
      });

      verificationResult = await response.json();

      // Update UI based on result
      updateEmailFieldUI(verificationResult);
    } catch (error) {
      console.error('Verification failed:', error);
    } finally {
      emailInput.classList.remove('verifying');
    }
  });

  function updateEmailFieldUI(result) {
    // Remove existing messages
    const existingMessage = document.querySelector('.email-verification-message');
    if (existingMessage) existingMessage.remove();

    // Create message element
    const message = document.createElement('div');
    message.className = 'email-verification-message';

    if (result.status === 'invalid') {
      message.classList.add('error');
      message.textContent = 'Please enter a valid email address';
      submitButton.disabled = true;
    } else if (result.result?.disposable) {
      message.classList.add('warning');
      message.textContent = 'Please use a permanent email for account recovery';
    } else if (result.status === 'valid') {
      message.classList.add('success');
      message.textContent = '✓ Email verified';
      submitButton.disabled = false;
    }

    emailInput.parentNode.appendChild(message);
  }
});
</script>

<style>
.email-verification-message {
  font-size: 12px;
  margin-top: 4px;
}
.email-verification-message.error { color: #c9302c; }
.email-verification-message.warning { color: #f0ad4e; }
.email-verification-message.success { color: #5cb85c; }
input[type="email"].verifying {
  background-image: url('/path/to/spinner.gif');
  background-position: right 10px center;
  background-repeat: no-repeat;
}
</style>

사용 사례

1. 가짜 계정 등록 방지

가입 시 일회용 및 무효한 이메일 차단:

// Theme app extension
async function validateRegistration(email) {
  const result = await verifyEmail(email);

  if (result.status === 'invalid') {
    return {
      valid: false,
      message: 'Please enter a valid email address',
    };
  }

  if (result.result.disposable) {
    return {
      valid: false,
      message: 'Temporary email addresses are not allowed',
    };
  }

  return { valid: true };
}

2. 장바구니 이탈 복구

유효한 주소에만 이탈 이메일 발송:

Shopify Flow:
  Trigger: Checkout abandoned (1 hour delay)

  Condition: Check email verification status

  If valid:
    → Send abandonment email
    → Add to remarketing audience

  If invalid:
    → Skip email
    → Log for analytics

3. 주문 위험 평가

사기 감지에 이메일 품질 반영:

function calculateOrderRiskScore(order, emailVerification) {
  let riskScore = 0;

  // Email verification factors
  if (emailVerification.status === 'invalid') {
    riskScore += 30;
  }

  if (emailVerification.result?.disposable) {
    riskScore += 20;
  }

  if (emailVerification.result?.free && order.total > 500) {
    riskScore += 10; // High value order with free email
  }

  // Other factors...
  if (order.billing_address !== order.shipping_address) {
    riskScore += 15;
  }

  return riskScore;
}

4. 고객 세분화

이메일 품질에 따라 고객 세그먼트 생성:

세그먼트기준마케팅 전략
고가치검증됨, 비즈니스 이메일프리미엄 캠페인
표준검증됨, 무료 이메일일반 캠페인
위험미검증, 오래된 계정재검증 캠페인
제외무효, 일회용마케팅 제외

Metafield 설정

검증 데이터를 저장할 metafield 생성:

고객 Metafields

mutation createMetafieldDefinitions {
  metafieldDefinitionCreate(definition: {
    namespace: "email_verification"
    key: "status"
    name: "Email Verification Status"
    type: "single_line_text_field"
    ownerType: CUSTOMER
  }) {
    createdDefinition { id }
  }
}

권장 metafields:

NamespaceKeyType설명
email_verificationstatussingle_line_text_fieldvalid, invalid, unknown
email_verificationscorenumber_decimal0.0 - 1.0
email_verificationverified_atdate_time마지막 검증 날짜
email_verificationdisposableboolean일회용 이메일 여부
email_verificationrole_basedboolean역할 기반 이메일 여부

Liquid에서 Metafields 액세스

{% if customer.metafields.email_verification.status == 'valid' %}
  <span class="verified-badge">✓ Verified</span>
{% endif %}

대량 고객 검증

기존 고객 목록 정리:

고객 내보내기

async function exportCustomersForVerification(admin) {
  const query = `
    query getCustomers($cursor: String) {
      customers(first: 250, after: $cursor) {
        edges {
          node {
            id
            email
            createdAt
            metafield(namespace: "email_verification", key: "status") {
              value
            }
          }
          cursor
        }
        pageInfo {
          hasNextPage
        }
      }
    }
  `;

  let customers = [];
  let cursor = null;

  do {
    const response = await admin.graphql(query, {
      variables: { cursor },
    });

    const { edges, pageInfo } = response.data.customers;

    // Filter unverified customers
    const unverified = edges
      .filter((e) => !e.node.metafield)
      .map((e) => ({
        id: e.node.id,
        email: e.node.email,
      }));

    customers.push(...unverified);
    cursor = edges[edges.length - 1]?.cursor;
  } while (response.data.customers.pageInfo.hasNextPage);

  return customers;
}

대량 검증 및 업데이트

async function bulkVerifyCustomers(customers) {
  const emails = customers.map((c) => c.email);

  // Submit bulk verification job
  const job = await emailVerify.verifyBulk(emails);

  // Wait for completion
  const results = await waitForJobCompletion(job.job_id);

  // Update customers with results
  for (const result of results) {
    const customer = customers.find((c) => c.email === result.email);
    if (customer) {
      await updateCustomerVerificationStatus(customer.id, result);
    }
  }

  return results;
}

모범 사례

1. 여러 지점에서 검증

  • 등록: 가짜 계정 차단
  • 결제: 주문 알림이 고객에게 도달하도록 보장
  • 장바구니 이탈: 무효한 주소에 이메일 낭비하지 않음

2. 결과 캐싱

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

const CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours

async function verifyWithCache(email) {
  const cacheKey = `email_verify:${email}`;
  const cached = await redis.get(cacheKey);

  if (cached) {
    return JSON.parse(cached);
  }

  const result = await emailVerify.verify(email);
  await redis.setex(cacheKey, CACHE_DURATION / 1000, JSON.stringify(result));

  return result;
}

3. 엣지 케이스 처리

function handleVerificationResult(result, context) {
  switch (result.status) {
    case 'valid':
      // Normal flow
      break;

    case 'invalid':
      if (context === 'checkout') {
        // Don't block checkout, just log
        logInvalidEmail(result.email, 'checkout');
      } else if (context === 'registration') {
        // Block registration
        throw new Error('Invalid email');
      }
      break;

    case 'unknown':
      // Accept but flag for review
      flagForReview(result.email);
      break;

    case 'accept_all':
      // Valid but monitor for bounces
      markAsCatchAll(result.email);
      break;
  }
}

4. 모니터링 및 최적화

다음 메트릭 추적:

  • 검증 성공률
  • 반송률 감소
  • 가짜 계정 방지율
  • 장바구니 이탈 이메일 전달률

관련 리소스

On this page