구글 Apps Script 와 Firebase 연동하기
개요
예전에 구글 앱스 스크립트를 이용해 스프레드 시트 내용을 json으로 만들어 구글 드라이브에 올리고, 이 json을 구글 드라이브 호스팅을 이용해 호스팅하는 방법을 발표했다. 하지만 이제 구글 드라이브 호스팅이 중단되었기 때문에 이 방식은 더 이상 동작하지 않아, 세계최고의 안드로이드 앱인 PS4 News에도 문제가 생겼다.
그래서 요즘 핫한 firebase를 이용해 비슷한 동작을 하도록 수정했다. 이 글은 구글 앱스 스크립트를 이용해 firebase realtime db의 내용을 갱신하고, 이를 내려받는 방법을 소개한다.
최종 결과물은 아래와 같다. 최종 결과물이 너무 허접해도 놀라지 마시길.
왕오리님 여행기 사이트
스프레드 시트 생성
우선 제공할 내용을 담은 시트가 필요하다. 물론 이미 있다면 그대로 사용하면 되고. 이 시트에 앱스 스크립트를 추가해야 하기 때문에, 메뉴 중 Tools > Script editor를 선택해서 스크립트를 작성하자.
우선 내가 생성한 시트 내용은 다음과 같다.
이제 Tools > Script editor 에서 스크립트를 작성하자. 예제에선 시트 내용을 json으로 생성한다. 앱스 스크립트 관련 내용은 Google Apps Script를 참고한다. 아래와 같이 테스트 펑션을 만들어 두면, 스크립트 에디터에서 일부분을 테스트해보기 매우 편하다.
function genData() { var ss = SpreadsheetApp.getActive(); var curSheet = ss.getActiveSheet(); var data = curSheet.getRange(2, 1, curSheet.getLastRow()-1,3 ).getValues(); var result = new Object(); var tours = []; result.tours = tours; var tour; for( var i =0;i<data.length;i++) { if( data[i][0] != '' ) { tour = new Object(); tour.title = data[i][0]; tour.articles = []; tours.push(tour); } var article = new Object(); article.title = data[i][1]; article.url = data[i][2]; tour.articles.push(article); } return result; } function test_genData() { Logger.log(genData()); }
이렇게 만들어서 생성한 json 결과물은 다음과 같다. 콘솔에서 test_genData() 를 실행하고, 맥 기준 cmd+enter를 눌러 콘솔을 열면 다음 내용이 출력된다.
{tours=[{title=hongkong, articles=[{title=홍콩 여행1, url=https://brunch.co.kr/@kingori/6}, {title=홍콩 여행2, url=https://brunch.co.kr/@kingori/7}]}, {title=north_europe, articles=[{title=북유럽 여행1, url=https://brunch.co.kr/@kingori/10}, {title=북유럽 여행2, url=https://brunch.co.kr/@kingori/11}]}]}
Firebase 프로젝트 설정
DB 생성하고 테스트하기
firebase콘솔 > 새 프로젝트 만들기 메뉴를 이용해 프로젝트를 만든다. firebase엔 여러 기능이 있는데, 일단은 디비만 필요하니 요쪽만 들여다본다.
메뉴 중 Database 선택하고, 우선은 read / write 가 제대로 되는지부터 살펴봐야 하기 때문에 규칙 탭으로 이동해서, anonymous read / write 가 되도록 설정한다. 더 자세한 내용은 reference 문서 를 참고한다.
{ "rules": { ".read": true, ".write": true } }
이제 write 잘 되는 지 실행해보자. 명령창에서 대충 이렇게 입력하고 실행하면 콘솔의 디비에 내용이 들어온 걸 확인할 수 있다.
curl -X PUT -d '{ "first": "Jack", "last": "Sparrow" }' 'https://kingoritours.firebaseio.com/tours.json'
디비 내용이 수정된 걸 확인했다면 얼른 anonymous write 권한은 뺏어두자.
{ "rules": { ".read": true, ".write": false } }
권한 설정하기
아무나 write하진 못하게 막아두었으니, 이제 관리자만 내용을 update할 수 있도록 작업해야 한다.
fireabse rest api는 header나 parameter를 이용해 access token을 직접 넘겨서 인증을 할 수 있다. 이번 예제에선 어차피 관리자 혼자만 업데이트하면 되니까 이 내용만 살펴보자. firebase 콘솔의 auth 메뉴엔 여러 권한 관리 방법을 제공하는데, 관리자만 내용 update하는 경우엔 이쪽을 들여다 볼 필요가 없더라. 관리자의 access token만 잘 만들면 된다.
firebase 콘솔 좌상단의 톱니바퀴 > 권한을 누르면 "IAM 및 관리자" 페이지로 이동한다. 여기서 좌측 메뉴 중 "서비스 계정"을 선택하고,서비스 계정 만들기를 선택한다. 만들면 json 파일을 하나 다운로드 하는데, 이 파일은 잃어버리지 않도록 잘 관리해야 한다.
방금 만든 서비스 계정을 이용해 access token을 만들어야 하는데, 이 내용은 레퍼런스 문서에 잘 나와있다. 다만 java 예제코드만 나와있으니, 다른 언어 사용자라면 구글 oauth 관련 문서를 참고하자.
자바 기준으로 위 예제의 코드를 실행하려면 google auth client library가 필요하고, 이 라이브러리는 링크에서 받을 수 있다.
이제 access token도 구했으니, 앱스 스크립트로 이동해서 firebase 연동을 해 보자. 몇 번의 권한 팝업이 뜨고 실행에 성공할 것이다.
function exportToFirebase() { var options = { "method" : "put", "payload" : JSON.stringify(genData()) }; UrlFetchApp.fetch('https://kingoritours.firebaseio.com/tours.json?access_token=12345' , options); }
이렇게 만든 json은 브라우저에서도 쉽게 접근해서 확인할 수 있다.
하지만 이렇게 만든 access token 의 유효기간은 그리 길지 않기 때문에 주기적으로 실행되는 스크립트라면 어느새 access token이 만료되어 문제가 생긴다. 따라서 access token을 계속 발급받아야 한다.
access token을 발급받기 위해, 이 stackoverflow 링크에 언급된 앱스 스크립트 라이브러리를 사용했다. 스크립트 에디터의 Resources > Libraries 를 누르고, 라이브러리 이름 입력 란에 MJ5317VIFJyKpi9HCkXOfS0MLm9v2IJHf 를 입력한 다음, 다시 스크립트 에디터 메뉴 중 File > Project properties > Script properties 에 jsonKey 라는 키를 하나 만들고, 값은 아까 내려받은 json 파일의 내용을 모조리 붙여넣는다. 다음엔 라이브러리 readme 의 내용을 firebase에 맞게 다음과 같이 살짝 고치면 access token을 매번 생성할 수 있다
function tokenService(userEmail){ var userEmail = userEmail || "" var jsonKey = JSON.parse(PropertiesService.getScriptProperties().getProperty("jsonKey")); var privateKey = jsonKey.private_key; var serviceAccountEmail =jsonKey.client_email; if(!userEmail){userEmail = serviceAccountEmail}; var sa = GSApp.init(privateKey, ['https://www.googleapis.com/auth/firebase.database','https://www.googleapis.com/auth/userinfo.email'], serviceAccountEmail).addUser(userEmail); var tokenObj = JSON.parse(PropertiesService.getScriptProperties().getProperty(userEmail)) || {}; return function(){ var nowTime = parseInt((Date.now()/1000).toString().substr(0,10)); if(!("token" in tokenObj) || tokenObj.expire < nowTime){ var newToken = sa.requestToken().getToken(userEmail); PropertiesService.getScriptProperties().setProperty(userEmail, JSON.stringify(newToken)); tokenObj.token = newToken.token; tokenObj.expire = newToken.expire; } return tokenObj.token; } }
웹페이지 호스팅
이제 json까지는 쉽게 만들었는데, 만약 html 페이지로 보여주고 싶다면 firebase의 호스팅을 이용하면 된다. 호스팅은 로컬의 특정 디렉터리를 cli 도구를 이용해 firebase 쪽으로 올려주는 기능을 한다.
위에서 만든 json을 실제 보여주는 것 까지 해보려면 html 페이지가 필요해서 간단히 만들어보았다.
<html> <head> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script> <script type="text/javascript"> function fetchData() { $.getJSON( "https://kingoritours.firebaseio.com/tours.json" , function(data) { var table = $("#content") var html = ''; $.each( data.tours, function( k,v) { html = html+ '<tr><td rowspan="'+v.articles.length+'">'+v.title+'</td>'; $.each( v.articles, function(k,v) { if( k != 0 ) { html = html + '<tr>'; } html = html + '<td><a href="'+v.url+'">'+v.title+'</a></td></tr>'; }); }); $('#content tr:last').after(html); }); } </script> </head> <body> <table id="content"> <thead> <tr> <td>Tour name</td> <td>Article</td> </tr> </thead> <tbody> </tbody> </table> </body> <script type="text/javascript"> fetchData(); </script> </html>
cli 도구가 워낙 잘 만들어져있어서 하라는데로 하면 끝!


















