• Home
  • About
    • 인디 개발자 도마 photo

      인디 개발자 도마

      안드로이드 개발자. 프리랜싱 중입니다. 방랑벽 쬐끔 있습니다.

    • Learn More
    • Email
  • Posts
    • All Posts
    • All Tags

스프레드시트를 Db로 사용하기

26 Jul 2021

Reading time ~3 minutes

왜? 굳이? 스프레드 시트를 DB로?

개인 앱을 개발하다 보니, 다국어 지원에 대한 필요성을 느끼기 시작했습니다. 그리고 다른 박성권님의 강의를 통해서 이 다국어 지원을 서버 구축할 필요 없이 스프레드시트로 관리할 수 있다는 것을 알게 되었습니다.

제가 생각하기에 스프레드 시트를 DB로 사용했을 때 가져갈 수 있는 장점은 크게 3가지가 있더군요.

  1. 관리자 페이지를 구축하지 않아도 된다.
    1. 특히 db관련해서는 관리자 페이지를 구축한다 한들 스프레드 시트보다 더 잘만들기는 어렵다.
    2. 특히 스프레드 시트는 대부분의 사람이 이미 UX적으로 친숙하다.
  2. 서버 세팅 시간을 줄일 수 있다.
    1. 이걸 서버를 써야 되나? 싶은 소규모 앱 같은 경우에는 좋은 선택지다.
  3. 스프레드 시트 내에서 구글 번역을 사용할 수 있다.
    1. 텍스트 마다 매 다른 언어로 번역본을 찾을 필요 없이, 스프레드 시트로 드래그하면 만사 해결.

    https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f8d36602-7f62-4950-8e92-2902b4d029be/화면-기록-2021-07-26-오전-10.46.04.gif

일단 당장은 스프레드 시트를 JSON으로 읽는 기능만 필요하므로, 그 부분에 대해서만 구현하겠습니다.

세팅

먼저 보통의 스프레드 시트 파일이 필요합니다. 테스트하고자 하는 혹은 적용하고자 하는 파일을 열어주세요.

사실 조사하다보니 스프레드 시트를 JSON으로 받아오는 방식에는 여러가지가 있는데, 저는 앱스 스크립트를 사용한 방식으로 구현하였습니다. 가격에 대한 부담감이 없다면, Sheet API도 좋은 선택일 듯 합니다.

스프레드 시트 세팅

그 후 도구에서 스크립트 편집기를 클릭해주세요.

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8abe8cd6-dee9-49b1-ac28-bdf451ff6ff3/Untitled.png

앱스 스크립트 세팅

클릭 하면 새 창으로 아래와 같은 에디터로 이동하는데, 초기 myFunction들을 지우고 아래에 있는 코드로 대체해줍니다.

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/bc356148-6119-452a-b557-ee5144b34775/Untitled.png

우리가 앱스 스크립트로 구현할 웹앱에는 doGet(e)와 doPost(e) 함수를 내장하고 있습니다. 아래 코드들은 그 함수들을 구체화하는 작업을 합니다. 자세한 사항은 공식 문서를 참조해주세요.

var SCRIPT_PROP = PropertiesService.getScriptProperties(); // new property service

// If you don't want to expose either GET or POST methods you can comment out the appropriate function
function doGet(e) {
  var lock = LockService.getPublicLock();
  lock.waitLock(30000);
  
  var rawData;
  var columns;
  var limitStart;
  var limitSize;
  var columnArr= [];
  var conditionKey;
  var conditionValue;

  try {
    var doc = SpreadsheetApp.openById(SCRIPT_PROP.getProperty("key"));
    var sheetName = e.parameter["table"];
    var sheetInitial = doc.getSheetByName(sheetName);

    var columns = e.parameter["columns"];
    if(columns){
      columnArr = columns.split("/");
    }

    var condition = e.parameter["condition"];
    if(condition){
      var condtionArr =condition.split("-");
      conditionKey = condtionArr[0];
      conditionValue = condtionArr[1];
    }

    var limitSize = e.parameter["limitSize"];
    if(limitSize===undefined){
      limitSize = 0;
    }

    var limitStart = e.parameter["limitStart"];
    if(limitStart===undefined){
      limitStart = 0;
    }

    var headers = sheetInitial.getRange(1, 1, 1, sheetInitial.getLastColumn()).getValues()[0];
    var rawData = sheetInitial.getRange(2, 1, sheetInitial.getLastRow(), sheetInitial.getLastColumn()).getValues();
    
  } catch (error) {
    return ContentService
      .createTextOutput(JSON.stringify({"ok": false, "error": error["message"]}))
      .setMimeType(ContentService.MimeType.JSON);
  }
  var data = [];
  var headerLength = headers.length;
  var rowLength = rawData.length;
    
  if (rawData) {
    for (var r=0; r < rowLength; r++) {
      if((r >= limitStart && r < parseInt(limitStart) + parseInt(limitSize)) || limitSize == 0 ){
        if (rawData[r][0] === "") {
          continue;
        }
        var isConditionValid = true;
        if(conditionKey&&conditionValue){
          for(var i=0; i< headerLength; i++){
            if(headers[i]==conditionKey){
              if(rawData[r][i] == conditionValue){
                isConditionValid = true;
              }else{
                isConditionValid = false;
              }
            }
          }
        }
        var row = {};
        if(isConditionValid){
          for (var i=0; i< headerLength; i++) {
            if(columnArr.length == 0){
              row[headers[i]] = rawData[r][i];
            }else{
              for(var c =0; c < columnArr.length; c++ ){
                if(columnArr[c] == headers[i]){
                  row[headers[i]] = rawData[r][i];
                }
              }
            }
          }
          data.push(row);
        }
      }
    }
  }
  
  return ContentService
    .createTextOutput(JSON.stringify({"ok": true, "data": data}))
    .setMimeType(ContentService.MimeType.JSON);
}

function doPost(e){
  //    do nothing
}

function setup() {
  var doc = SpreadsheetApp.getActiveSpreadsheet();
  SCRIPT_PROP.setProperty("key", doc.getId());
}

스크립트를 대체한 후, 프로젝트 이름을 저장해주고

메뉴 - 실행 - 함수실행 - setup 을 클릭해주세요.

최초에는 사용자 인증이 필요합니다. 본인 아이디로 권한 인증을 해주세요.

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9b789902-9082-4203-9a42-08300d0a6ca5/Untitled.png

앱스 스크립트 배포

이제 웹앱으로 배포할 시간입니다. 상단 우측에 있는 배포- 새 배포를 클릭해주세요.

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ddedda14-029d-4644-9e4c-c008ea631d5a/Untitled.png

유형 선택에서 웹앱으로 선택을 해주고, 본인이 원하는 대로 설명을 추가한뒤 아래와 같이 세팅해줍니다.

액세스 권한이 있는 사용자를 모든 사용자로 변경해주는 것이 중요합니다.

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8c82216e-2187-45e7-bf0c-b75a5b649dd4/Untitled.png

배포를 클릭하면 웹앱의 주소를 줍니다. https://script.google.com/macros/s/스프레드시트의 아이디/exec 의 형태이니 잘 복사해 둡시다.

마지막으로 스프레드 시트 파일로 돌아가서 스프레드 시트를 링크가 있는 모든 사용자가 접근할 수 있도록 변경해줍니다. 제가 참고한 블로그에서는 이 과정이 생략되어 있어, 구글 로그인을 하지 않은 접근인 경우 액세스 오류가 나더라구요.

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/59ca28fb-6141-4986-ba60-4ff1f39370e9/Untitled.png

웹앱 사용하기

앱스 스크립트를 잘 살펴보면 알겠지만 기본적으로 5가지를 get Parameter로 전달 받습니다.

  • table
    • 데이터를 가져올 sheet의 이름입니다. 해당 파라미터는 기본적으로 전달되야합니다.
  • limitSize
    • 데이터의 사이즈를 정합니다. mysql의 limit과 같습니다.
    • 지정하지 않을 경우 모든 행의 데이터를 가져옵니다.
  • limitStart
    • 데이터의 시작지점을 정해줍니다.
    • 지정하지 않을 경우 0부터 가져옵니다.
  • columns
    • ’/’ 를 구분자로 가져오려는 열을 지정하여 가져옵니다.
    • 전달하지 않을 경우 모든 열을 가져옵니다.
  • condition
    • ’-‘를 구분자로 key-value 형식으로 전달합니다.

예시

https://script.google.com/macros/s/스프레드시트의아이디/exec?table=ui&condition=id-text_title&limitSize=3&limitStart=1&columns=id/en/ko/ja

참고



Share Tweet +1