본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 15. 14:51

WordPress의 로딩 지연은 이제 그만! 오늘 바로 Ajax를 사용해 보세요!

요약

WordPress 환경에서 페이지 새로고침 없이 서버와 통신하는 AJAX 구현 방법을 설명합니다. admin-ajax.php의 작동 원리, 액션 이름 등록 규칙, 보안을 위한 nonce 사용법 및 디버깅 팁을 다룹니다.

핵심 포인트

  • WordPress AJAX의 핵심은 action 파라미터를 통한 훅(hook) 연결임
  • 로그인/비로그인 사용자에 따라 wp_ajax_ 및 wp_ajax_nopriv_를 구분하여 등록해야 함
  • action 키 누락 시 WordPress는 '0'을 반환하며, 이는 정상적인 동작임
  • 보안을 위해 nonce를 사용하고 스크립트 인큐잉을 준수해야 함

WordPress 폼을 만듭니다. 페이지를 새로고침하지 않고도 저장되어야 합니다.

'전송(Send)'을 클릭합니다. 아무 일도 일어나지 않습니다.

또는 네트워크(Network) 탭에 admin-ajax.php가 단 하나의 문자 0을 반환하는 것이 표시됩니다.

이 상황이 익숙하게 들린다면, 당신만 그런 것이 아닙니다. 6년 동안 WordPress 테마, 플러그인, 그리고 WooCommerce 프로젝트를 구축해 오면서, 저는 클라이언트 사이트에서 동일한 패턴을 계속 목격해 왔습니다. JavaScript는 괜찮아 보입니다. PHP도 괜찮아 보입니다. 하지만 요청이 콜백(callback)에 도달하지 않거나, 잘못된 훅(hook)에 도달하거나, 응답이 전혀 JSON 형식이 아닙니다.

대부분의 경우, 문제는 JavaScript 기술력이 아닙니다. WordPress가 백그라운드 요청을 어떻게 라우팅(routing)하는지 이해하지 못하는 것이 문제입니다.

이 글에서는 다음의 모든 내용을 다룹니다: WordPress에서 AJAX란 무엇인지, admin-ajax.php가 어떻게 작동하는지, nonce를 이용한 보안, 스크립트 인큐잉(enqueueing), 프론트엔드(frontend) vs 관리자(admin), JSON 응답, 디버깅(debugging), 그리고 대신 REST를 사용해야 할 때입니다. 예제 코드를 자신의 플러그인에 복사하고 접두사(prefix)를 변경하여 사용하세요.

WordPress에서 AJAX란 무엇인가?

AJAX는 브라우저가 페이지를 새로고침하지 않고도 백그라운드에서 서버와 통신할 수 있게 해줍니다.

당신이 이미 보았을 실제 사례들:

  • 설정 폼을 제출하고 즉시 "저장됨!"을 표시
  • 버튼을 클릭했을 때 더 많은 블로그 포스트를 로드
  • 헤더의 장바구니 수량 업데이트
  • 페이지를 떠나지 않고 연락처 폼 저장

WordPress는 모든 플러그인을 위해 커스텀 API 엔드포인트(endpoint)를 직접 만들라고 요구하지 않습니다. 대신 하나의 내장된 라우터(router)를 제공합니다:

/wp-admin/admin-ajax.php

당신은 PHP에서 **액션 이름(action name)**을 등록합니다. JavaScript는 모든 POST 요청에 동일한 액션을 보냅니다. WordPress는 이 둘을 연결합니다. 이는 add_action()과 유사하지만, 페이지 로드가 아닌 브라우저로부터 트리거됩니다.

대부분의 프로젝트를 망가뜨리는 단 하나의 규칙

모든 AJAX 요청에는 반드시 다음이 포함되어야 합니다:

action=your_action_name

WordPress는 해당 값을 읽고 다음과 같은 훅(hook)을 실행합니다:

  • wp_ajax_{action} — 로그인한 사용자 전용
  • wp_ajax_nopriv_{action} — 로그아웃된 방문자

만약 PHP에서 wp_ajax_send_form_data를 등록했는데 JavaScript가 send-form-data를 보낸다면, 아무것도 실행되지 않습니다.

만약 wp_ajax_만 등록하고 로그아웃된 상태로 공개 사이트에서 테스트한다면, 아무것도 실행되지 않습니다.

action 키를 완전히 누락하면, WordPress는 0을 반환합니다.

이는 정상적인 WordPress의 동작입니다. 이를 미리 알고 있다면 디버깅 속도가 훨씬 빨라집니다.

전체 요청 루프 (The full request loop)

정상적으로 작동하는 설정에서는 다음과 같은 일이 일어납니다:

  1. 사용자가 양식(form)에서 제출(Submit)을 클릭합니다.
  2. JavaScript가 preventDefault()를 호출하여 페이지가 새로고침되지 않도록 합니다.
  3. JavaScript가 action, nonce, 그리고 사용자 필드들을 포함하여 admin-ajax.php로 POST 요청을 보냅니다.
  4. WordPress가 wp_ajax_{action} 또는 wp_ajax_nopriv_{action}을 실행합니다.
  5. PHP 콜백(callback) 함수가 실행되어, nonce를 검증하고 입력을 정화(sanitize)합니다.
  6. PHP가 wp_send_json_success() 또는 wp_send_json_error()를 통해 JSON을 반환합니다.
  7. JavaScript가 response.success를 읽고 UI를 업데이트합니다.

이 체인의 단계 중 하나라도 잘못되면, 코드의 절반이 맞더라도 기능이 "고장 난" 것처럼 느껴집니다.

파트 1: 첫 번째 AJAX 핸들러 (Your first AJAX handler)

가장 간단한 예제부터 시작해 보세요. 아직 nonce는 포함하지 않은, 로그인된 관리자 요청입니다 (보안은 파트 2에서 추가합니다).

PHP에서 액션(action)을 등록합니다:

add_action( 'wp_ajax_send_form_data', 'myplugin_send_form_data_callback' );

function myplugin_send_form_data_callback() {
...

주의할 점:

  • 액션 이름은 send_form_data입니다. 실제 프로젝트에서는 다른 플러그인과 충돌하지 않도록 플러그인 이름을 접두사로 붙이세요 (myplugin_send_form_data).
  • JavaScript는 반드시 정확히 action: 'send_form_data'를 보내야 합니다.
  • wp_send_json_success()는 JSON을 출력하고 실행을 중단합니다. 따라서 그 뒤에 die()를 사용할 필요가 없습니다.

wp-admin 내부의 JavaScript에서 요청을 보냅니다:

jQuery.ajax({
    method: 'POST',
    url: ajaxurl,
...

중요한 세부 사항: 전역 변수인 ajaxurl은 WordPress 관리자 영역(admin area)에 자동으로 존재합니다. 하지만 공개 프론트엔드(frontend)에는 존재하지 않습니다. 프론트엔드 양식의 경우 wp_localize_script()를 사용해야 하며, 이는 파트 3에서 다룹니다.

파트 2: nonce를 사용한 보안 AJAX (Secure AJAX with nonces)

AJAX 엔드포인트(endpoints)는 공개 URL입니다. 누군가 액션 이름을 추측한다면 누구든 POST 요청을 보낼 수 있습니다.

데이터를 저장, 삭제 또는 변경하는 모든 작업에 대해 **nonce (논스)**를 검증해야 합니다. nonce는 요청이 귀하의 사이트에서 발생했음을 증명하기 위해 WordPress가 생성하는 수명이 짧은 토큰입니다.

1단계 — nonce를 생성하고 JavaScript로 전달하기

wp_localize_script(
    'myplugin-admin-js',
    'my_ajax_obj',
...

이 코드는 스크립트에 필요한 두 가지 값인 AJAX URL과 nonce 문자열을 포함하는 전역 JavaScript 객체 my_ajax_obj를 생성합니다.

스크립트 핸들 'myplugin-admin-js'wp_enqueue_script()에서 사용한 핸들과 일치해야 합니다.

문자열 'myplugin-ajax'는 nonce 액션 이름입니다. 검증할 때 동일한 문자열을 사용하세요.

2단계 — JavaScript에서 nonce 전송하기

data: {
    action: 'send_form_data',
    nonce: my_ajax_obj.nonce,
...

3단계 — 다른 작업을 수행하기 전에 PHP에서 검증하기

function myplugin_send_form_data_callback() {

    if ( ! isset( $_POST['nonce'] ) ) {
...

nonce가 없다면 인터넷상의 누구나 귀하의 엔드포인트(endpoint)에 스팸을 보낼 수 있습니다. nonce가 있다면, 등록된(enqueued) 스크립트에서 생성된 유효한 토큰을 포함한 요청만이 검증을 통과할 수 있습니다.

파트 3: 스크립트를 등록(Enqueue)하고 데이터를 올바르게 전달하기

폼이 커스텀 관리자 페이지에 있는 경우, 해당 화면에 JavaScript를 로드하고 AJAX URL을 직접 전달해야 합니다.

add_action( 'admin_enqueue_scripts', 'myplugin_load_admin_scripts' );

function myplugin_load_admin_scripts() {
...

각 부분의 역할:

부분목적
admin_enqueue_scriptswp-admin에서만 에셋(assets)을 로드함
...

/wp-admin/admin-ajax.php를 하드코딩하지 마세요. 항상 admin_url()을 사용해야 합니다.

파트 4: 조각들을 하나로 합치기

깔끔한 플러그인은 책임을 분리합니다:

  • Assets 클래스 — JS를 등록(enqueue)하고, nonce와 URL을 전달
  • Admin 페이지 — HTML 폼을 렌더링
  • Ajax 클래스wp_ajax_* 훅(hook)과 콜백(callback)을 등록

JavaScript 측면:

(function ($) {
    $(document).ready(function () {

...

사용자가 전송(Send)을 클릭하면:

  1. 일반적인 폼 제출 (Normal form submit)이 차단됩니다.
  2. POST 요청이 admin-ajax.php로 전송됩니다.
  3. WordPress가 action=send_form_data를 읽습니다.
  4. wp_ajax_send_form_data 훅 (Hook)이 실행됩니다.
  5. PHP가 논스 (nonce)를 검증하고 JSON을 반환합니다.
  6. JavaScript가 결과를 표시합니다.

이것이 전체 시스템입니다. 숨겨진 것은 없습니다.

파트 5: JSON 응답 — 성공 및 오류

WordPress 헬퍼 (helpers)를 사용하세요. 무작위 텍스트를 echo하고 die()를 호출하지 마세요. 이는 JavaScript의 JSON 파싱 (parsing)을 깨뜨립니다.

성공 (Success):

wp_send_json_success(
    array(
        'message' => 'Saved!',
...

오류 (Error):

wp_send_json_error(
    array(
        'message' => 'Something went wrong.',
...

JavaScript가 수신하는 내용:

{
    "success": true,
    "data": {
...

response.data를 읽기 전에 항상 response.success를 확인하세요.

저장하기 전에 항상 새니타이즈 (sanitize) 하세요:

$name  = sanitize_text_field( wp_unslash( $_POST['user_name'] ?? '' ) );
$email = sanitize_email( wp_unslash( $_POST['email'] ?? '' ) );
$id    = absint( $_POST['item_id'] ?? 0 );

파트 6: 로그아웃된 사용자를 위한 프론트엔드 AJAX

공개 연락처 폼에는 nopriv 훅 (hook)이 필요합니다.

동일한 콜백 (callback)으로 둘 다 등록하세요:

add_action( 'wp_ajax_send_form_data', 'myplugin_send_form_data_callback' );
add_action( 'wp_ajax_nopriv_send_form_data', 'myplugin_send_form_data_callback' );
훅 (Hook)실행 시점
wp_ajax_{action}사용자가 로그인한 상태일 때
wp_ajax_nopriv_{action}사용자가 로그인하지 않은 상태일 때

이것은 가장 흔한 "나한테는 되는데 클라이언트에게는 안 되는" 버그입니다. 당신은 관리자 (admin)로 테스트하지만, 실제 방문자는 로그아웃된 상태입니다.

관리자 전용 작업 (글 삭제, 설정 저장 등)의 경우, nopriv를 건너뛰고 대신 콜백 내부에서 current_user_can()을 추가하세요.

파트 7: 프론트엔드에서 인큐 (Enqueue)

관리자 페이지는 admin_enqueue_scripts를 사용합니다. 공개 사이트는 wp_enqueue_scripts를 사용합니다:

add_action( 'wp_enqueue_scripts', 'myplugin_load_public_scripts' );

function myplugin_load_public_scripts() {
...

동일한 패턴입니다. 다만 훅 (hook)만 다릅니다. 만약 스크립트가 폼 (form)이 나타나는 페이지에 인큐 (enqueue)되지 않았다면, PHP 코드가 완벽하더라도 JavaScript는 절대 실행되지 않습니다.

Part 8: jQuery 대신 fetch() 사용하기

WordPress는 어떤 라이브러리를 사용하는지 상관하지 않습니다. 중요한 것은 POST, action, 그리고 nonce입니다.

document.querySelector('.myplugin-ajax-form')?.addEventListener('submit', async function (event) {

    event.preventDefault();
...

Part 9: admin-ajax.php vs REST API

WordPress는 두 가지 비동기 (async) 패턴을 제공합니다:

admin-ajax.php

  • URL: /wp-admin/admin-ajax.php
  • add_action( 'wp_ajax_*' )로 등록
  • 클래식 플러그인, wp-admin UI, 작은 규모의 폼에 가장 적합

REST API

  • URL: /wp-json/your-namespace/v1/your-route
  • register_rest_route()로 등록
  • 헤드리스 (headless) 사이트, Gutenberg 블록, 모바일 앱에 가장 적합

WordPress 관리자 내부에서 이루어지는 대부분의 테마 및 플러그인 작업에는 여전히 admin-ajax.php가 가장 빠른 경로입니다. 표준 HTTP 라우트 (route), 외부 소비자 (external consumers), 또는 블록 에디터 (block editor) 통합이 필요한 경우에 REST API로 전환하세요.

Part 10: AJAX 실패 시 디버깅하기

추측하기 전에 다음 사항들을 순서대로 확인하세요:

  1. Action 이름 — PHP 훅 (hook)과 JavaScript의 action 필드에 동일한 문자열이 있습니까?
  2. 네트워크 (Network) 탭 — 개발자 도구 (DevTools)를 열고, admin-ajax.php를 찾아 응답 본문 (response body)을 읽으세요.
  3. 로그아웃 상태인가요?wp_ajax_nopriv_를 등록했습니까?
  4. 스크립트가 로드되었나요? — 이 특정 페이지에서 wp_enqueue_script가 실행되고 있습니까?
  5. Nonce — 생성되어 JS로 전달되고, 다시 전송되었으며, 동일한 action 문자열로 검증되었습니까?

일반적인 응답:

응답의미
0Action이 등록되지 않았거나 콜백 (callback)을 찾을 수 없음
...

PHP에서 임시로 로그 남기기:

error_log( print_r( $_POST, true ) );

wp-config.php에서 WP_DEBUG_LOG를 활성화하고 wp-content/debug.log를 확인하세요.

빠른 참조 (Quick reference)

목적사용법
로그인한 사용자의 AJAX 처리add_action( 'wp_ajax_{action}', 'callback' )
...

흔한 실수

  1. JavaScript에서 action 누락 — WordPress가 0을 반환함
  2. PHP와 JS 간의 action 이름 불일치
  3. 공개 양식(public forms)에 wp_ajax_nopriv_를 설정하지 않음
  4. 쓰기 작업(write operations) 시 Nonce를 사용하지 않음
  5. wp_send_json_* 대신 echo를 사용함
  6. /wp-admin/admin-ajax.php를 하드코딩함
  7. $_POST 데이터를 정제(sanitizing)하지 않음
  8. 잘못된 훅(hook)이나 잘못된 관리자 페이지에서 스크립트를 스케줄링함

배포 전 체크리스트

  • action 이름이 고유하며 접두사(prefix)가 붙어 있음 (myplugin_send_form_data)
  • wp_ajax_가 등록됨 (공개 페이지라면 wp_ajax_nopriv_도 포함)
  • Nonce가 생성되어 JS로 전달되고 PHP에서 검증됨
  • 입력값은 정제(sanitized)되었고, 출력값은 이스케이프(escaped) 처리됨
  • wp_send_json_success / wp_send_json_error를 사용함
  • 올바른 훅(hook)과 페이지에서 스크립트가 인큐(enqueued)됨
  • 로그인 상태와 로그아웃 상태 모두에서 테스트함
  • 네트워크(Network) 탭에서 0, -1, 또는 500 에러를 확인함

이를 통해 만들 수 있는 것들

흐름이 명확해지면, 많은 기능들을 간단하게 구현할 수 있습니다:

  • 실시간 검색 및 필터
  • 무한 스크롤 (Infinite scroll) / 더 보기 (load more)
  • 즉시 저장되는 테마 옵션 패널
  • 페이지 새로고침 없이 갱신되는 관리자 대시보드
  • WooCommerce 스타일의 장바구니 업데이트

모두 동일한 WordPress 훅(hook) 방식입니다 — JavaScript에서 트리거될 뿐입니다.

더 읽어보기

Medium — 추가 예제가 포함된 전체 가이드:

https://medium.com/@hemal.akanda.39/no-refresh-in-wordpress-by-ajax-4942c5e80bd2

Substack — 코드가 없는 쉬운 설명 버전:

https://substack.com/@mdhemalakhand/note/p-201821073

제가 지금 만들고 있는 것

수년간의 WordPress 클라이언트 작업 이후, 저는 AppsLaunch를 만들고 있습니다. 이는 하나의 대시보드에서 지역 서비스 비즈니스를 위한 브랜드화된 Android 및 iPhone 앱을 제작하는 서비스입니다.

작동 방식:

  • 회사를 생성하고 로고, 색상, 서비스 및 제품을 추가합니다.
  • 고객은 앱에서 둘러보고 문의를 보냅니다 — 리드(leads)는 대시보드 수신함에 도착합니다.
  • 클라우드에서 서명된 Android 및 iPhone 앱을 빌드하여 Google Play 및 App Store에 게시합니다.
  • 각 비즈니스는 고유한 앱 이름과 스토어 등록 정보를 갖게 됩니다 — 일반적인 빌더 브랜드가 아닙니다.

월 $30부터 시작하는 플랜. 유료 플랜에는 리드당 수수료가 없습니다.

https://applaunch.teamzlab.com/

마치며

WordPress AJAX는 모든 프로젝트에서 네 가지 아이디어로 요약됩니다:

  1. PHP에서 액션(action)을 등록합니다.
  2. JavaScript에서 동일한 액션 이름을 보냅니다.
  3. 논스(nonces)와 새니타이제이션(sanitization)으로 요청을 보호합니다.
  4. 깨끗한 JSON을 반환합니다.

이 루프를 한 번 이해하고 나면, 페이지 새로고침이 없는 폼(no-refresh forms)이 더 이상 마법처럼 느껴지지 않고 일반적인 WordPress 개발처럼 느껴지기 시작할 것입니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0