(function () { function ensureReady() { return new Promise((resolve, reject) => { if (!window.grecaptcha) return reject(new Error('grecaptcha not loaded')); window.grecaptcha.ready(resolve); }); } window.recaptchaV3Token = async function (action, formEl) { if (!window.__recaptchaSiteKey) throw new Error('__recaptchaSiteKey missing'); await ensureReady(); const token = await window.grecaptcha.execute(window.__recaptchaSiteKey, { action }); // formEl이 있으면 hidden input 자동 세팅 if (formEl) { const input = formEl.querySelector('input[name="g-recaptcha-response"]'); if (input) input.value = token; } return token; }; })();