테스트 사이트 - 개발 중인 베타 버전입니다

ajax 요청을 간편하게

· 12개월 전 · 1093 · 5

매번 ajax 요청 코드를 작성하는 게 귀찮아서 함수로 만들어서 사용 중입니다.

필요하다면 상황에 맞게 수정하셔서 사용하시면 되지 싶습니다.
 

[code]

const App = window.App || {};
App.callbacks = {};

document.addEventListener('DOMContentLoaded', function () {
    addProgressStyles();
    addProgressElement();
    document.addEventListener('click', handleButtonClick);
});

function addProgressStyles() {
    const style = document.createElement('style');
    style.textContent = `
        #progress { display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 9999; overflow: hidden; background: #000; opacity: .1; }
        #progress:after { content: ""; position: fixed; top: calc(50% - 30px); left: calc(50% - 30px); border: 6px solid #60718b; border-top-color: #fff; border-bottom-color: #fff; border-radius: 50%; width: 60px; height: 60px; animation: animate-progress 1s linear infinite; }
        @keyframes animate-progress {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
    `;
    document.head.appendChild(style);
}

function addProgressElement() {
    const progress = document.createElement('div');
    progress.id = 'progress';
    document.body.appendChild(progress);
}

function handleButtonClick(event) {
    if (event.target.classList.contains('btn-form-submit-ajax')) {
        handleAjaxFormSubmit(event.target);
    }
}

// 상황에 맞게 수정하거나 삭제
const API_BASE_URL = window.API_FULL_BASE_URL || '/api/v1';
let cachedCsrfToken = null;

// CSRF 토큰을 가져오는 함수 //상황에 맞게 수정하거나 삭제
async function getCsrfToken() {
    if (cachedCsrfToken) return cachedCsrfToken;
    
    try {
        const response = await fetch(`${API_BASE_URL}/token/getUserCsrfToken`);
        if (!response.ok) throw new Error('Failed to fetch CSRF token');
        const data = await response.json();
        if (!data.token) throw new Error('CSRF token not found in response');
        cachedCsrfToken = data.token;
        return cachedCsrfToken;
    } catch (error) {
        console.error('Error fetching CSRF token:', error);
        throw error;
    }
}

// CSRF 토큰을 초기화하는 함수
function resetCsrfToken() {
    cachedCsrfToken = null;
}

// AJAX 요청을 보내는 함수
async function sendAjaxRequest(method, url, data, isFormData = false, isLoading = false, retryCount = 0) {
    try {
        // CSRF 토큰 관련 함수 및 아래 부분에 상황에 맞게 수정하거나 삭제
        const csrfToken = await getCsrfToken();
        const options = {
            method,
            headers: {
                'X-CSRF-Token': csrfToken
            }
        };

        if (method.toUpperCase() !== 'GET') {
            if (isFormData && data instanceof FormData) {
                options.body = data;
            } else if (!isFormData) {
                options.headers['Content-Type'] = 'application/json';
                options.body = JSON.stringify(data);
            } else {
                throw new Error('Invalid data type for isFormData=true');
            }
        } else if (data) {
            url += (url.includes('?') ? '&' : '?') + new URLSearchParams(data).toString();
        }

        if (isLoading) toggleProgress(true);

        const response = await fetch(url, options);
        const responseData = await response.json();

        if (isLoading) toggleProgress(false);

        if (!response.ok) {
            if (response.status === 419 && retryCount < 3) { // CSRF 토큰 만료 시 반환하는 상태 코드, 최대 3번 재시도
                //console.log('CSRF 토큰이 만료되어 새로운 토큰을 요청합니다.');
                resetCsrfToken();
                // 재귀적으로 동일한 요청을 다시 시도
                return sendAjaxRequest(method, url, data, isFormData, isLoading, retryCount + 1);
            }
            throw new Error(responseData.error || 'Request failed');
        }

        return responseData;
    } catch (error) {
        handleError(error);
        return Promise.reject(error); // 호출한 곳으로 에러 전달
    }
}

// AJAX 폼 제출 처리 함수
async function handleAjaxFormSubmit(button) {
    const form = button.closest('form');
    const url = button.getAttribute('data-target');
    const beforeSubmitFunctionName = button.getAttribute('data-beforesubmit');
    const callbackFunctionName = button.getAttribute('data-callback');
    const loading = button.getAttribute('data-loading') === 'true';

    // 폼 전송 전 함수 실행
    if (beforeSubmitFunctionName) {
        const shouldProceed = await executeCallback(beforeSubmitFunctionName);
        if (!shouldProceed) {
            return; // beforeSubmit 함수가 false를 반환하면 폼 전송 중단
        }
    }

    // TinyMCE 에디터 내용 저장 - 타에디터 사용 시 해당 에디터에 맞게 수정.
    document.querySelectorAll('.editor-form').forEach(editor => {
        if (tinymce.get(editor.id)) {
            tinymce.get(editor.id).save();
        }
    });

    if (!validateForm(form)) return; // 폼을 체크하는 함수, 삭제 후 사용

    button.disabled = true;

    try {
        const formData = new FormData(form);
        const data = await sendAjaxRequest('POST', url, formData, true, loading);

        if (data.result === "success") {
            if (callbackFunctionName) {
                executeCallback(callbackFunctionName, data);
            }
        } else {
            alert('오류: ' + (data.message || '알 수 없는 오류가 발생했습니다.'));
        }
    } catch (error) {
        handleError(error);
    } finally {
        button.disabled = false;
    }
}
 

async function sendCustomAjaxRequest(method, url, data, loading = false, callbackFunctionName = null) {
    try {
        const responseData = await sendAjaxRequest(method, url, data, false, loading);
        if (callbackFunctionName) {
            executeCallback(callbackFunctionName, responseData);
        }
        return responseData;
    } catch (error) {
        handleError(error);
        return Promise.reject(error);
    }
}

// 콜백 함수 실행
function executeCallback(callbackFunctionName, data) {
    if (typeof App.callbacks[callbackFunctionName] === 'function') {
        App.callbacks[callbackFunctionName](data);
    }
}

// 에러 핸들러
function handleError(error) {
    const errorMessage = error.message || '알 수 없는 오류가 발생했습니다.';
    if (errorMessage.includes('CSRF 토큰이 만료되었습니다')) {
        console.error(errorMessage);
    } else {
        console.error('Error: ' + errorMessage);
    }
}

// 프로그레스 표시 토글 함수
function toggleProgress(show = true) {
    const progress = document.getElementById('progress');
    if (progress) progress.style.display = show ? 'block' : 'none';
}

function prettyPrintJson(data) {
    console.log(JSON.stringify(data, null, 2));
}

// 콜백 함수 등록
App.registerCallback = function(name, callback) {
    if (typeof callback === 'function') {
        App.callbacks[name] = callback;
    } else {
        //console.error(`Invalid callback function for ${name}`);
    }
};

[/code] 


## 폼 전송 시
<form name="frm" id="frm" enctype="multipart/form-data">
<!-- form html --->
<button type="button" value="확인"  data-target="url경로" data-beforesubmit="beforeSubmit" data-callback="fnCallback">확인</button>
</form>

## 폼 이외 전송 시
<button type="button" value="확인" onclick="submitAjax();">확인</button>

<script>

function submitAjax() {
    var data = {data1:data1, data2:data2}; //전송할 데이터
    var url = '/ajax.php'; //ajax 파일 경로

    try {
       const result = await sendCustomAjaxRequest('POST', url, data, true, 'customFnCallback');
    }
    catch (error) {
        alert('순위 검색에 실패했습니다: ' + (error.message || '알 수 없는 오류'));
    }
}

// ajax 전송 전 또는 전송 후 실행할 함수 , 물론 함수명은 샘플임.
App.registerCallback('beforeSubmit', function(data) {
    console.log(data);
});

App.registerCallback('fnCallback', function(data) {
    console.log(data);
});

App.registerCallback('customFnCallback', function(data) {
    console.log(data);
});

</script>

댓글 작성

댓글을 작성하시려면 로그인이 필요합니다.

로그인하기

댓글 5개

12개월 전

감사합니다 ^^

12개월 전

감사합니다.

함수를 자체적으로 만드는 분을 보면 대단하다고 생각합니다.

 

12개월 전

오호 css 까지 동적으로 생성하셨네요. 추천

감사합니다.

12개월 전

감사합니다

게시글 목록

번호 제목
23254
23244
23241
23236
23200
23199
23179
23174
23138
23128
23125
23116
23109
23099
23092
23083
23079
23063
23050
23036
23029
23007
23003
22983
22959
22943
22939
22934
22905
22897