요즘 많이 쓰고 있는 기술인 AJAX를 사용하는데 있어, 제이쿼리 라이브러리가 많이 쓰이고 있다. 하지만 어쩔 수 없이 제이쿼리를 쓸수 없는 상황이라면 어떻게 해야 할까?
정보를 가져올수가 없는 구조이다.
더군다나 제이쿼리를 이용한 AJAX 역시 내부적으로는 XmlHttpRequest (이하 xhr) 을 이용하기 때문에 xhr을 이해하고 넘어가는것이 비동기 처리에 있어 큰 도움이 될것이라 생각하고 xhr을 직접 써보기로 했었다.
구글 드라이브 관련 REST API를 프로젝트에 도입하면서 제이쿼리가 없는 환경에서 xhr을 구현과정이 어려웠지만, 전반적인 데이터통신에 대한 어떤 통찰을 얻어서 오히려 더 나은 선택이지 않았나 싶다.
Google Drive Rest API를 통해 어떤식으로 작성을 하는지 코드를 보면서 알아보도록 하자.
먼저, 사용자 토큰을 얻어내는 함수를 작성했다. 이 함수는 앞으로 모든 비동기 요청과정에서 기본이 된다. 하나하나 차근히 설명하겠다. 전체코드를 쭉 훑어보시길
private xhrWithAuth(method: string, url: string, callback, params, requestBody, requestHeader) {
let access_token;
let retry = true;
let that = this;
console.log("xhr 을 시작합니다");
noInteractiveGetToken();
// 토큰을 얻는 과정이다. 이 과정은 토큰이 없는 경우 자동적으로 실패를 리턴하게 된다.
// 초기 프로그램 실행시, 사용자 경험 향상을 위해서 interactive 값을 false로 해두었다.
// interactive 가false 값을 갖게되면, 사용자에게 토큰정보를 구할 인터페이스창이 뜨지 않고 에러 // 도 나지않고 그대로 프로그램이 멈추기때문에,
// try catch 문으로 강제로 에러를 잡아내 프로그램이 실행되도록 하였다.
// catch 부분을 보면 에러가났을 경우 토큰을 얻을 수 있는 사용자 페이지로 라우팅 되게 해두었다.
function noInteractiveGetToken() {
try {
chrome.identity.getAuthToken({ interactive: false }, function (token) {
if (chrome.runtime.lastError) {
console.log(chrome.runtime.lastError);
console.log("토큰을 구하는 과정에서의 에러입니다.");
callback(chrome.runtime.lastError);
return;
}
access_token = token;
requestStart();
});
} catch (e) {
callback(chrome.runtime.lastError);
}
}
function formatParams(params) {
return "?" + Object
.keys(params)
.map(function (key) {
return key + "=" + params[key]
})
.join("&");
}
function requestStart() {
var xhr = new XMLHttpRequest();
console.log(url + formatParams(params));
xhr.open(method, url + formatParams(params), true);
xhr.setRequestHeader('Authorization', 'Bearer ' + access_token);
if (requestHeader) {
Object
.keys(requestHeader)
.forEach(header => {
xhr.setRequestHeader(header, requestHeader[header]);
})
}
xhr.onload = function requestComplete() {
if (xhr.status == 401 && retry) {
console.log("리퀘스트 결과에 따른 401 에러입니다.");
retry = false;
chrome.identity.removeCachedAuthToken({ token: access_token },
noInteractivegetToken);
} else {
callback(null, xhr.status, xhr.response);
}
}
if (requestBody) {
xhr.send(requestBody);
} else {
xhr.send();
}
}
}
xhrWithAuth 함수의 매개변수를 보면, method, url , callback, params,requestHeader,requestBody 로 총 6가지이다.
method 는 http 메소드를 지칭한다. GET, POST, PATCH같은 내용이 들어간다.
url은 실제 비동기 요청을 보낼 url 이다. 참고로 xhr은 SOP정책을 피할 수 있다 .
callback 은 이 토큰을 얻고 난뒤에 실행할 콜백함수를 의미한다.
params 는 쿼리스트링에 들어갈 객체이다.
requestHeader 와 body는 http 메세지를 작성할때 쓰인다.
내부함수로는 3가지가 있다.
function noInteractiveGetToken
function formatParams(params)
function requestStart()
이 3가지 함수를 통해 토큰을 가져오는 과정을 실행할 수 있다.
가장 먼저 실행되는 것은
function noInteractiveGetToken
interactive란 단어는 Google API에서 사용되는 언어로 사용자에게 권한을 물어보는 인터페이스창을 지칭한다. 즉 이 함수는 인터페이스를 띄우지 않고 토큰을 얻어온다는 의미이다.
interactive는 이런걸 의미한다.
function noInteractiveGetToken() {
try {
chrome.identity.getAuthToken({ interactive: false }, function (token) {
if (chrome.runtime.lastError) {
console.log(chrome.runtime.lastError);
console.log("토큰을 구하는 과정에서의 에러입니다.");
callback(chrome.runtime.lastError);
return;
}
access_token = token;
requestStart();
});
} catch (e) {
callback(chrome.runtime.lastError);
}
}
이 함수를 보면 chrome 객체를 쓰고 있는데, 이는 크롬 앱에서만 사용가능한 객체이다.
이 포스트는 xhr에 집중되어 있으므로, 술술 넘기겠다.
어쨌든 저 함수를 통과시키고 나면, 토큰을 얻게 되고, 우리는 비로소 구글 드라이브에 xhr을 보낼 준비를 마쳤다. 이제 객체를 준비해서 보내보자. 에러가 나지 않았다면 다음 함수를 호출한다.
function requestStart() {
var xhr = new XMLHttpRequest();
xhr.open(method, url + formatParams(params), true);
xhr.setRequestHeader('Authorization', 'Bearer ' + access_token);
if (requestHeader) {
Object
.keys(requestHeader)
.forEach(header => {
xhr.setRequestHeader(header, requestHeader[header]);
})
}
xhr.onload = function requestComplete() {
if (xhr.status == 401 && retry) {
console.log("리퀘스트 결과에 따른 401 에러입니다.");
retry = false;
chrome.identity.removeCachedAuthToken({ token: access_token },
noInteractivegetToken);
} else {
callback(null, xhr.status, xhr.response);
}
}
if (requestBody) {
xhr.send(requestBody);
} else {
xhr.send();
}
}
먼저 new 연산자를 통해 xhr 객체를 준비를 한다.
xhr.open()을 통해 매개변수로 받았던 method, url, params을 넘겨준다.
url 뒤에는 parmas 을 자동으로 붙여주는 함수를 이용했다.
function formatParams(params) {
return "?" + Object
.keys(params)
.map(function (key) {
return key + "=" + params[key]
})
.join("&");
}
params는 객체형태로 받게되는데, 객체으 함수는 keys와 map 등을 통해서 ?key=value&key=value 형식으로 붙여준다. 이 함수는 꽤나 써먹을만 하다.
다시 xhr로 돌아와서 open 함수를 실행한 후에는 header넣어주어야 한다.
xhr.setRequestHeader('Authorization', 'Bearer ' + access_token);
if (requestHeader) {
Object
.keys(requestHeader)
.forEach(header => {
xhr.setRequestHeader(header, requestHeader[header]);
})
}
토큰은 반드시 있어야 하므로 직접 넣어주고, 매개변수로 받은 requestHeader가 있다면 하나하나 작성하도록 한다.
xhr.onload = function requestComplete() {
if (xhr.status == 401 && retry) {
console.log("리퀘스트 결과에 따른 401 에러입니다.");
retry = false;
chrome.identity.removeCachedAuthToken({ token: access_token },
noInteractivegetToken);
} else {
callback(null, xhr.status, xhr.response);
}
}
if (requestBody) {
xhr.send(requestBody);
} else {
xhr.send();
}
onload 함수는 xhr이 동작을 완료했을때를 기점으로 실행시키는 콜백함수인데, 에러가 나지 않았다면 우리가 매개변수로 받았던 콜백함수를 넘겨주도록 했다.
xhr이 다 정리가 되었다면 requestBody 유무를 판단해, send 함수를 호출한다.
다음 포스트에서 이 xhrWithAuth 함수에 적당한 callback함수를 넣어줌으로써
드라이브 정보를 가져오는 코드를 작성하겠다.
댓글
댓글 쓰기