Google Spreadsheet 로 쉽고 빠른 Backoffice 구축

Google IO Extended Seoul 에 다녀왔다. 나는 C 홀에 있었는데 라이트닝 토크 세션이 있고 그 자리에서 발표신청을 해도 된다길래 즉석해서 “스타트업 개발자의 수고를 줄여주는 GoogleSpreadsheet” 라는 주제로 자료를 만들어 발표했다.

5분은 짧은 시간이었지만 개괄적으로 이야기하기에는 충분한 시간이었다. 세션 후에 적극적으로 질문 해주시는 분들을 보고 나에게만 고민스러웠던 주제가 아니구나 하는 생각을 하게 됐다. 발표자료를 공유하고 더 자세한 내용을 블로그 포스트로 적어봤다.

덧) 오프라인 세미나를 준비하고 있다. 구글 설문지 를 통해 의향을 알려주셨으면 한다.

앱이나 웹을 개발할 때 서비스를 구성하는 컨텐츠를 제작해야 한다. 컨텐츠는 맛집서비스라면 맛집데이터 일 수 있고, “이메일 형식으로 입력해 주세요” 같은 문구일 수도 있다. 프로젝트에서 컨텐츠 제작에는 대부분 기획자나 큐레이터 등 별도의 롤을 둔다. 그리고 개발자는 그 컨텐츠를 소프트웨어에 맞게 옮긴다. 그런데 이렇게 옮기는 작업에도 시간이 든다. 한글로 된 컨텐츠라면 맞춤법이 틀린 부분을 발견하고 피드백하는데도 시간이 든다.

그래서 스타트업 개발자는 언제나 시간이 부족하다. 그렇다고 컨텐츠 담당자에게 “데이터를 JSON으로 만들어주세요.” 라고 요청할 수는 없는 것 아닌가. 그래서 지금도 수많은 개발자들이 텍스트데이터를 복사와 붙여넣기 기능으로 JSON 으로, 하드코딩으로 소프트웨어를 만들고 있다.

누구나 이 쯤에서 백오피스로 만들어 처리하면 좋겠다고 생각할 것이다. 하지만 스타트업 개발자에게 그럴만한 여유가 없다. 한시가 바쁜데 서비스와 서비스의 컨텐츠를 구성하는 도구까지 만들어야하는 것이다. 그리고 백오피스를 구축하려면 사내에서만 접속하도록 보안관련 사항을 구축하고 사내에 활용교육도 해야한다. 그리고 구축 후 뭔가 변경되면 그 때마다 교육이 필요하다. 쉽지 않은 일이다.

따라서 개발자는 백오피스를 만드는 데에 보수적이어야 한다. 도구를 갖는 것에 반대해야 한다는 것이 아니다. 빠르게 변하는 스타트업의 비즈니스를 담을 수 있는 은탄환같은 백오피스를 만들기에는 시간이 많이 들기 때문에 쉽게 접근해서는 안된다는 것이다.

그렇다고 도구를 갖지 않을 수는 없으니 백오피스는 스스로 구축하기 보단 있는 것을 조합해 사용하자. 그래서 나는 구글 스프레드시트로 백오피스를 대신하는 것을 추천한다.

왜 구글 스프레드시트인가?

구글스프레드시트는 모두의 도구다. 특히 IT 에 밝지 않은 사람이 새로운 도구를 익히는데 방어적인 경우가 많은데 그렇다하더라도 엑셀을 다루지 못하는 사람은 드물다. 여기에는 컨텐츠 담당자들에게 손에 익숙한 도구를 쥐어주고 협업을 강화하는데 의미가 있다. 컨텐츠 작성을 컨텐츠 담당자에게 온전히 돌려주고, 개발자가 소프트웨어로 옮기는 작업을 최소화할 수 있다.

구글 스프레드 시트는 강력하다. 기본적으로 엑셀과 비슷한 함수셋을 가지고 있기 때문에 컨텐츠 개발자에게는 반복되는 일을 줄여줄 것이다. 또 필요한 함수가 있다면 개발자가 직접 자바스크립트로 직접 작성해 기능을 추가할 수도 있다. 또 공개된 API 를 사용해 스프레드시트의 내용을 JSON/XML 포맷으로 쉽게 변환해 다운로드 받을 수 있다.

다시말해 구글 스프레드시트는 비 개발자들에게는 손쉬운 도구이면서 개발자에게는 강력한 도구가 된다.

구글 스프레드시트로 백오피스를 대체하는 방법

먼저 백오피스에서 입력할만한 데이터의 특성은 읽기에는 열려있고 쓰기에는 닫겨있다. 즉, 입력을 하는 사람은 한정적이다. 하지만 데이터 자체는 어떤 방법으로든 모든 사람에게 공개될 것이다. 예를들면 웹사이트의 “언론보도” 에 들어가는 데이터 정도를 들 수 있다.

데이터 입력은 구글 설문지에서

데이터 입력은 어느정도 정규화되어야 한다. 따라서 구글 설문지를 활용하자. 입력값 검증(validation) 도 어느정도 가능하다. 데이터를 입력하는 사람이 한정적이라는 것은 상대적으로 검증에 여유롭다는 말도 되므로 적은 검증으로도 활용할 수 있다.

데이터 수정/삭제는 스프레드시트에서 직접수행

설문양식으로 입력받은 데이터는 스프레드시트에 곧바로 들어간다. 따라서 이 시트에서 직접 수정하고 삭제할 수 있다.

개발자의 스프레드시트 활용

필요한 함수가 있다면 직접 작성

Google Apps Script 를 통해 스프레드시트에 들어갈 함수를 직접작성할 수 있다.

예를들어 아래 코드는 내가 만든 전각-반각 변환 자바스크립트 함수다. 회사가 중국어를 다루다보니 필요해서 만들게 되었다.

아스키 문자를 입력하더라도 중국어나 일본어 키보드로 입력하면 I'm full width 처럼 입력된다. 이 때 이 글자들이 바로 전각문자다. 자바스크립트를 사용해 구글 스프레드시트의 함수를 작성하고, 스프레드시트 내에서 이 함수를 호출해 반각문자로 손쉽게 변경할 수 있다.

참고로 전각문자들은 아스키코드와 동일한 순서로 코드가 배열되어 있어 그대로 변환만 하면된다. 다만 영어/한국어로 타이핑 할 때 마침표(.)를 타이핑하면 중국어/일본어에서는 。 로 표시가 되는데, 이것은 아스키코드 순서로 변환할 수 없어 이에 대해서만 별도의 처리를 해주었다. 전각 코드표는 여기에서 볼 수 있다. https://en.wikipedia.org/wiki/Halfwidth_and_fullwidth_forms

function ch_fw2hw(string) {
  var re = /([\uFF01-\uFF5E])/g;
  var unicodeStarting = "\uFF01".charCodeAt();
  var asciiStarting = "!".charCodeAt();
  string = string.replace(re, function (match, p1, offset, string) {
      var asciiCharCode = asciiStarting + (p1.charCodeAt() - unicodeStarting);
      return String.fromCharCode(asciiCharCode);
  }); 
  string = string.replace(/。/g, '.');
  return string;
}   

이렇게 작성한 코드는 스프레드 시트에서 곧바로 사용할 수 있다.

Screen Shot 2015-07-13 at 2.37.55 AM

외부서비스로부터 데이터가져오기, 데이터 내보내기 가능

그리고 URL 로부터 데이터를 받아온다던지 네트워크를 지원하므로 외부 서비스와의 연동도 가능하다. 예를들면 Facebook API 와 연동해 좋아요 횟수를 카운트 할 수도 있다.

Screen Shot 2015-07-13 at 2.38.58 AM

여러가지 방법으로 스프레드시트 API 에 접근해 내용을 JSON 으로 받아올 수 있다. API 에 접근하는 라이브러리를 사용해 직접 만들 수도 있고, 외부에서 Apps Script 를 실행해 읽어올 수도 있다.

아래 그림은 내부 프로젝트로 진행했던 translator 의 실행화면이다. 시트의 내용을 그대로 JSON 으로 받아오고 있다.

Screen Shot 2015-07-13 at 2.43.13 AM

보안문제는 어떤가?

발표 후에 가장 많이 들은 질문이다. 보안이라면 시트자체의 보안과 스크립트 실행권한의 2가지 측면에서 살펴볼 수 있을 것이다.

기본적으로 시트의 공유설정을 통해 받은 사람 외에는 다른 사람은 쓰거나 볼 수 없게 설정이 가능하다. 그리고 그림과 같이 Apps Script 실행권한을 제한할 수 있다. Apps Script 실행권한은 App 이 시트에 접근할 때 어떤 사용자를 대신해 접근하는지와 어떤 권한으로 Apps Script 을 실행할지로 나눠 생각할 수 있다.

Screen Shot 2015-07-13 at 1.53.00 AM

다만 Apps Script 에서 접근을 제한하는 경우에는 로그인이 필요하기 때문에 커맨드라인 인터페이스를 통해 Google OAuth 로그인하는 프로그램을 짜야하고 이것은 난이도가 다소 높다.

다시 말하지만 스타트업의 쉴새없이 변하는 비즈니스를 담을 수 있는 백오피스는 없다. 대신 도구를 조합해 최고의 퍼포먼스를 발휘하는데에 집중하자. 사실 나는 이 전에 그런 백오피스를 만들고자 했고, 관리에 실패한 후 이것을 깨달았음을 고백한다.

글을 쓰고보니 코드 한 줄 없이 개발자에게 공부할거리만 지운것은 아닌가 싶다. Apps Script 사용법을 알려드리고 직접 작성해보는 오프라인 세미나를 갖는 것이 좋겠다는 생각이 들었는데 혹시 참석 의향이 있다면 아래 구글 설문지로 의견을 주시기 바란다. 대략 2~3시간 정도면 Apps Script 의 기본 사용법을 라이브코딩으로 배울 수 있을 것이라 생각한다.

중첩된.오브젝트.프로퍼티.가져오기.성공적 = getobject 

TypeError: Cannot read property ‘..’ of undefined 는 매우 흔히 발생하고 오랫동안 개발자를 괴롭혀온 에러다.

이 에러는 오브젝트의 프로퍼티에 접근하려 하는데 그 오브젝트가 undefined 인 경우 발생한다. 즉, 아래 같은 상황이다.

> var opt = {};
undefined
> opt.a.b.c.d
TypeError: Cannot read property 'b' of undefined
...
> 

특히 API 연동처럼 외부에서 생성된 오브젝트를 가져와 사용할 때 프로퍼티가 누락되면 자주 만나게 된다. 특히 스키마가 없는 NoSQL 데이터베이스를 사용하는 경우에 자주 만나는 오류이기도 하다.

그래서 여러가지 방법으로 돌파를 시도하는데, 변하지 않는건 단계가 깊어질 수록 조건문이 길어진다는 것이다. 커피스크립트 등에서는 ?. 이라는 존재연산자(existential operator)를 지원하고, JavaScript 완벽가이드에도 비슷한 코드가 수록되어 있다.

이렇게 중첩된 프로퍼티들을 조건문을 사용하지 않고 가져올 수 있다면 꽤 편리할텐데 이런 기능을 가진 패키지를 찾았다. 바로 Ben Alman 이 만든 getobject 이다. Ben Alman 은 Gruntjs 의 최초작성자이기도 하다. 이 패키지는 Gruntjs 에도 포함되어 있다. (사실은 이 패키지를 Gruntjs 코드를 살펴보다 알게되었다.)

패키지는 NodeJS 에서 사용할 수 있는 node-getobject 와 jquery 및 클라이언트쪽 자바스크립트에서 사용할 수 있는 jquery-getobject 패키지 두가지 이다. jquery-getobject 라는 프로젝트 명을 가지고 있긴 하지만 jquery 를 사용하지 않는 경우도 이 패키지를 사용할 수 있다.

node-getobject

먼저 node 쪽의 node-getobject 를 살펴보자. 프로젝트 홈은 http://github.com/cowboy/node-getobject 이다.

설치

npm install getobject

사용법

getobject.get(object, parts, create);

중첩된 프로퍼티에서 값을 편리하게 가져온다. 값이 없는 경우에는 undefined 를 리턴한다.

  • obj 는 대상이 되는 오브젝트이다.
  • parts 는 오브젝트 내에 접근하는 중첩된 키들을 문자열로 적는다. 예를들어 opts.a.b.c 를 가져오고 싶다면 ‘a.b.c’ 로 적는 식이다.
  • create 은 parts 에 접근하는과정에 undefined 를 만나는 경우 이들을 생성할지 설정하는 옵션이다.

 getobject.set(obj, parts, value);

중첩된 프로퍼티에 값을 적는다.

  • obj 는 대상이 되는 오브젝트이다.
  • parts 는 오브젝트 내에 접근하는 중첩된 키들을 문자열로 적는다. .get 에서의 parts 와 같다.
  • value 는 적을 값이다.

getobject.exists(obj, parts);

중첩된 프로퍼티에 값이 존재하는지 검사한다.

예제

var getobject = require(‘getobject’);
var opt = {};
getobject.get(opt, ‘a.b.c.d.e’, true);
typeof getoject.get(opt, ‘a.b.c.d’);  // object
getobject.set(opt, ‘a.b.c.d.e’, ‘hello');
getobject.get(opt, ‘a.b.c.d.e’); // hello

jquery-getobject

프로젝트 홈은 http://github.com/cowboy/jquery-getobject 이다.

클라이언트측 자바스크립트에서의 사용법은 Node 에서의 사용과는 조금 다르다.

jQuery 를 사용중이라면 jQuery 내의 메서드처럼 동작하고 없는 경우에는 window.Cowboy 라는 객체 아래에 메서드들이 위치한다.

즉, window.jQuery.getObject(),window.jQuery.setObject() (물론, window.jQuery 는 $ 로 줄여쓰는 경우가 많다.) 이거나 window.Cowboy.getObject(), window.Cowboy.setObject() 가 된다.

사용법

$.getObject(parts, create, obj);

중첩된 프로퍼티에서 값을 편리하게 가져온다. 값이 없는 경우에는 undefined 를 리턴한다.

  • parts 는 오브젝트 내에 접근하는 중첩된 키들을 문자열로 적는다. 예를들어 opts.a.b.c 를 가져오고 싶다면 ‘a.b.c’ 로 적는 식이다.
  • create 은 parts 에 접근하는과정에 undefined 를 만나는 경우 이들을 생성할지 설정하는 옵션이다.
  • obj 는 대상이 되는 오브젝트이다.

$.setObject(name, value, context);

중첩된 프로퍼티에 값을 적는 메서드이다.

  • parts 는 오브젝트 내에 접근하는 중첩된 키들을 문자열로 적는다. .getObject 에서의 parts 와 같다.
  • value 는 적을 값이다.
  • context 는 대상이 되는 오브젝트이다. context 는 생략가능하고, 생략한 경우 window 객체에서 찾아내려간다.

$.exists(parts, obj)

중첩된 프로퍼티에 값이 존재하는지 검사한다.

예제

jQuery 를 사용 중인경우

window.$ === window.jQuery; //true
var opt = {};
$.getObject('a.b.c.d.e', true, opt); //undefined 
$.setObject('a.b.c.d.e', 'hello', opt);

jQuery 를 사용하지 않는경우

window.Cowboy.getObject('a.b.c.d.e', true, opt); //undefined        
window.Cowboy.setObject('a.b.c.d.e', 'hello', opt);

Google Chrome 에서 Grunt 사용하기

KStyleTrip 에서는 프론트엔드 영역에서 디자이너와 개발자가 협업하고 있다.

나는 사실 HTML CSS 를 어느 수준으로 다루기는 하지만 퍼블리셔는 아니어서 픽셀 단위의 디테일을 구현하는데는 어려움이 있다. 디자이너는 디자이너 대로 원하는 만큼의 디테일을 구현하기 어려워하는 내 모습을 보기 안타까워 했다. 그래서 현실적인 수준에서 협업을 시작했다.

개발자가 디자인을 보고 큰 덩이에 대해 마크업과 CSS 처리를 하면 디자이너가 원하는 수준의 디테일을 구현하는 방식이다.

그런데 역시 개발자와 비 개발자간의 협업은 환경구성에 어려움이 있기 마련이다. 예를들어 서버를 띄우거나 하는 일들 말이다. 물론 이것도 쉘 스크립트 등을 사용해 할 수 있는 한 단순하게 만들어 관리하고 있었지만 더 쉬운 방법을 찾게 되었다.

바로 크롬 개발자도구에서 Grunt 를 사용하는 것이다. 쉘스크립트로 관리되던 작업들을 Gruntfile 에 통합하고 grunt-devtools 를 사용해 여러 작업들을 GUI 로 작동할 수 있게 만들었다.

설치과정과 실행방법을 공유하고자 한다.

설치

크롬 익스텐션과 grunt-devtools npm 패키지를 설치해야 한다.

크롬 익스텐션 설치

크롬 익스텐션은 크롬 웹스토어에서 설치할 수 있다. 바로가기

익스텐션을 설치하고 크롬 개발자도구를 열어보면 Grunt 탭이 추가된 것을 알 수 있다.

abstract

grunt-devtools 명령어 설치

명령어를 실행할 것이므로 -g 옵션을 주어 패키지를 설치한다.

$ npm install -g grunt-devtools

사용방법

Gruntfile 이 있는 경로에서 grunt-devtools 를 실행하고 개발자 도구의 gruntjs 탭으로 이동하면 프로젝트가 추가된다.

$ grunt-devtools

여러 디렉토리를 옮겨가며 추가하면 상단 프로젝트 바에 여러 프로젝트들이 추가된다. 이 프로젝트들은 일시적인 것으로, grunt-devtools 와 크롬을 종료하면 사라진다. 따라서 크롬을 다시 시작하면 프로젝트마다 다시 실행해줘야 한다.

그리고 왼편에 위치한 태스크 리스트 아이템을 더블클릭하면 태스크가 실행된다.

태스크에는 Background Tasks, Alias Tasks, Tasks 가 있다.

Alias Tasks 나 Tasks 는 Grunt 의 기본기능이다. Background Tasks 가 생소한데, 간단히 말하면 콘솔을 점유하는 watch 같은 태스크를 Background 로 실행시킬 수 있는 기능이다. 콘솔을 점유하는 태스크들은 종료하지 않으면 다른 태스크를 사용할 수 없다. 이럴 때 Background Tasks 로 실행시켜두면 실행을 종료하지 않고 다른 태스크를 동시에 실행할 수 있다.

태스크를 일단 실행시키고 그 위에 B 아이콘을 클릭하면 Background Tasks 로 실행된다.

background-task

이제 디자이너는 grunt-devtools 라는 명령 하나만 외우면, 혹은 단축 아이콘을 더블클릭하는 것으로 서버를 띄우고 스프라이트 아이콘을 빌드하는 등의 작업을 할 수 있게 되었다. 더 쉽게 더 많은 권한을 드릴 수 있게 되어 기분이 매우 좋다 🙂

P.S. Gulp 에도 gulp-devtools 라는 프로그램이 있다.

페이스북에 Gruntjs 관심그룹을 운영 중입니다. 가입을 언제나 환영합니다 😀

Git Merging 과 Rebase 의 상황별 사용법

Git 을 사용하기 시작한지가 벌써 3년이다. 한번은 문상환님하고 Git 의 Merge branch 커밋에 대해 이야기를 한 적이 있다. 대화가 진행될 수록 Rebase 와 Merge 를 머리로만 알고 있을 뿐, 제대로 이해하지 못하단 걸 알게됐다. 일종의 산파법이랄까.

Git 에서 코드를 합치는 방법에 대해 탕수육의 뿌먹파와 찍먹파처럼 Rebase 파와 Merging 파가 있다. 나는 Merging 파였다. Rebase 에 대해서 알고는 있지만 그저 “충돌나면 머리아프니 안써” 라며 배제 했었는데 좋은 글을 찾아 요약하게 되었다.

개인적으로 중요한 내용인, 각각의 장단점과 활용방법에 대해서만 요약했다. 원문은 소스트리 블로그 에서 볼 수 있다.

각 기능의 장단점

Merging 장점

  • 이해하기 쉬움
  • 원래 브랜치의 컨텍스트를 유지함.
  • Fast-Forward Merge 를 하지 않는다면 브랜치 별로 커밋을 분리해 유지. 특히 이런 분리는 기능 브랜치에 유용.
  • 원래 브랜치의 커밋들은 변경되지 않고 계속 유지되어 다른 개발자들의 작업과 공유되는 것에 대해 신경쓸 필요가 없음.

Merging 단점

  • 단순히 모든 사람들이 같은 브랜치에서 작업하기 때문에 머지해야할 때는 merge 가 커밋 히스토리상으로 전혀 유용하지 않고 어지럽기만 하다.

Rebase 장점

  • 단순한 히스토리
  • 여러 개발자들이 같은 브랜치를 공유할 때 커밋을 합치는 가장 직관적이고 깔끔한 방법.

Rebase 단점

  • 충돌상황에서 다소 복잡. 커밋 순서대로 Rebase 를 하는데, 각 커밋마다 충돌해소를 순서대로 해주어야 한다. SourceTree 가 가이드하기는 하지만, 역시 복잡한 것은 사실이다.
  • 해당 커밋들을 다른 곳에 푸시한 적이 있다면 히스토리를 다시쓰는 것에 부작용이 발생한다. Mercurial 에서는 간단히 푸시를 할 수 없다. Git 에서는 Push 할 수 있으나 당신 혼자 쓰는 리모트 브랜치에만 가능하다. 만약 다른 사람이 그 브랜치를 체크아웃 받은 후 당신이 리베이스 한다면 꽤 혼란스럽게 될 것이다.

결론

Rebase 와 Merging 모두 나름의 가치가 있는 것으로, 논란의 대상이 아니고 상황에 따라 사용해야할 것이 다른것이다.

  1. 여러 개발자들이 같은 브랜치를 공유할 때는 Pull & Rebase 가 히스토리를 깔끔하게 유지하는데 좋음.
  2. 완료된 기능 브랜치를 다시 합칠 때는 머지를 사용.
  3. 기능 브랜치에 부모 브랜치의 변경 내용을 반영하고 싶을 때는
    1. 아래 상황에서는 리베이스가 낫다.
      • 이 브랜치를 다른 곳에 푸시한 적 없는 경우.
      • (Mercurial 이 아닌) Git을 사용하고 다른 사람이 이 기능브랜치를 체크아웃할 일이 없을 것이라 확신하는 경우.
    2. 이 외의 상황에는 머지가 낫다.

따라서 KStyleTrip 는 Git 그라운드 룰을 다음처럼 정하기로 했다.

  • Remote 와 Local 에 동시에 존재하는 브랜치를 Pull 할 때에는 Rebase 를 사용하도록 한다.
  • 기능 브랜치에 대해서는 Merge 를 사용, Rebase 와 비슷한 동작을 하게되는 Fast-Forward Merge 를 사용하지 않는다.
  • 기능 브랜치에 그 부모 브랜치의 내용을 합칠 때는 로컬 브랜치일 때만 Rebase 로 합침.

p.s. Github 클라이언트는 Pull 시 자동으로 Rebase 한다, Sourcetree 도 해당 기능을 설정할 수 있다. 설정법은 원문 참조.

핸드스튜디오 사내강의 “Git+, Git 조금 더 배워보기”

2013년 핸드스튜디오와 연이 닿아 프로젝트를 함께 진행한 적이 있었다. 개인적으로 매우 잘 아는 유명한 회사이다 🙂

당시 핸드스튜디오는 Git 을 사내 버전 관리시스템으로 도입한 초기였고, 그 과정에서 도움이 되고싶어 사내강의를 자처했다.

당시 “svn 능력자를 위한 git 개념 가이드” 라는 아주 좋은 슬라이드가 있어 도입초기에는 이 슬라이드를 교안으로 강의를 진행했고, 이후 조금 더 잘 사용해보자는 의미에서 아래 슬라이드를 제작했다.

강의 이후 까맣게 잊고 지내다 며칠 전 함께 일했던 동료로부터 git flow 도입과 관련한 문의를 받고 생각난 김에 슬라이드쉐어에 업로드 했다.

아래 슬라이드는 git-blame, git-log, git-rebase, git-flow 에 대해 간단히 알아보는 정도로 내용이 구성되어 있어 능력자들께는 부족할 수도 있겠다 🙂

혹시라도 사내 강의가 필요하시면 연락주세요(소곤소곤)