데브포일 홈
WordPress 자동화
Guest
STAGE 19 · Lv.95

매출 분석 자동 알림

매출 임계값 설정, 일일 매출 알림, 이상 징후 감지, 트렌드 분석, Make.com 알림 파이프라인을 구축합니다.

실시간 매출 모니터링의 중요성

매출 이상 징후를 즉시 감지해야 하는 이유

이커머스에서 결제 오류, 서버 문제, 재고 부족 등으로 매출이 급감해도 수동 모니터링으로는 몇 시간, 심지어 며칠이 지나서야 발견하는 경우가 많습니다. 자동 매출 알림 시스템은 이런 문제를 실시간으로 감지하여 빠른 대응을 가능하게 합니다.


실제 사례를 보면, 한 이커머스 사이트에서 결제 게이트웨이 오류가 발생했지만 6시간 동안 아무도 인지하지 못했습니다. 이 시간 동안 약 50건의 주문이 실패했고, 예상 매출 손실은 800만 원에 달했습니다. 자동 알림 시스템이 있었다면 30분 이내에 문제를 발견하고 대응할 수 있었을 것입니다.


반대로 매출 급증도 중요한 신호입니다. 바이럴 콘텐츠, 미디어 노출, 인플루언서 효과 등으로 주문이 폭증할 때, 재고 확보와 서버 증설, 고객 서비스 인력 보충 등의 사전 대응이 필요합니다.


자동 매출 알림이 감지하는 상황:

- 매출 급감: 결제 게이트웨이 오류, 서버 다운, 가격 설정 오류, SEO 패널티

- 매출 급증: 바이럴 효과, 미디어 노출, 프로모션 효과, 봇 공격(가짜 주문)

- AOV 이상: 고가 상품 재고 소진, 쿠폰 남용, 가격 오류

- 환불 급증: 품질 문제, 배송 지연, 사기 주문

- 특정 상품 급감: 상품 페이지 오류, 재고 0, 경쟁사 가격 변동

📉
급감 감지
매출 30% 이상 감소 시
즉시 알림
📈
급증 감지
매출 50% 이상 증가 시
기회 알림
실시간
시간대별
매출 추적
🎯
일일 목표
일 매출 목표
달성률 추적
알림 유형 감지 조건 심각도 알림 채널 대응 액션
매출 급감7일 평균 대비 -30% 이상CriticalSMS + Slack + 이메일결제/서버 상태 즉시 점검
주문 0건영업시간 3시간+ 주문 없음HighSlack + 이메일사이트 접근성 확인
매출 급증7일 평균 대비 +50% 이상PositiveSlack재고/서버 용량 확인
AOV 이상7일 평균 대비 ±30% 이상MediumSlack가격/쿠폰 설정 확인
환불 급증일 환불 3건 이상 또는 10% 이상HighSlack + 이메일품질/배송 문제 조사
월 목표 위험현재 속도로 월 목표 미달 예상Medium이메일 (주간)프로모션/마케팅 강화

WooCommerce 매출 분석 시스템 구축

// WooCommerce 종합 매출 분석 엔진
class Revenue_Analytics {
    private $wpdb;

    public function __construct() {
        global $wpdb;
        $this->wpdb = $wpdb;
    }

    // ===================================================
    // 1. 지정 기간 매출 데이터 종합 조회
    // ===================================================
    public function get_revenue_data($date_from, $date_to = null) {
        $date_to = $date_to ?: current_time('Y-m-d');

        $data = $this->wpdb->get_row($this->wpdb->prepare(
            "SELECT
                COUNT(DISTINCT p.ID) as order_count,
                COALESCE(SUM(CAST(pm_total.meta_value AS DECIMAL(10,2))), 0) as gross_revenue,
                COALESCE(SUM(CAST(pm_tax.meta_value AS DECIMAL(10,2))), 0) as total_tax,
                COALESCE(SUM(CAST(pm_ship.meta_value AS DECIMAL(10,2))), 0) as total_shipping,
                COALESCE(SUM(CAST(pm_disc.meta_value AS DECIMAL(10,2))), 0) as total_discount,
                COALESCE(AVG(CAST(pm_total.meta_value AS DECIMAL(10,2))), 0) as avg_order_value,
                COALESCE(MAX(CAST(pm_total.meta_value AS DECIMAL(10,2))), 0) as max_order_value,
                COALESCE(MIN(CAST(pm_total.meta_value AS DECIMAL(10,2))), 0) as min_order_value,
                COUNT(DISTINCT pm_email.meta_value) as unique_customers
             FROM {$this->wpdb->posts} p
             LEFT JOIN {$this->wpdb->postmeta} pm_total ON p.ID = pm_total.post_id AND pm_total.meta_key = '_order_total'
             LEFT JOIN {$this->wpdb->postmeta} pm_tax ON p.ID = pm_tax.post_id AND pm_tax.meta_key = '_order_tax'
             LEFT JOIN {$this->wpdb->postmeta} pm_ship ON p.ID = pm_ship.post_id AND pm_ship.meta_key = '_order_shipping'
             LEFT JOIN {$this->wpdb->postmeta} pm_disc ON p.ID = pm_disc.post_id AND pm_disc.meta_key = '_cart_discount'
             LEFT JOIN {$this->wpdb->postmeta} pm_email ON p.ID = pm_email.post_id AND pm_email.meta_key = '_billing_email'
             WHERE p.post_type = 'shop_order'
             AND p.post_status IN ('wc-completed', 'wc-processing')
             AND DATE(p.post_date) BETWEEN %s AND %s",
            $date_from, $date_to
        ), ARRAY_A);

        $data['net_revenue'] = $data['gross_revenue'] - $data['total_tax'] - $data['total_shipping'];
        $data['profit_margin'] = $data['gross_revenue'] > 0
            ? round(($data['net_revenue'] / $data['gross_revenue']) * 100, 1) : 0;

        return $data;
    }

    // ===================================================
    // 2. 시간대별 매출 분석 (오늘)
    // ===================================================
    public function get_hourly_revenue($date = null) {
        $date = $date ?: current_time('Y-m-d');

        $results = $this->wpdb->get_results($this->wpdb->prepare(
            "SELECT
                HOUR(p.post_date) as hour,
                COUNT(*) as orders,
                SUM(CAST(pm.meta_value AS DECIMAL(10,2))) as revenue,
                AVG(CAST(pm.meta_value AS DECIMAL(10,2))) as avg_order
             FROM {$this->wpdb->posts} p
             JOIN {$this->wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_order_total'
             WHERE p.post_type = 'shop_order'
             AND p.post_status IN ('wc-completed', 'wc-processing')
             AND DATE(p.post_date) = %s
             GROUP BY HOUR(p.post_date)
             ORDER BY hour",
            $date
        ), ARRAY_A);

        // 24시간 전체에 대한 빈 슬롯 채우기
        $hourly = array_fill(0, 24, array('orders' => 0, 'revenue' => 0, 'avg_order' => 0));
        foreach ($results as $row) {
            $hourly[(int)$row['hour']] = $row;
        }
        return $hourly;
    }

    // ===================================================
    // 3. 일별 매출 추이 (트렌드 분석)
    // ===================================================
    public function get_daily_trend($days = 30) {
        return $this->wpdb->get_results($this->wpdb->prepare(
            "SELECT
                DATE(p.post_date) as date,
                COUNT(*) as orders,
                SUM(CAST(pm.meta_value AS DECIMAL(10,2))) as revenue,
                AVG(CAST(pm.meta_value AS DECIMAL(10,2))) as aov,
                COUNT(DISTINCT pe.meta_value) as customers
             FROM {$this->wpdb->posts} p
             JOIN {$this->wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_order_total'
             LEFT JOIN {$this->wpdb->postmeta} pe ON p.ID = pe.post_id AND pe.meta_key = '_billing_email'
             WHERE p.post_type = 'shop_order'
             AND p.post_status IN ('wc-completed', 'wc-processing')
             AND p.post_date >= DATE_SUB(CURDATE(), INTERVAL %d DAY)
             GROUP BY DATE(p.post_date)
             ORDER BY date ASC",
            $days
        ), ARRAY_A);
    }

    // ===================================================
    // 4. 베스트셀러 상품 분석
    // ===================================================
    public function get_best_sellers($date_from, $limit = 10) {
        return $this->wpdb->get_results($this->wpdb->prepare(
            "SELECT
                oi_meta.meta_value as product_id,
                MAX(oi.order_item_name) as product_name,
                SUM(oi_qty.meta_value) as total_qty,
                SUM(oi_total.meta_value) as total_revenue,
                COUNT(DISTINCT oi.order_id) as order_count,
                AVG(oi_total.meta_value / GREATEST(oi_qty.meta_value, 1)) as avg_unit_price
             FROM {$this->wpdb->prefix}woocommerce_order_items oi
             JOIN {$this->wpdb->prefix}woocommerce_order_itemmeta oi_meta ON oi.order_item_id = oi_meta.order_item_id AND oi_meta.meta_key = '_product_id'
             JOIN {$this->wpdb->prefix}woocommerce_order_itemmeta oi_qty ON oi.order_item_id = oi_qty.order_item_id AND oi_qty.meta_key = '_qty'
             JOIN {$this->wpdb->prefix}woocommerce_order_itemmeta oi_total ON oi.order_item_id = oi_total.order_item_id AND oi_total.meta_key = '_line_total'
             JOIN {$this->wpdb->posts} p ON oi.order_id = p.ID
             WHERE p.post_type = 'shop_order'
             AND p.post_status IN ('wc-completed', 'wc-processing')
             AND p.post_date >= %s
             GROUP BY oi_meta.meta_value
             ORDER BY total_revenue DESC
             LIMIT %d",
            $date_from, $limit
        ), ARRAY_A);
    }

    // ===================================================
    // 5. 결제 수단별 분석
    // ===================================================
    public function get_payment_method_breakdown($date_from) {
        return $this->wpdb->get_results($this->wpdb->prepare(
            "SELECT
                pm_method.meta_value as payment_method,
                COUNT(*) as order_count,
                SUM(CAST(pm_total.meta_value AS DECIMAL(10,2))) as revenue,
                AVG(CAST(pm_total.meta_value AS DECIMAL(10,2))) as avg_order
             FROM {$this->wpdb->posts} p
             JOIN {$this->wpdb->postmeta} pm_total ON p.ID = pm_total.post_id AND pm_total.meta_key = '_order_total'
             JOIN {$this->wpdb->postmeta} pm_method ON p.ID = pm_method.post_id AND pm_method.meta_key = '_payment_method_title'
             WHERE p.post_type = 'shop_order'
             AND p.post_status IN ('wc-completed', 'wc-processing')
             AND p.post_date >= %s
             GROUP BY pm_method.meta_value
             ORDER BY revenue DESC",
            $date_from
        ), ARRAY_A);
    }

    // ===================================================
    // 6. 신규 vs 재구매 고객 분석
    // ===================================================
    public function get_customer_type_analysis($date_from) {
        $result = $this->wpdb->get_results($this->wpdb->prepare(
            "SELECT
                pm_email.meta_value as email,
                COUNT(*) as order_count,
                SUM(CAST(pm_total.meta_value AS DECIMAL(10,2))) as total_spent,
                MIN(p.post_date) as first_order
             FROM {$this->wpdb->posts} p
             JOIN {$this->wpdb->postmeta} pm_total ON p.ID = pm_total.post_id AND pm_total.meta_key = '_order_total'
             JOIN {$this->wpdb->postmeta} pm_email ON p.ID = pm_email.post_id AND pm_email.meta_key = '_billing_email'
             WHERE p.post_type = 'shop_order'
             AND p.post_status IN ('wc-completed', 'wc-processing')
             AND pm_email.meta_value != ''
             GROUP BY pm_email.meta_value
             HAVING MIN(p.post_date) >= %s",
            $date_from
        ), ARRAY_A);

        $new_customers = count($result);
        $total_orders = $this->wpdb->get_var($this->wpdb->prepare(
            "SELECT COUNT(*) FROM {$this->wpdb->posts}
             WHERE post_type = 'shop_order'
             AND post_status IN ('wc-completed', 'wc-processing')
             AND post_date >= %s",
            $date_from
        ));

        $repeat_orders = $total_orders - $new_customers;
        return array(
            'new_customers'      => $new_customers,
            'repeat_orders'      => max(0, $repeat_orders),
            'new_customer_rate'  => $total_orders > 0 ? round($new_customers / $total_orders * 100, 1) : 0,
            'repeat_rate'        => $total_orders > 0 ? round(max(0, $repeat_orders) / $total_orders * 100, 1) : 0,
        );
    }

    // ===================================================
    // 7. 7일 평균 일매출 계산
    // ===================================================
    public function get_average_daily_revenue($days = 7) {
        $result = $this->wpdb->get_row($this->wpdb->prepare(
            "SELECT
                COALESCE(AVG(daily_revenue), 0) as avg_revenue,
                COALESCE(AVG(daily_orders), 0) as avg_orders,
                COALESCE(AVG(daily_aov), 0) as avg_aov
             FROM (
                SELECT
                    DATE(p.post_date) as order_date,
                    SUM(CAST(pm.meta_value AS DECIMAL(10,2))) as daily_revenue,
                    COUNT(*) as daily_orders,
                    AVG(CAST(pm.meta_value AS DECIMAL(10,2))) as daily_aov
                FROM {$this->wpdb->posts} p
                JOIN {$this->wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_order_total'
                WHERE p.post_type = 'shop_order'
                AND p.post_status IN ('wc-completed', 'wc-processing')
                AND DATE(p.post_date) BETWEEN DATE_SUB(CURDATE(), INTERVAL %d DAY) AND DATE_SUB(CURDATE(), INTERVAL 1 DAY)
                GROUP BY DATE(p.post_date)
             ) daily_stats",
            $days
        ), ARRAY_A);

        return $result;
    }
}
🔒
여기까지는 미리보기입니다
매출 분석 자동 알림
무료 가입하면 이어서 볼 수 있고, 강의를 완료할 때마다 XP와 레벨이 쌓입니다.
Google로 3초 만에 시작 →🧵 Threads로 시작무료 공개 강의 둘러보기 (Lv.1~3)