워드프레스 방식의 더 흔한 jQuery ajax 폼 메일, admin-ajax.php

워드프레스 방식의 폼 데이터 처리 방식으로 Html 폼 작성, 폼 데이터 검증, 메일 발송 방법에 관하여 워드프레스 폼 데이터 처리 방식으로 메일 전송 폼 직접 만들고 Contact Form 7 그만 쓰기 포스트에서 간단하게 살폈습니다. 이번에는 워드프레스 방식의 ajax 요청으로 폼 데이터를 메일로 전송해봅니다.

이 포스트에서는 지난 포스트의 최종 파일에서 일부 요소를 변경하고, ajax 요청에 관한 자바스크립트 코드를 추가합니다. 다음은 지난 포스트의 최종 파일 페이지입니다.

위 파일 기준으로 이어갑니다.

admin-post.php

일반적인 POST, GET 요청에 관한 워드프레스 폼 데이터 처리 과정은 폼 액션 URL과 폼 데이터에 action(name)과 값을 지정하여 전달하는 것이 핵심입니다.

// 24번 줄
<form id="sendmail_form" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="POST">
// 42번 줄
<input type="hidden" name="action" value="sendmail_formdata">

그리고, 폼 데이터 처리를 위한 훅을 다음 유형으로 지정하는 것으로 끝입니다. 데이터 검증 등의 나머지는 사용자 의도에 따른 것이므로 기준은 아닙니다.

// admin_post_{action_value}, admin_post_nopriv_{action_value}
add_action( 'admin_post_nopriv_sendmail_formdata', 'func_sendmail_formdata' );
add_action( 'admin_post_sendmail_formdata', 'func_sendmail_formdata' );

admin-ajax.php

지난 파일을 기준으로 워드프레스 방식의 ajax 요청을 위해서는 URL 액션만 제거하면 됩니다. 폼 아이디(id)는 원하는 것으로 정하면 됩니다.

// 파일 24번 줄을 다음으로 변경
<form id="ajax_sendmail_form" method="POST">

다음으로 워드프레스 ajax 요청 훅을 다음처럼 변경합니다. 훅 유형을 보면 더 설명할 필요가 없습니다.

// wp_ajax_{action_value}, wp_ajax_nopriv_{action_value}
add_action( 'wp_ajax_nopriv_sendmail_formdata', 'func_sendmail_formdata' );
add_action( 'wp_ajax_sendmail_formdata', 'func_sendmail_formdata' );

이렇게 하면 폼 데이터 제출 시 전달 경로가 없는데, 자바스크립트 코드에서 추가해야 합니다. 그 경로는 다음과 같습니다.

domain/wp-admin/admin-ajax.php

자바스크립트에 관한 내용은 마지막에 추가하기로 하고, Html 폼과 폼 데이터 처리에 관한 부분을 먼저 완료합니다.

Html 폼

먼저, 요청에 의한 응답 메시지 출력을 위한 블록을 추가하고, 자바스크립트 selector로 사용할 블록 아이디와 클래스를 추가 및 변경한 Html 폼 변경 전체 소스는 다음과 같습니다.

add_shortcode( 'ajaxmailform', 'custom_ajaxform_creation' );
function custom_ajaxform_creation(){
    ob_start();
?>
<div class="ajaxform" style="margin-bottom: 2em">
    <form id="ajax_sendmail_form" method="POST">
        <p>모든 입력 필드는 반드시 입력해야 하며, 인용기호는 사용할 수 없습니다. 한 번 보낸 후 다시 보내려면 일정 시간이 지나야 합니다.</p>
        <p>
            <label for="ftitle">문의에 관한 주제를 입력하세요</label>
            <input type="text" id="ftitle" name="ftitle" placeholder="제목" required>
        </p>
        <p>
            <label for="fname">이름 또는 단체명</label>
            <input type="text" id="fname" name="fname" placeholder="이름" required>
        </p>
        <p>
            <label for="femail">이메일 주소를 바르게 입력하세요</label>
            <input type="email" id="femail" name="femail" placeholder="이메일" required>
        </p>
        <p>
            <label for="fcontent">내용을 입력하세요</label>
            <textarea type="textarea" id="fcontent" name="fcontent" maxlength="3000" rows="8" placeholder="줄 바꿈, 단락(빈 줄) 구분만 가능하며, 최대 글자 수 제한이 있습니다. (3000)" required></textarea>
        </p>
        <input type="hidden" name="action" value="sendmail_formdata">
        <?php wp_nonce_field( 'sendmail_formdata', 'sendmail_formdata_field' ); ?>
        <button type="submit" class="submit">보내기</button>
    </form>
</div>
<div class="msg"></div>
<?php
    return ob_get_clean();
}

위의 코드 6, 29번 줄의 변경 및 추가 내용에 초점을 두면 되고, 일부 요소의 이름이 변경되었는데 핵심은 아닙니다.

폼 데이터 처리 및 응답 메시지

지난 파일을 보면 wp_die 함수를 사용한 폼 처리 결과 메시지를 정의한 곳이 있습니다. 모두 네 군데인데, ajax 요청 응답에 관한 워드프레스 함수를 사용하여 모두 변경합니다. 일부 코드를 제외하면 다음과 같습니다.

add_action( 'wp_ajax_nopriv_sendmail_formdata', 'func_sendmail_formdata' );
add_action( 'wp_ajax_sendmail_formdata', 'func_sendmail_formdata' );
function func_sendmail_formdata() {

    //

    if ( get_transient( $transient ) ) {
        wp_die( wp_send_json_error( array ( 'message' => '한 번 보내면 ' . absint( $delay ) . '분 후에 다시 보낼 수 있습니다.' ) ) );
    }

    //

        //

        if ( $success_req ) {
            //

            wp_die( wp_send_json_success( array( 'message' => '메일 전송 요청 성공! 고맙습니다.' ) ) );
            // wp_die( wp_send_json_success( array( 'message' => '메일 전송 요청 성공! 고맙습니다.', 'content' => $body ) ) );

        } else {

            wp_die( wp_send_json_error( array( 'message' => '메일 전송에 오류가 있었습니다. 나중에 다시 해보세요.' ) ) );

        }

    } else {

        wp_die( wp_send_json_error( array( 'message' => '빠진 데이터가 있거나 올바른 방법이 아니군요.' ) ) );

    }
    
}

사용한 함수들은 ajax 요청에 관한 응답 결과를 전달하는 것으로 간단하므로 따로 안내하지 않습니다. 코덱스를 참고하거나 이 포스트의 결과를 확인하는 것으로 충분합니다.

이제 ajax 요청에 의한 폼 데이터 전달 및 응답 결과 출력하는 자바스크립트 코드만 정의하면 됩니다. 포스트에서는 jQuery를 사용합니다.

Ajax 요청에 의한 폼 데이터 전송

파일 맨 아래에 기본적인 jQuery ajax 요청 패턴을 다음처럼 정의합니다.

add_action( 'wp_footer', 'ajax_form_script' );
function ajax_form_script() {
    ?>
    <script type="text/javascript">
        ( function( $ ) {
            $( '#ajax_sendmail_form' ).on( 'submit', function( event ) {
                event.preventDefault();
                var ajax_form_data = $( '#ajax_sendmail_form' ).serialize(); // serialize the form data
                $.ajax( {
                    // url: ajaxurl, // localhost/wp-admin/admin-ajax.php
                    url: '<?php echo esc_url( admin_url('admin-ajax.php') ); ?>',
                    type: 'POST',
                    data: ajax_form_data,
                    success: function( response ) {
                        $( '.msg' ).html( response.data.message );
                    }
                } )
            } );
        } )( jQuery );
    </script>
    <?php
}

위의 8번 줄에서 폼 데이터 전체를 시리얼 데이터로 변수에 할당하여 11번 줄의 워드프레스 ajax 요청에 의한 데이터 처리 경로에 전달합니다.

9번 줄에서 17번 줄은 다음과 같은데, 폼 데이터를 전달하고, 응답 메시지를 받는 코드입니다.

$.ajax( {
    // url: ajaxurl, // localhost/wp-admin/admin-ajax.php
    url: '<?php echo esc_url( admin_url('admin-ajax.php') ); ?>',
    type: 'POST',
    data: ajax_form_data,
    success: function( response ) {
        $( '.msg' ).html( response.data.message );
    }
} )

6번 줄은 ajax 요청 연결 성공을 뜻하며, 폼 데이터 내부 처리에 관한 성공 또는 오류 등을 말하지 않는다는 것을 기억하면 됩니다. 따라서, 폼 데이터 처리와 관련한 응답 메시지는 7번 줄 하나로 충분합니다.

요청 URL

위의 3번 줄 대신에 2번 줄의 ajaxurl 자바스크립트 변수를 사용해도 됩니다. 이 변수는 워드프레스 관리페이지 또는 프런트 페이지에 항상 출력되는 것으로, 다음처럼 소스에 출력됩니다. 도메인은 상대적입니다.

<script type="text/javascript">
    var ajaxurl = 'http://localhost/wp-admin/admin-ajax.php';
</script>

보통은 사용자가 추가한 플러그인 또는 기능을 구현할 때, 결론적으로는 같은 URL(도메인 제외)이지만, 별도로 정의하는 때가 많습니다. 이 포스트에서는 함수를 이용한 것뿐이며, 다른 의도는 없습니다.

action, nonce

이 포스트에서는 Html 폼에 actionnonce 데이터를 포함하였기에 자바스크립트에서 별도로 추가하지 않았으며, 폼 데이터 ajax_form_data 변수에 모두 포함되어 전송되므로 따로 정의할 필요가 없습니다. 만약, 폼에서 추가하지 않는다면 다음처럼 자바스크립트 ajax 요청에 추가하면 됩니다.

$.ajax( {
    url: '<?php echo esc_url( admin_url('admin-ajax.php') ); ?>',
    type: 'POST',
    data: ajax_form_data,
    action: sendmail_formdata,
    nonce: '<?php wp_create_nonce( "ajax-form-nonce" ); ?>'
    success: function( response ) {
        $( '.msg' ).html( response.data.message );
    }
} )

이때 nonce 검증에 관하여 다음의 함수를 사용한 코드 편집이 필요할 수 있으며, 설명은 생략합니다. 변경 후에는 시험하여 올바르게 적용되는지 확인해야 합니다.

if ( check_ajax_referer( 'ajax-form-nonce', 'nonce', false ) == false ) {
    wp_send_json_error();
}

폼에 nonce 데이터를 포함할 때 자바스크립트에 nonce 데이터를 따로 추가하지 않는 게 좋습니다. 폼에 추가한 nonce 데이터 및 wp_verify_nonce 함수를 통한 검증은 자바스크립트에 추가한 nonce 데이터를 check_ajax_referer 함수로 검증한 것과 다르지 않습니다.

페이지 내 자바스크립트와 파일로 분리

이 포스트는 자바스크립트 코드를 wp_footer 훅을 사용하여 페이지 내에 추가하였습니다. 위의 코드를 별도의 파일을 만들어 추가한다면 스크립트 대기열 등록 훅을 사용하여 다음처럼 정의할 수 있습니다. 코드는 예를 든 것으로 파일 이름 등은 관련이 없습니다.

add_action( 'wp_enqueue_scripts', 'custom_enqueue_script' );
function custom_enqueue_script() {
        
    wp_enqueue_script( 'ajax-script', get_template_directory_uri() . '/js/script.js', array( 'jquery' ), '', false );
    // wp_enqueue_script( 'ajax-script', plugins_url( '/js/script.js', __FILE__ ), array('jquery') );

}

또, 자바스크립트에 사용할 admin-ajax.php 경로와 nonce 데이터를 wp_localize_script 함수를 사용하여 추가할 수 있습니다.

add_action( 'wp_enqueue_scripts', 'custom_enqueue_script' );
function custom_enqueue_script() {
        
    wp_enqueue_script( 'ajax-script', get_template_directory_uri() . '/js/script.js', array( 'jquery' ), '', false );
    // wp_enqueue_script( 'ajax-script', plugins_url( '/js/script.js', __FILE__ ), array('jquery') );

    wp_localize_script( 'ajax-script', 'ajax_object', array(
        'ajax_url' => admin_url( 'admin-ajax.php' ),
        'nonce' => wp_create_nonce( 'ajax-form-nonce )
    ) );
}

어떤 것이 좋다는 기준은 없으므로 사용자의 판단과 구성 기능에 따라 효율성을 우선으로 두면 됩니다. 이 포스트는 사실, 설명의 편의를 위해 하나의 파일로 처리한 것입니다.

워드프레스 Ajax 폼 완성 파일

다음 링크의 페이지에서 이 포스트의 완성 파일을 받을 수 있습니다. 간단한 플러그인 형식으로 구성한 것이며, 자바스크립트 코드에는 jQuery 일부 API를 사용하여 과정에 따른 폼 변화를 정의한 부분이 있습니다. 이 포스트의 핵심 내용은 아니므로 설명은 생략합니다.

다음 그림은 완성 파일을 사용하여 폼 데이터를 전송하고, 응답 메시지를 출력하는 최종 결과입니다.

워드프레스 Ajax 폼 Success 응답 메시지

다음 그림은 재전송 시간이 지나지 않은 때 응답 메시지입니다.

워드프레스 Ajax 폼 Error 응답 메시지

이 포스트 완성 파일을 열어 보면 107, 108번 줄에 다음 2, 3번 줄이 있습니다. 다음 2번 줄 비활성, 3번 줄 활성화 후 145번 줄 활성화하면 ajax 요청 응답에 폼에 입력한 데이터를 출력할 수 있습니다.

// 107, 108번 줄
wp_die( wp_send_json_success( array( 'message' => '메일 전송 요청 성공! 고맙습니다.' ) ) );
// wp_die( wp_send_json_success( array( 'message' => '메일 전송 요청 성공! 고맙습니다.', 'content' => $body ) ) );
// 145번 줄
// $( '#ajax_sendmail_form' ).html( response.data.content );

정리

워드프레스 시스템에서 특정 기능을 구현할 때 워드프레스에서 제공하는 함수, 방식 등을 먼저 검색하여 파악하면 상대적으로 구현이 쉬울 때가 많습니다. 폼 데이터 전송, 검증, 데이터 처리, 저장 등의 과정이 그에 포함됩니다.

이 포스트에서 폼 데이터 처리 등의 내용은 이미 다음의 지난 포스트에서 안내하였기에 생략했는데, 실제 그 내용이 더 많은 시간을 요구합니다. 한 번 둘러보세요.

위의 2번째 포스트의 완성 파일에 있는 ‘폼 데이터 포스트 저장’ 코드만 그대로 이번 포스트의 완성 파일에 추가하면 ajax 요청에 의한 폼 데이터도 포스트로 저장할 수 있습니다. 참고하세요.