본문 바로가기

Apps Script로 엑셀 데이터와 Google Groups 자동화하기 (2) OAuth 2.0

Apps Script로 엑셀 데이터와 Google Groups 자동화하기 (2) OAuth 2.0

 

서비스 계정으로 Google API에 액세스 하기 위해서 OAuth 2.0을 구현하여 액세스 토큰을 발급받아야 한다. 아래 링크인 독스에서도 나와있지만 코드를 직접 작성하는 것보다 라이브러리 사용을 추천한다. 

 

https://developers.google.com/identity/protocols/oauth2/web-server?hl=ko#authorization-errors-redirect-uri-mismatch 

 

웹 서버 애플리케이션용 OAuth 2.0 사용  |  Authorization  |  Google for Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. Switch to English 의견 보내기 웹 서버 애플리케이션용 OAuth 2.0 사용 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분

developers.google.com

 

내가 사용한 라이브러리는 apps-script-oauth2로 이슈 관리나 업데이트가 잘 되어 있고, 서비스 계정과 관련된 코드를 지원해서 선택했다. -> https://github.com/googleworkspace/apps-script-oauth2/blob/main/samples/GoogleServiceAccount.gs

 

 

1. OAuth 2.0 라이브러리 import 

설명이 굉장히 자세하고 쉽게 되어있어서 금방 따라할 수 있다. (아래는 레포지토리 README에서 발췌)

 

아래와 같이 라이브러리에 OAuth2가 보이면 성공!

2. OAuth 2.0 코드

 

주의할 점 1

  • 다른 점이 있다면 private_key 등의 경우 stack overflow 질문 글을 참고하여, apps script에서 환경변수를 설정했다.
  • 아래와 같이 storeSecrets 함수를 만들어서 환경 변수 값들을 세팅한 다음, 함수는 지워도 된다. 

 

  • Apps Script의 설정 메뉴에 들어가서 직접 설정해줘도 된다. 어차피 위의 함수로 저장된 값들이 아래의 형태로 저장되기 때문이다.

 

주의해야 할 점은 private_key를 꺼낼 때 뒤에 replace 메서드를 사용해줘야 한다. 

  • private_key는 JSON 파일에서 \n(줄 바꿈 문자)가 포함된 채로 저장된다. (PEM 형식의 문자열)

  • 이때, PEM 형식의 문자열에서 이스케이프된 줄 바꿈 문자를 실제 줄 바꿈 문자로 변환시켜주지 않으면 에러가 난다.
  • 따라서 .replace(/\\n/g, '\n') 를 넣어주어 실제 줄 바꿈 문자로 바꿔준다.
    • /: 정규 표현식 패턴의 시작
    • \\: JavaScript에서 백슬래시는 이스케이프 문자이기 때문에, 실제로 백슬래시 문자를 찾고 싶을 때 두 번 사용
    • n: n 문자 찾기
    • /: 정규 표현식 패턴의 끝
    • g: 문자열 내에서 패턴과 일치하는 모든 부분 찾기

 

주의할 점 2

getService_ 함수 뒤에 붙어있는 언더바는 지우면 안 된다. (컨벤션)

  • 중요한 정보를 담고 있는 만큼 해당 함수가 외부에서 직접 호출되지 않아야 하기 때문이다.

 

 

코드

 

(1) client_email은 서비스 계정의 메일 / user_email은 권한을 위임받은 나의 이메일 값이다.
(2) setScope 부분은 내가 쓸 'directory.group' url을 적었다.

 

oauth.gs
/** (환경설정에 해당되는 부분입니다. getService_ 함수명 뒤에 있는 언더바_ 부분 절대 지우지 마세요!) */
function getService_() {

var USER_EMAIL = PropertiesService.getScriptProperties().getProperty('user_email');
var PRIVATE_KEY = PropertiesService.getScriptProperties().getProperty('private_key').replace(/\\n/g, '\n');
var CLIENT_EMAIL = PropertiesService.getScriptProperties().getProperty('client_email');

  return OAuth2.createService('GoogleDrive:' + USER_EMAIL)
      // Set the endpoint URL.
      .setTokenUrl('https://oauth2.googleapis.com/token')

      // Set the private key and issuer.
      .setPrivateKey(PRIVATE_KEY)
      .setIssuer(CLIENT_EMAIL)

      // Set the name of the user to impersonate. This will only work for
      // Google Apps for Work/EDU accounts whose admin has setup domain-wide
      // delegation:
      // https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority
      .setSubject(USER_EMAIL)

      // Set the property store where authorized tokens should be persisted.
      .setPropertyStore(PropertiesService.getScriptProperties())

      // Set the scope. This must match one of the scopes configured during the
      // setup of domain-wide delegation.
      .setScope('https://www.googleapis.com/auth/admin.directory.group');
}

/** Reset the authorization state, so that it can be re-tested. */
function reset() {
  getService_().reset();
}

 

이렇게 하고 실제 필요한 기능 구현을 위해 API 호출 함수를 작성하면 된다. 끝!

 

getMyGroup.gs 일부 
function getMyGroup() { 
  var service = getService_();
  if (service.hasAccess()) {
 
   // 여기서 필요한 Google API를 호출하여 작업하면 된다. 
  var getOptions = {
  	muteHttpExceptions: true, 
    headers: { Authorization: 'Bearer ' + service.getAccessToken()} // 액세스 토큰 첨부!
    };

  var response = UrlFetchApp.fetch(getUrl, getOptions);
  .
  .
  .
  .
  } else {
    Logger.log(service.getLastError());  // 예외 처리 로직이 너무 떨어져 있다는 단점이 있는 구조
  }
}

 

가독성을 생각하면 아래와 같은 구조가 더 깔끔한 듯하다. (Early exit pattern)

function getMyGroup() { 
  var service = getService_();
  if (!service.hasAccess()) {              // 예외 처리 초반에 하고 깔끔하게 마무리
    Logger.log(service.getLastError()); 
    return;
  }
    

}
728x90
⬆︎