워드프레스 우커머스 결제페이지에서 상품 삭제 버튼 추가하기

우커머스에서 장바구니를 거치지 않고 바로 결제페이지로 이동하도록 구성한 사이트를 운영하다 보면 한 가지 불편한 점이 생길 수 있습니다. 결제페이지의 주문 내역에는 담긴 상품이 보이지만, 그 자리에서 상품을 삭제할 수 없는 경우입니다. 사용자가 실수로 상품을 담았거나 결제 직전에 상품을 변경하고 싶다면 다시 장바구니나 상품페이지로 이동해야 하기 때문에 구매 흐름이 불편해질 수 있습니다. 이번 작업에서는 우커머스 결제페이지의 주문 내역에 상품 삭제 버튼을 직접 추가해 결제페이지를 벗어나지 않고 상품을 삭제할 수 있도록 개선했습니다.

기본 우커머스 결제 페이지의 아쉬운 점

기존 결제페이지에서는 주문할 상품이 다음과 같이 표시됩니다.

실제 고객 사례

하지만 상품 옆에 삭제 버튼이 없다면 사용자가 상품을 잘못 선택했을 때 다시 장바구니로 돌아가거나 장바구니를 비활성화한 경우, 아예 수정 자체가 불가능한 상태가 됩니다. 이로 인해 많은 사람들이 불편하다고 느낄 수 있기 때문에 이 부분에 대한 개선이 필요했습니다.

구현 결과

실제 고객 사례

어떤 부분이 달라지나요?

상품명 앞에 × 삭제 버튼을 추가하여 결제페이지 → 상품 삭제 → 금액 자동 갱신 까지 한 화면에서 처리할 수 있도록 코드를 설계하였습니다.

우커머스 결제페이지에서는 상품 목록에 따라 다음 항목들도 함께 계산됩니다.

이번 코드에서는 상품 삭제가 정상적으로 완료되면 결제페이지를 다시 불러오지 않고도 현재 주문 금액을 자동으로 갱신하도록 구성했습니다. 사용자 입장에서는 페이지가 새로고침되는 느낌 없이 자연스럽게 금액이 변경됩니다.

마지막 상품까지 삭제하면 어떻게 되나요?

결제페이지에서 상품을 하나씩 삭제하다가 마지막 상품까지 삭제하면 더 이상 결제할 상품이 남아 있지 않습니다. 이 상태에서 결제화면을 그대로 유지하면 빈 주문서나 로딩 화면이 표시될 수 있기 때문에 별도의 처리가 필요합니다. 이번 코드에서는 마지막 상품을 삭제하면 장바구니 페이지로 이동하도록 처리했습니다. 따라서 다음과 같은 흐름이 됩니다.

상품 여러 개가 있는 경우, 상품 삭제 → 남은 상품 유지 → 결제금액 자동 갱신
마지막 상품을 삭제한 경우, 상품 삭제 → 장바구니가 비어 있음 → 장바구니 페이지로 이동

이렇게 처리하면 빈 결제페이지에서 발생할 수 있는 오류나 무한 로딩을 방지할 수 있습니다.

빈 결제페이지도 따로 처리했습니다.

우커머스는 일반적으로 장바구니가 비어 있는 상태에서 결제페이지에 접근하면 장바구니 페이지로 이동시키는 동작을 합니다. 하지만 사이트 구조에 따라서는 빈 결제페이지에서도 별도의 안내를 보여주는 것이 더 자연스러울 수 있습니다. 이번 코드에는 장바구니가 비어 있는 상태에서 결제페이지에 접근한 경우 다음과 같은 안내를 표시하는 기능도 포함되어 있습니다.

결제할 상품이 없습니다.
결제할 강의를 선택한 후 다시 이용해주세요.

그리고 아래에 강의 목록 보기 버튼을 표시해 다시 상품을 선택할 수 있도록 했습니다. 빈 상태에서는 실제 결제폼과 결제 관련 AJAX 기능도 동작하지 않도록 처리해 불필요한 오류가 발생하지 않도록 구성했습니다.

전체 코드

아래 코드는 우커머스의 장바구니 추가 이벤트를 이용해 팝업을 표시하기 위한 전체 코드입니다. 차일드 테마의 Functions.php에 삽입하거나, 코드 스니펫 플러그인을 설치하여 해당 코드를 삽입하시면 작동됩니다.

Functions.phpPHP
/**
 * WooCommerce 체크아웃 주문내역에서 상품 삭제 기능
 * - 클래식 WooCommerce Checkout 전용
 * - 상품명 앞에 × 버튼 표시
 * - AJAX로 상품 삭제
 * - 삭제 후 결제금액 자동 갱신
 */

add_filter(
    'woocommerce_cart_item_name',
    function ( $product_name, $cart_item, $cart_item_key ) {

        // 체크아웃 페이지에서만 적용
        if ( ! is_checkout() || is_order_received_page() ) {
            return $product_name;
        }

        // WooCommerce 기본 삭제 URL
        // JS가 동작하지 않는 경우에도 fallback으로 사용
        $remove_url = wc_get_cart_remove_url( $cart_item_key );
        $remove_button = sprintf(
            '<a href="%1$s"
                class="leo-checkout-remove"
                data-cart-item-key="%2$s"
                aria-label="상품 삭제"
                title="상품 삭제">×</a>',
            esc_url( $remove_url ),
            esc_attr( $cart_item_key )
        );
        return $remove_button . $product_name;
    },
    20,
    3
);


/**
 * 체크아웃 상품삭제 AJAX + 스타일
 */
add_action(
    'wp_footer',
    function () {
        if ( ! is_checkout() || is_order_received_page() ) {
            return;
        }
        $cart_url = wc_get_cart_url();
        ?>
        <style>
        /* 체크아웃 상품 삭제 버튼 */
        .woocommerce-checkout-review-order-table .leo-checkout-remove {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            width: 20px;
            height: 20px;
            margin-right: 10px;
            font-size: 18px;
            font-weight: 700;
            line-height: 1;
            text-decoration: none !important;
            color: #444;
            cursor: pointer;
            transition:
                color .2s ease,
                opacity .2s ease,
                transform .2s ease;
        }
        .woocommerce-checkout-review-order-table .leo-checkout-remove:hover {
            color: #e53935;
            transform: scale(1.12);
        }
        .woocommerce-checkout-review-order-table .leo-checkout-remove.is-loading {
            opacity: .3;
            pointer-events: none;
        }
        </style>


        <script>
        jQuery(function($){
            $(document.body).on(
                'click',
                '.leo-checkout-remove',
                function(e){
                    e.preventDefault();
                    var $button = $(this);
                    if ($button.hasClass('is-loading')) {
                       return;
                    }
                    var cartItemKey = $button.data('cart-item-key');
                    if (!cartItemKey) {
                        window.location.href = $button.attr('href');
                        return;
                    }

                    /*
                     * 삭제 전 상품 개수 저장
                     * 마지막 상품까지 삭제할 경우 장바구니 페이지로 이동
                     */
                    var itemCount = $('.leo-checkout-remove').length;
                    $button.addClass('is-loading');

                    /*
                     * WooCommerce 기본 AJAX endpoint
                     */
                    var ajaxUrl = wc_checkout_params.wc_ajax_url
                        .toString()
                        .replace(
                            '%%endpoint%%',
                            'remove_from_cart'
                        );

                    $.ajax({

                        type: 'POST',
                        url: ajaxUrl,
                        data: {
                            cart_item_key: cartItemKey
                        },
                        dataType: 'json',
                        success: function(response){

                            /*
                             * 삭제 실패 시 WooCommerce 기본 URL 사용
                             */
                            if (!response || response.error) {
                                window.location.href = $button.attr('href');
                                return;
                            }

                            /*
                             * 미니카트 등의 WooCommerce fragment 갱신
                             */
                            if (response.fragments) {
                                $.each(
                                    response.fragments,
                                    function(selector, html){
                                        $(selector).replaceWith(html);
                                    }
                                );
                            }

                            /*
                             * 마지막 상품을 삭제한 경우
                             */
                            if (itemCount <= 1) {
                                window.location.href =
                                    <?php echo wp_json_encode( $cart_url ); ?>;
                                return;
                            }

                            /*
                             * Checkout 재계산
                             *
                             * 상품목록
                             * 소계
                             * 옵션금액
                             * 할인
                             * 최종금액
                             * 결제수단
                             *
                             * 모두 WooCommerce가 다시 계산
                             */
                            $(document.body).trigger(
                                'update_checkout'
                            );
                        },
                        error: function(){

                            /*
                             * AJAX 오류 발생 시
                             * WooCommerce 기본 삭제 방식으로 fallback
                             */
                            window.location.href =
                                $button.attr('href');
                        }
                    });
                }
            );
        });
        </script>
        <?php
    },
    100
);

/**
 * 빈 Checkout 화면 유지
 *
 * - 빈 Checkout → Cart 리디렉션 방지
 * - Checkout 페이지에 "결제할 상품이 없습니다" 표시
 * - 빈 상태에서는 Checkout 폼/AJAX 작동 중지
 */

/* ---------------------------------------------------------
 * 1. WooCommerce의 빈 Checkout → Cart 리디렉션 방지
 * --------------------------------------------------------- */

add_filter(
    'woocommerce_checkout_redirect_empty_cart',
    '__return_false'
);

/* ---------------------------------------------------------
 * 2. 빈 Checkout body class 추가
 * --------------------------------------------------------- */

add_filter(
    'body_class',
    function ( $classes ) {
        if (
            function_exists( 'is_checkout' ) &&
            is_checkout() &&
            ! is_order_received_page() &&
            function_exists( 'WC' ) &&
            WC()->cart &&
            WC()->cart->is_empty()
        ) {
            $classes[] = 'leon-empty-checkout';
        }
        return $classes;
    }
);

/* ---------------------------------------------------------
 * 3. 빈 Checkout에서는 Checkout AJAX 스크립트 제거
 * --------------------------------------------------------- */

add_action(
    'wp_enqueue_scripts',
    function () {
        if (
            ! function_exists( 'is_checkout' ) ||
            ! is_checkout() ||
            is_order_received_page() ||
            ! function_exists( 'WC' ) ||
            ! WC()->cart ||
            ! WC()->cart->is_empty()
        ) {
            return;
        }
        wp_dequeue_script( 'wc-checkout' );
        wp_deregister_script( 'wc-checkout' );
    },
    999
);

/* ---------------------------------------------------------
 * 4. 빈 Checkout 안내문 출력
 * --------------------------------------------------------- */

add_action(
    'woocommerce_before_checkout_form',
    function () {
        if (
            ! function_exists( 'WC' ) ||
            ! WC()->cart ||
            ! WC()->cart->is_empty()
        ) {
            return;
        }
        
        /*
        상품 목록 URL 삽입
        */
        $course_url = home_url( '/shop/' );
        ?>
        <div class="leon-empty-checkout-message">
            <h3>결제할 상품이 없습니다.</h3>
            <p>결제할 상품을 선택한 후 다시 이용해주세요.</p>
            <a
                href="<?php echo esc_url( $course_url ); ?>"
                class="button"
            >상품 목록 보기</a>
        </div>
        <?php
    },
    5
);


/* ---------------------------------------------------------
 * 5. 빈 Checkout에서는 실제 결제폼 숨김
 * --------------------------------------------------------- */

add_action(
    'wp_head',
    function () {
        if (
            ! function_exists( 'is_checkout' ) ||
            ! is_checkout() ||
            is_order_received_page() ||
            ! function_exists( 'WC' ) ||
            ! WC()->cart ||
            ! WC()->cart->is_empty()
        ) {
            return;
        }
        ?>
        <style>
        body.leon-empty-checkout
        form.woocommerce-checkout {
            display: none !important;
        }
        body.leon-empty-checkout
        .woocommerce-form-coupon-toggle,
        body.leon-empty-checkout
        .woocommerce-form-login-toggle {
            display: none !important;
        }
        .leon-empty-checkout-message {
            padding: 50px 30px;
            text-align: center;
            border: 1px solid #e5e5e5;
            border-radius: 12px;
            background: #fff;
        }
        .leon-empty-checkout-message h3 {
            margin: 0 0 10px;
            font-size: 24px;
        }
        .leon-empty-checkout-message p {
            margin: 0 0 25px;
            color: #777;
        }
        .leon-empty-checkout-message .button {
            display: inline-block;
        }
        </style>
        <?php
    },
    99
);

운영 중인 사이트라면 바로 적용하기보다 테스트 환경에서 먼저 확인하는 것을 권장합니다.

적용 전 확인할 부분

코드를 적용한 뒤에는 다음 항목을 확인하는 것이 좋습니다.

266번째 줄 코드PHP
 /*
상품 목록 URL 삽입
*/
$course_url = home_url( '/shop/' );

현재 사이트에서 실제 상품 또는 상품 목록 URL이 다르다면 '/shop/' 부분을 변경해야 합니다.

우커머스 사이트는 테마나 추가 플러그인에 따라 장바구니 처리 방식이 달라질 수 있기 때문에 운영 사이트에 적용하기 전 테스트 환경에서 먼저 확인하는 것을 권장합니다.

마치며

우커머스 결제페이지는 기본적으로 결제를 완료하는 데 초점이 맞춰져 있기 때문에 상품 수정 기능은 장바구니 페이지보다 제한적입니다. 하지만 사이트 구조에 따라서는 사용자가 결제 직전에도 상품을 수정할 수 있는 기능이 필요할 수 있습니다. 이번 커스텀은 우커머스 자체 주문 계산 기능은 그대로 사용하면서, 상품 삭제 → 주문금액 재계산 → 빈 장바구니 처리 과정을 자연스럽게 연결하는 방식입니다.

특히 장바구니를 거치지 않고 바로 결제페이지로 이동하는 사이트라면 비교적 작은 수정으로 결제과정의 불편함을 줄일 수 있습니다. 알려줄LEON에서는 실제 워드프레스, 우커머스 사이트를 제작하고 운영하면서 사용했던 기능과 문제 해결 방법을 계속 정리합니다.

복잡한 개발 이론보다는 실제 사이트를 운영하면서 “이거 어떻게 하지?” 싶었던 부분을 쉽게 적용할 수 있는 방법을 중심으로 소개합니다.

답글 남기기