오늘

javascript - XMLHttpRequest with promise 본문

javascript

javascript - XMLHttpRequest with promise

jhw715 2023. 3. 27. 11:51

XMLHttpRequest 사용 도중 status code에 따라 재요청을 해야하는 로직이 필요하게 되었다.

재요청 로직을 위해 재귀 호출로 구현을 하면서 promise 객체를 활용하였다.

 

재요청 로직 : status code 429 ( API key 사용량 초과)일때 다른 API key를 사용하여 API 호출. 최대 5번.

https://developer-lostark.game.onstove.com/usage-guide#api-errors

 

Lostark OpenAPI Developer Portal

USAGE GUIDE This guide describes how to interact with Lostark Open APIs. This is a technical guide intended for software developers, which walks you through implementation details and sample codes. Basic HTTP Making a valid HTTP request to Lostark Open API

developer-lostark.game.onstove.com

 

기본적인 XMLHttpRequest 코드 샘플

더보기
// XMLHttpRequest 객체 생성
let xmlHttpRequest = new XMLHttpRequest();   

// XMLHttpRequest 객체 초기화
// method : "GET" or "POST"
xmlHttpRequest.open(method, url, true);

// 서버 요구사항에 맞는 헤더 추가
xmlHttpRequest.setRequestHeader('accept', 'application/json');
xmlHttpRequest.setRequestHeader('rejectUnauthorized', 'false');

// 이벤트 핸들러 추가
xmlHttpRequest.onreadystatechange = () => {
    if( xmlHttpRequest.DONE == xmlHttpRequest.readyState ) {
        if(xmlHttpRequest.status == 200) {
        	// http status code : 200 = OK
            // 요청이 성공했을때 수행할 동작
        } else {
            // 그외 http status code 에 따른 동작 구현. 
        } ....
    }
};

// 목적 서버로 http 요청.
if(method === "POST") {
    xmlHttpRequest.setRequestHeader('Content-Type', 'application/json');
    // post body json 방식
    xmlHttpRequest.send(JSON.stringify(data));
} else {
    xmlHttpRequest.send();
}

 

예제 코드 

PromiseHttpRequest() 를 재귀함수로 구현하며 promise 객체 활용.

const apiMaxRetry = 5;

API : async (url, method, data) => {
        let retryCnt = 0;
        const PromiseHttpRequest = async () => {
        	// 사용가능한 API Key가 생길때까지 대기
            await keyManager.waitKeyLimit();
            return new Promise((resolve, reject) => {
                try {
                    const apiKeyIdx = keyManager.getKeyIdx();
                    let xmlHttpRequest = new XMLHttpRequest();   
                    xmlHttpRequest.open(method, url, true); // "GET", "POST"
                    xmlHttpRequest.setRequestHeader('accept', 'application/json');
                    xmlHttpRequest.setRequestHeader('authorization', 'bearer ' + keyManager.getKey(apiKeyIdx) + '');
                    xmlHttpRequest.setRequestHeader('rejectUnauthorized', 'false');
                    
                    xmlHttpRequest.onreadystatechange = () => {
                        if( xmlHttpRequest.DONE == xmlHttpRequest.readyState ) {
                            if(xmlHttpRequest.status == 200) {
                                // API 성공. api 서버가 응답한 resposeText값를 리턴
                                return resolve(xmlHttpRequest.responseText);
                            } else if(xmlHttpRequest.status == 429) {
                                // API 사용량 초과.
                                logger.log('error', `xmlHttpRequest  429 : ${JSON.stringify(xmlHttpRequest)}`);
                                
                                // API 사용량 초과가 발생한 api key lock
                                keyManager.setKeyLimit(apiKeyIdx);
                                
                                // retryCnt 체크
                                if(retryCnt >= apiMaxRetry) {
                                    throw new Error("Rate Limit Exceeded");
                                }
                                retryCnt++;
                                // API 재요청
                                return PromiseHttpRequest();
                            } else {
                                logger.log('error', `xmlHttpRequest status:${xmlHttpRequest.status}. statusText : ${xmlHttpRequest.statusText}`);
                                reject();
                            }
                        }
                    };

                    if(method === "POST") {
                        xmlHttpRequest.setRequestHeader('Content-Type', 'application/json');
                        xmlHttpRequest.send(JSON.stringify(data));
                    } else {
                        xmlHttpRequest.send();
                    }

                } catch (error) {
                    logger.log('error', `PromiseHttpRequest url : ${url}`);
                    logger.log('error', `PromiseHttpRequest error : ${error}`);
                    reject();
                }
            });
        };

        return PromiseHttpRequest();
    },

사용예제 

    request: async () => {
        let retData = [];
        // 'https://developer-lostark.game.onstove.com/news/events'
        const url = LostarkApiUrl.news_event;
        try {
            const tmpList = JSON.parse(await requestAPI.API(url, "GET", null));
            if(tmpList.length > 0)
                retData = tmpList;
        } catch (error) {
            logger.log('error', `news/events error : ${error}`);
        }
    
        return retData;
    }

 

++ XMLHttpRequest 사용시 onload 를 사용하려면 onerror 도 같이 정의해 주어야 한다.

onerror 를 정의하지 않았을때, 에러가 발생하면 핸들링을 하지 못하고 계속 대기하는 경우를 겪었다.

 

Comments