[gs] Google Spreadsheets를 DB로 사용해서 방문자 수 저장하기

jong·2021년 6월 16일
1

취미

목록 보기
12/12

프로그램의 사용자 수를 저장하고 불러올 때 기존에는 그누보드를 이용해서 방문자 수를 저장하고 파싱으로 불러왔었다. 자체 서버를 열든 기존처럼 하든 비용이 나오는 게 문제였다.
이 문제를 구글 드라이브를 통해 해결하는 방법이 있어 사용하기로 했다. 아마 하루에 사용할 수 있는 통신량은 있을 듯한데, 하루에 해봐야 30명 이내라 괜찮을 듯싶다.

Google Drive를 DB로 사용하기

작동 방식 구성하기

일단 스크립트를 작성하기 위해 내가 원하는 구성이 뭔지 고민해 봤다.

  • API를 IP데이터와 함께 요청
  • 전체 방문자 수, 오늘 방문자 수를 반환
  • API를 요청한 유저의 IP와 시간을 로그에 저장

중복 요청 시 카운트를 세지 않는 것도 생각해 봤지만, 하루에 사용할 수 있는 자원이 한정적이고, 일괄 확인하는데 꽤 긴 시간이 필요할 것이라 판단되어 해당 기능은 추가하지 않기로 했다.
IP를 기록하는 기능에 대해서는 자율적으로 사용할 수 있도록 GET으로 API를 요청할 때 클라이언트에서 관련 내용을 지정할 수 있다.

전체 코드

var SHEET_NAME = "Main Sheet"; //메인 시트 이름
var SCRIPT_PROP = PropertiesService.getScriptProperties();
var scriptVer = 1.1; //스크립트 버전

//오류이슈로 GET 만 사용하도록 설정
function doGet(e) {
    return handleResponse(e);
}

function onOpen(e) {
    var doc = SpreadsheetApp.getActiveSpreadsheet();
    doc.getSheetByName(doc.getSheets()[0].getName()).setName("Main Sheet");

    //첫 실행시 새로운 프로젝트라면 설정
    var sheet = doc.getSheetByName(SHEET_NAME);
    if (sheet.getRange("Z1").getValue() == "") {
        sheet.getRange("B2").setValue("Total User");
        sheet.getRange("B3").setValue("Today User");
        sheet.getRange("C2").setValue(0);
        sheet.getRange("C3").setValue(0);
        sheet.getRange("Y1").setValue("Script Version");
        sheet.getRange("Z1").setValue(scriptVer);
    }

    //다른날이면 0으로 수정
    dayReset();
}

function dayReset() {
    var doc = SpreadsheetApp.getActiveSpreadsheet();
    var sheet = doc.getSheetByName(SHEET_NAME);

    var date = new Date();
    var utc = date.getTime() + date.getTimezoneOffset() * 60 * 1000;
    var KR_TIME_DIFF = 9 * 60 * 60 * 1000;
    var krDate = new Date(utc + KR_TIME_DIFF);

    var day = "last load: " + krDate.getFullYear() + "/" + (krDate.getMonth() + 1) + "/" + krDate.getDate();
    if (sheet.getRange("C4").getValue() != day) {
        sheet.getRange("C4").setValue(day);
        sheet.getRange("C3").setValue(0);
    }
}

function handleResponse(e) {
    //LockService를 통해 동시 접근 막기
    var lock = LockService.getDocumentLock();
    lock.waitLock(30000);
    try {
        dayReset();

        var doc = SpreadsheetApp.getActiveSpreadsheet();
        var sheet = doc.getSheetByName(SHEET_NAME);
        var totalRange = sheet.getRange("C2");
        var todayRange = sheet.getRange("C3");

        if (e.parameter["IP"] == "ReloadData") {
            return ContentService.createTextOutput(JSON.stringify({ result: "success", total: totalRange.getValue().toString(), today: todayRange.getValue().toString() })).setMimeType(
                ContentService.MimeType.JSON
            );
        }

        sheet.getRange("C2").setValue(totalRange.getValue() + 1);
        sheet.getRange("C3").setValue(todayRange.getValue() + 1);

        //log
        var doc = SpreadsheetApp.getActiveSpreadsheet();
        var sheetCount = doc.getNumSheets();
        if (sheetCount < 2) {
            //log를 저장할 곳이 없다면
            addSheet();
        } else if (doc.getSheetByName(doc.getSheets()[1].getName()).getLastRow() + 1 > 999) {
            addSheet();
        }
        var logSheet = doc.getSheetByName(doc.getSheets()[1].getName());

        var date = new Date();
        var lastRow = logSheet.getLastRow();
        logSheet.getRange(lastRow + 1, 1).setValue(date.getFullYear() + "/" + (date.getMonth() + 1) + "/" + date.getDate() + "/" + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds());
        logSheet.getRange(lastRow + 1, 2).setValue(e.parameter["IP"]);

        return ContentService.createTextOutput(JSON.stringify({ result: "success", total: totalRange.getValue().toString(), today: todayRange.getValue().toString() })).setMimeType(
            ContentService.MimeType.JSON
        );
    } catch (e) {
        Logger.log("Error: " + e);
        return ContentService.createTextOutput(JSON.stringify({ result: "error", msg: e })).setMimeType(ContentService.MimeType.JSON);
    } finally {
        lock.releaseLock();
    }
}

function addSheet() {
    var doc = SpreadsheetApp.getActiveSpreadsheet();
    doc.insertSheet(1);
    var sheet = doc.getSheetByName(doc.getSheets()[1].getName());
    var date = new Date();
    sheet.setName("log " + date.getFullYear() + "/" + (date.getMonth() + 1) + "/" + date.getDate() + "/" + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds());

    sheet.getRange("A1").setValue("Date");
    sheet.getRange("B1").setValue("IP");

    Logger.log("add log sheet");
}

GitHub Code

사용 방법

DB로 만들 파일 생성

Google Drive에 접속해서 원하는 위치에 Spreadsheet를 새로 생성한다.

스크립트 추가

위 코드를 복사하고 (최신의 코드가 업데이트가 된 경우 위 링크에서 확인) 도구 > 스크립트 편집기로 들어간다.

복사한 코드를 아래와 같이 Code.gs에 붙여 넣고 디스크 모양의 버튼을 눌러 저장한다.

그런 뒤에 구글 스프레드시트를 닫고 다시 열어준 뒤, 기다리면 아래 사진과 같이 기본적인 화면이 보이도록 수정된다.

사진과 같이 전체 접속한 유저의 수와, 오늘 접속한 유저의 수가 표시되면 스크립트가 정상적으로 적용된 것이다.

배포

완성된 코드를 배포함으로써 API를 외부에서 사용할 수 있도록 한다.

배포 > 새 배포에 들어가면 아래 사진과 같은 창이 뜬다.

그림과 같이 '웹 앱'으로 유형을 선택하고, 설명은 각자 원하는 대로 설정하면 된다. 웹 앱을 진행하기 위해선, 인증을 해야 한다. 다른 사용자들도 접속하여 사용할 수 있어야 하기 때문에 액세스 권한은 모든 사용자로 한다.

하단의 배포를 누르면 인증을 요구하는 화면으로 넘어간다.

액세스 승인 버튼을 눌러주어 인증하는 페이지를 열어준다.

필자의 경우에는 항시 경고화면이 등장했다. Advanced를 눌러 접속한다.


Allow를 눌러 권한을 부여한다.
그러고 나면 아래 사진과 같이 배포가 완료된다.

URL을 복사하여 GET 요청을 하면 된다.

profile
공부 기록

0개의 댓글