구글 캘린더 API 사용하기 (for Node.js)

Explanation

구글 캘린더 API를 활용하여 구글 사용자 인증과 캘린더의 이벤트를 받아오고 캘린더를 추가 및 삭제하는 방법입니다. 내용을 구글 API 사이트의 'Quickstart'를 기반으로 하였습니다.
참고 : 구글 Quickstart (node.js)

1. 시작하기전에

node.js 와 npm이 설치 되어있다면

npm install googleapis --save
npm install google-auth-library --save

위와 같이 클라이언트 라이브러리를 설치합니다.
그리고 구글에 로그인한 후 https://console.developers.google.com/flows/enableapi?apiid=calendar로 이동하여 API 프로젝트를 생성하고 API키를 발급 받습니다.

2. 시작

프로젝트를 생성하여 API를 발급 받은 후 '사용자 인증 정보' 페이지의 'OAuth 2.0 클라이언트 ID' 가장 오른쪽에 있는 다운로드 아이콘을 클릭하여 'client_secret_xxxx.json' 다운로드 합니다. 그리고 파일 이름을 'client_secret.json'으로 변경하여 node의 디렉토리에 넣습니다. 그리고 아래와 같은  'quickstart.js' 파일을 만들어보겠습니다.

var fs = require('fs');
var readline = require('readline');
var google = require('googleapis');
var googleAuth = require('google-auth-library');

var SCOPES = [
              'https://www.googleapis.com/auth/calendar.readonly', // 캘린더 정보의 읽기 권한? 입니다.
              'https://www.googleapis.com/auth/calendar' // 캘린더 정보의 편집 권한? 입니다.
              ];

var TOKEN_DIR = (process.env.HOME || process.env.HOMEPATH ||
    process.env.USERPROFILE) + '/.credentials/';
// 토큰이 저장될 디렉토리
var TOKEN_PATH = TOKEN_DIR + 'calendar-nodejs-quickstart.json';
// 토큰 파일 명

fs.readFile('client_secret.json', function processClientSecrets(err, content) {
  // 로컬의 client_secret.json 파일을 읽고 'authorize' 함수 실행
  if (err) {
    console.log('Error loading client secret file: ' + err);
    return;
  }
  authorize(JSON.parse(content), listEvents);
});

function authorize(credentials, callback) {
  var clientSecret = credentials.installed.client_secret;
  var clientId = credentials.installed.client_id;
  var redirectUrl = credentials.installed.redirect_uris[0];
  // client_secret.json 파일에서 시크릿키, 클라이언트 아이디, 리다이렉트 URI를 받습니다.
  // 웹 어플리케이션으로 만들었을 때에는 'installed' -> 'web' 으로 수정합니다.
  // ex 'var clientSecret = credentials.web.client_secret;'

  var auth = new googleAuth();
  var oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);

  fs.readFile(TOKEN_PATH, function(err, token) {
    // 앞서 지정한 디렉토리와 파일명으로 토큰 파일을 읽습니다.
    if (err) {
      getNewToken(oauth2Client, callback); // 토큰이 없다면 만듭니다.
    } else {
      oauth2Client.credentials = JSON.parse(token); // 토큰이 있다면 정보를 객체에 담습니다.
      callback(oauth2Client);
    }
  });
}

function getNewToken(oauth2Client, callback) {
  var authUrl = oauth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: SCOPES
  });

  // 브라우저에 'authUrl'의 링크로 접근하여 데이터 접근에 동의한 후 리다이렉트 되는 URL의
  // 'code=' 다음 부분을 복사합니다.
  console.log('Authorize this app by visiting this url: ', authUrl);
  var rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  });
  // 복사한 'code=' 다음 부분을 입력합니다.
  // 로컬에서 테스트 할때에는 code= 다음 부분의 값이 URL 인코딩이 되어 있을 수 있으니
  // URL 디코딩한 후에 입력합니다.
  rl.question('Enter the code from that page here: ', function(code) {
    rl.close();
    oauth2Client.getToken(code, function(err, token) {
      if (err) {
        console.log('Error while trying to retrieve access token', err);
        return;
      }
      oauth2Client.credentials = token;
      storeToken(token);
      callback(oauth2Client);
    });
  });
}

function storeToken(token) {
  try {
    fs.mkdirSync(TOKEN_DIR);
  } catch (err) {
    if (err.code != 'EEXIST') {
      throw err;
    }
  }
  fs.writeFile(TOKEN_PATH, JSON.stringify(token)); // 토큰을 만듭니다.
  console.log('Token stored to ' + TOKEN_PATH);
}

function listEvents(auth) {
  // 토큰 인증이 완료되면 최종적으로 실행되는 함수 입니다.
  // 아래의 예제는 구글 캘린더에 등록된 이벤트 10개의 정보를 출력합니다.
  var calendar = google.calendar('v3');

  calendar.events.list({
    auth: auth,
    calendarId: 'primary', // 이곳에 이벤트를 가져올 캘린더 id를 입력해야 합니다.
    timeMin: (new Date()).toISOString(),
    maxResults: 10,
    singleEvents: true,
    orderBy: 'startTime'
  }, function(err, response) {
    if (err) {
      console.log('The API returned an error: ' + err);
      return;
    }
    var events = response.items;
    if (events.length == 0) {
      console.log('No upcoming events found.');
    } else {
      console.log('Upcoming 10 events:');
      for (var i = 0; i < events.length; i++) {
        var event = events[i];
        var start = event.start.dateTime || event.start.date;
        console.log('%s - %s', start, event.summary);
      }
    }
  });


  // 다음은 구글 캘린더에 'CHEOLGUSO' 라는 이름의 캘린더가 있는지 확인하여
  // 없다면 만들고 있다면 삭제해보겠습니다.
  calendar.calendarList.list({
    auth : auth
  }, function(err, calendarList){
    if(err) console.log(err);

    var deleteCalendarId; // 캘린더 삭제를 위한 아이디 변수
    var isCGSCalendar = false; // 'CHEOLGUSO' 캘린더가 존재하는지 확인하기 위한 변수

    // 'CHEOLGUSO' 캘린더가 있는지 확인
    for(var i=0; i<calendarList.items.length; i++){
      if(calendarList.items[i].summary === 'CHEOLGUSO'){
        isCGSCalendar = true;
        deleteCalendarId = calendarList.items[i].id;
      }
    }

    if(isCGSCalendar){

      // 'CHEOLGUSO' 캘린더가 있다면 캘린더를 지움
      calendar.calendars.delete({
        auth : auth,
        calendarId : deleteCalendarId
      }, function(err, calendars){
        if(err) console.log(err);
      });

    }else{

      // 'CHEOLGUSO' 캘린더가 없다면 캘린더를 만듬
      calendar.calendars.insert({
        auth : auth,
        resource : {
          summary : 'CHEOLGUSO'
        }
      }, function(err, calendars){
        if(err) console.log(err);
      });

    }

  });


}

실행은

node quickstart.js

3. 참고

구글 Quickstart (node.js)
https://github.com/google/google-api-nodejs-client/#authorizing-and-authenticating
https://github.com/wanasit/google-calendar

이전 댓글 4개
  1. cheolguso

    안녕하세요? 댓글 확인이 늦었네요. 파일이나 디렉토리를 찾을 수 없다는 것으로 보아, 예제에서 디렉토리 파일을 읽어오는 부분. ' var TOKEN_DIR= ' 토큰을 저장할 디렉토리를 선언하는 부분이나 ' client_secret.json ' 파일의 위치에서 문제가 생긴게 아닐까 싶습니다. 'client_secret.json' 파일을 올바른 디렉토리에 넣었는지, 또는 파일 디렉토리 경로를 수정하시거나 생성하신 서버의 환경에 맞게 토큰을 저장할 디렉토리('TOKEN_DIR')를 새로 지정하시면 되지 않을까 싶습니다.

  2. 잘봤습니다!!

    글 정말 잘봤습니다 근데... 한대로 그대로 따라했는데 Error: ENOENT: no such file or directory, mkdir 이런오류가 계속 나네요.. 혹시 왜그런지 아세요 ?ㅠㅠ

  3. FALSY LAB

    안녕하세요, sofkaints님. 실제로 서비스에서 사용하시려면 몇가지 수정이 필요할 것 같습니다. sofkaints님이 말씀하신 것처럼 구글 사용자 인증을 위해서 (문서상 변수)'authUrl'로 클라이언트에서 새로 페이지를 띄우거나 리다이렉션 시켜서 사용자가 인증절차를 걸칠 수 있게 해야 합니다. 그리고 인증이 완료되면 구글 API 프로젝트에서 사용자 인증에서 '승인된 리디렉션 URI'부분에 등록된 URI로 리다이렉션이 되는데 이때 스트링 파라미터로 ?code=... 라는 값이 함께 옵니다. '승인된 리디렉션 URI'에 등록된 URI의 로직에서는 스트링 파라미터로 code가 있는지 확인하고, 있다면 code를 이용해서 'getToken' 하여 토큰을 만들어서 'storeToken' 토큰을 등록하고 다음 이벤트 로직을 실행하면 됩니다. 짧게 글로만 적으려니, 더 복잡하게 느껴지는거 같네요. 조금이나마 도움이 되었을지 모르겠습니다... 댓글 감사합니다 :)

  4. sofkaints

    웹앱에서 본인인증을해야 캘린더 정보를 볼수 있는데 퀵스타터는 실행하면 주는 url접속후 인증하고 뱉어내는 code를 입력해야 정보가 나옵니다. 다른 유저가 이 기능을 쓰려면 어떻게 해야하죠? url을 클라한테 보내줘서 인증하게 해야하나요? 간단히 말해 응용방법을 모르겠습니다 ㅠ