개발팀장이 되면서 겪게된 점들 1

이미지
                                                         <팀원을 모집하기 위해 고군분투하는 모습이다. > 첫 한달  개발팀장을 맡다 2021년 5월 , 기존에 있던 CTO분이 휴직(개인사)을 하게 되면서    개발에 대한 모든 권한을 내게 일임하였다.   개발에 대한 모든 의사결정을 전부 내게 맡긴 것으로 ,   어느 정도 규모가 있는 회사의 의사결정권한을 갖게 된 것은 그만큼 내게 큰 신뢰가 있었음을   알수 있게해주는 대목이었다. 그러나 전혀 예측하지 않았던 상황이기에 준비가 되어있지 않았던만큼 처음에는 삐걱거렸다. 가장 첫번째로 어려움을 겪었던 것은 업무의 배분이었다.   관리자가 되니까 해야할일은 업무를 만들고 또 그것을 팀원들에게 분배하고 잘 되고있는지 취합하고 관리감독을 하는것이었다.   군 시절 장교로 복무하면서 겪어봤던 일이긴 했지만, 군복무 당시에도 그닥 잘 하지는 않았던 것 같다.   그럼에도 어쨌든 전반적인 시스템을 이해하고 있었고, 어떻게 구현해야할지에 대해서는 어느정도 경험이 쌓여있었기때문에 큰 문제가 없을 줄 알았다.   실무자로 일을 할 때에도 항상 업무를 받아서 하지는 않았다. 스스로 돌이켜보건대, 나는 주어진 업무가 없으면 스스로 만들어서 제안하고 기획하여 업무를 진행했다.  조그마한 스타트업이었던 첫 회사에서부터  내가 할일은 내가 만들어서 곧 잘했다. 어떤 큰 방향만 정해져있다면 그건 큰 어려움은 아니었다. 나에게 일은 항상 있었다.   매니저가되면서 달라진게있다면 내가 할일만 만드는 것은 아니라는 점이다 . 남이 할 일도 만들어줘야했다.  다행히 팀원들에 대한 면담을 실시한 결과,(팀원을 맡게되자마자 했던 부분)   마이크로 매니징을 원하지는 않았기때문에 큰 그림을 그리는 정도만 준비하면 됐었다.   문제는 내 실무를 동시에 진행하면서 팀원들의 업무 방향도 설정해야했기때문에 시간이 배로 들게 되었다는 점이다. 물론 두배로 일하지는 않았다. 대신에 내 실무시간을 줄였고

XmlHttpRequest to Google Drive 1




요즘 많이 쓰고 있는 기술인 AJAX를 사용하는데 있어, 제이쿼리 라이브러리가 많이 쓰이고 있다. 하지만 어쩔 수 없이 제이쿼리를 쓸수 없는 상황이라면 어떻게 해야 할까?
필자는 크롬 확장프로그램을 개발중인데, 크롬 확장프로그램은 프로그램 안에서 CDN같은식으로
정보를 가져올수가 없는 구조이다.

더군다나 제이쿼리를 이용한 AJAX 역시 내부적으로는  XmlHttpRequest (이하 xhr) 을 이용하기 때문에 xhr을 이해하고 넘어가는것이 비동기 처리에 있어 큰 도움이 될것이라 생각하고 xhr을 직접 써보기로 했었다.

구글 드라이브 관련 REST API를 프로젝트에 도입하면서 제이쿼리가 없는 환경에서 xhr을 구현과정이 어려웠지만, 전반적인 데이터통신에 대한 어떤 통찰을 얻어서 오히려 더 나은 선택이지 않았나 싶다.



Google Drive Rest API를 통해 어떤식으로 작성을 하는지 코드를 보면서 알아보도록 하자.

Google API를 쓰려면 먼저 OAuth 과정을 통해 사용자 토큰을 얻어내야한다.



링크를 통해 전반적인 인증과정을 먼저 알아보길 바란다.

먼저, 사용자 토큰을 얻어내는 함수를 작성했다. 이 함수는 앞으로 모든 비동기 요청과정에서 기본이 된다. 하나하나 차근히 설명하겠다. 전체코드를 쭉 훑어보시길
private xhrWithAuth(methodstringurlstringcallbackparamsrequestBodyrequestHeader) {
        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(methodurl + formatParams(params), true);
            xhr.setRequestHeader('Authorization''Bearer ' + access_token);
            if (requestHeader) {
                Object
                    .keys(requestHeader)
                    .forEach(header => {
                        xhr.setRequestHeader(headerrequestHeader[header]);
                    })

            }
            xhr.onload = function requestComplete() {
                if (xhr.status == 401 && retry) {
                    console.log("리퀘스트 결과에 따른 401 에러입니다.");
                    retry = false;
                    chrome.identity.removeCachedAuthToken({ token: access_token },
                        noInteractivegetToken);
                } else {
                    callback(nullxhr.statusxhr.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(methodurl + formatParams(params), true);
            xhr.setRequestHeader('Authorization''Bearer ' + access_token);
            if (requestHeader) {
                Object
                    .keys(requestHeader)
                    .forEach(header => {
                        xhr.setRequestHeader(headerrequestHeader[header]);
                    })

            }
            xhr.onload = function requestComplete() {
                if (xhr.status == 401 && retry) {
                    console.log("리퀘스트 결과에 따른 401 에러입니다.");
                    retry = false;
                    chrome.identity.removeCachedAuthToken({ token: access_token },
                        noInteractivegetToken);
                } else {
                    callback(nullxhr.statusxhr.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(headerrequestHeader[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(nullxhr.statusxhr.response);
                }
            }
            if (requestBody) {
                xhr.send(requestBody);
            } else {
                xhr.send();

            }

onload 함수는 xhr이 동작을 완료했을때를 기점으로 실행시키는 콜백함수인데, 에러가 나지 않았다면 우리가 매개변수로 받았던 콜백함수를 넘겨주도록 했다.

xhr이 다 정리가 되었다면 requestBody 유무를 판단해, send 함수를 호출한다.


다음 포스트에서 이 xhrWithAuth 함수에 적당한 callback함수를 넣어줌으로써
드라이브 정보를 가져오는 코드를 작성하겠다.


댓글

이 블로그의 인기 게시물

iframe 보안 문제 우회 및 해결법 1

iframe 보안 문제 우회 및 해결법 2

개발팀장이 되면서 겪게된 점들 1