Dockerfile – ADD 를 통해 build 캐시막기

Dockerfile 을 수정하고 다시 빌드할 때 cache 덕분에 빠르게 재실행이 가능하다.

Dockerfile 의 명령라인이 변경되지 않으면 자동으로 캐시가 작동하게 되는데 가끔 이 기능이 불편할 때가 있다.

Node 개발용 서버를 구축하는 중에 이런 경우를 만났다.

git pull 을 하고, 의존성 해결을 위해 npm install 을 하는 플로우인데, RUN git pull 행이 변경되지 않으니 계속 캐시가 작동했다.

캐시를 작동하지 않게 하는 명령을 추가하는 것에 대해 이 곳에서 계속 논의가 진행중인데, 이 이슈를 살펴보다 랜덤스트링을 추가하는 괜찮은 방법을 알게되었다.

ADD http://www.random.org/strings/?num=10&len=8&digits=on&upperalpha=on&loweralpha=on&unique=on&format=plain&rnd=new /var/tmp/uuid

이 명령을 더이상 캐시가 필요하지 않는 곳에 추가해주면 된다. 이렇게 추가해주면 build 할 때마다 랜덤스트링을 다운받고 컨테이너의 파일이 변경되기 때문에 이후의 명령들은 캐시가 동작하지 않게된다.

어느 스타트업의 백오피스 인증 구현기

스타트업에는 놓치지 않아야 할 여러 가지가 있다. 난 그 중 백오피스를 꽤 높은 순위에 둔다.

백오피스를 가졌을 때의 장점은

  1. 서비스를 만들고 지표의 변화를 감지해 서비스를 개선하는 데 큰 도움이 된다.
  2. 개발자전용 인터페이스(REST API 도구라든지 <form> 만 있는 HTML 페이지 등)를 사용하는 것보다 모두의 도구를 만드는 것이 가장 시간이 부족할 때 큰 도움이 된다.

지표에 대해 조금 더 이야기하면 IT 기업에서 지표를 측정하려면 개발자의 도움이 꼭 필요하다. Google Analytics와 같은 도구의 코드를 삽입하거나 데이터베이스 내의 수치 등의 많은 지표가 개발자의 손을 통해 측정되고 알 수 있게 되기 때문이다. 백오피스가 없는 경우 데이터에 접근성이 떨어지고 알고 싶은 리포트를 수동으로 작성할 수밖에 없다. 가뜩이나 인원도 시간도 모자란 스타트업에게 보고서 작성하는 시간을 할애하는 것과 같다고 생각한다.

현재 근무하고 있는 회사는 KStyleTrip.com이고 첫 서비스로 Luck2share 를 만들었다. 새로 입주한 사무실을 꾸미면서 위시리스트를 작성하려고 했는데 공수도 많이 들지 않을 테니 서비스로 만들어보자. 그리고 dogfooding 해보자는 개념으로 시작한 서비스다. KStyleTrip 의 위시리스트 바로 가기

아직 kstyletrip.com 은 제작 중이기 때문에 백오피스의 플랫폼을 만드는 작업은 주로 이 Luck2share 를 대상으로 했다. 그리고 회사의 서비스와 관리자에게 필요한 기능은 앞으로 계속 늘어날 것이기 때문에 서비스마다 각기 다른 백오피스를 갖는 것이 아니라 모든 서비스가 통합된 백오피스를 제작하는 것으로 방향을 잡았다. 이 방법이 각 서비스가 방치되지 않고 잘 관리되는 방법이라는 것에는 이견의 여지가 없으리라고 생각한다.

DB 쿼리 vs. REST API

여기까지 생각이 미치고 구현에 앞서 고민했던 점은 Luck2share 의 데이터를 가져오는 방법이었다. 검토했던 방법은 DB 포트를 열고 백오피스 서버에서 접근해 관리자 전용 API를 구현하는 것과 Luck2share 서버에 관리자용 API를 만들어 사용하는 것이었다.

Luck2share 의 DB 포트는 외부에 공개되지 않고 서버 안쪽에서만 사용하고 있다. 따라서 상대적으로 안전하게 보관되고 있는데 이 포트를 열어 보안이슈를 만들고 싶지 않았다. 그리고 이미 Luck2share 내에 DB쿼리하는 코드베이스가 충분한데 비슷한 코드를 다시 만들어야 하는 것에도 거부감이 있었다.

따라서 API는 Luck2share 서버에 두고 데이터를 가져오는 것으로 방향을 잡았다.

Private API 구축 – 비밀번호 공유 vs. 비대칭 키암호화

두 번째 고민은 Private API를 구축하는 부분이었다. 관리자를 위한 API라면 서버 간, 또는 서버와 클라이언트 간 HTTP 통신을 구축하는데 인증수단이 반드시 있어야 한다. 따라서 HTTP의 Authorization헤더를 통해 인증하는 방법을 사용했다.

우리는 구글 앱스를 사용하고 있기 때문에 회원을 관리할 필요는 없다. OAuth 로그인을 정상적으로 마친 후 OAuth 로부터 얻어온 회원정보에서 몇 가지를 추려 다시 토큰을 만들었다.

이 토큰을 Luck2share 서버에서 검사하도록 해야 했는데 secret을 공유해 암호화/복호화하는 방식은 깔끔하지 않을 뿐 아니라 유출되었을 때 다른 서비스도 동시에 유출되기 때문에 안전하지 않다. 따라서 비밀 키/공개 키를 통한 암호화 방식을 선택했다.

아래와 같은 절차로 Private API에 접근할 수 있다.

  1. 우선 비밀 키와 공개 키를 생성한다. 이 키 쌍의 비밀 키는 백오피스 서버에, 공개 키는 Luck2share(다른 서비스들의 서버)에 둔다
  2. 사용자가 로그인할 때 사용자의 필수정보와 토큰 생성시간과 관련한 데이터를 묶는다. 시간을 묶으면 생성할 때마다 토큰이 확연히 달라지기 때문에 유출의 위험이 적다.
  3. 2에서 만들어진 데이터를 백오피스 서버의 비밀 키로 암호화해 토큰으로 만든다.
  4. 통신이 필요할 때 토큰을 Luck2share 에 전달한다.
  5. Luck2share 에서는 미리 받아놓은 공개 키로 토큰을 복호화한다.
  6. 복호화가 정상적으로 이루어지고 조건에 맞으면 API 호출결과를 돌려주고 그렇지 않으면 401 Unauthorized를 내보낸다.

백오피스 API 서버에서 사용한 프레임워크는 Restify 로, Node 의 jwt 모듈과 express-jwt 미들웨어를 사용해 큰 어려움 없이 Private API를 구축할 수 있었다.

(사실 키 암호화에 대해 깊은 조예가 있어서 떠올린 것이 아니라, jwt 모듈의 기능을 살펴보다 알게 되었다. :-P)

OAuth Login 과 만료시간

앞서 밝혔듯 KStyletrip.com은 Google Apps for Business를 사용하고 있다. 따라서 구글에서 제공하는 OAuth 인증을 사용하면 되므로 회원가입, 회원정보 수정과 같은 것을 구현할 필요가 없었다. 단지 OAuth Consumer로서 회원정보를 활용하는 방법을 구현하면 되었다. 다만 Google에 로그인할 때 제공하는 Access Token의 만료시간은 1시간으로 굉장히 짧아서 API 호출 때마다 이 정보에 의존할 수는 없었다.

따라서 별도의 토큰을 만들어 유지하기로 했다. 이 토큰은 위에 설명한 Private API에 접근하는 토큰과 같은 것이다. 이 토큰을 만들고 사용하는데 만료기간이 너무 짧으면 불편할 것이고 너무 길면 퇴사하거나 변동이 생긴 직원에 대해 대처하기 어려워질 것으로 판단했다.

그래서 아래와 같은 방법으로 구현했다.

OAuth 인증 시에 오프라인 접근권한을 받을 수 있도록 했다. 오프라인 접근권한을 받으면 Refresh Token을 얻어올 수 있다. 이렇게 받아온 Refresh Token으로 백오피스 API 서버가 주기적으로 Google의 OAuth 서버에서 Access Token을 받아오도록 했다. 이렇게 하면 자동으로 사용자의 유효성을 측정할 수 있게 되어 퇴사한 직원에 대해 걱정하지 않아도 된다.

그리고 유효성이 확인된 토큰들을 메모리에 (사실은 API 서버의 변수에) 넣어두고 최초 로그인시 발급된 토큰을 갱신할 수 있는 API 를 만들었다. 변수에 저장된 토큰들은 서버를 새로 시작하면 날아가 버리기 때문에 다른 방법으로 보완할 예정이다. 2014. 8. 3 추가: nedb (Node Embeded DataBase) 를 사용했다.

프론트엔드에서는 주기적으로 갱신 API를 호출해 토큰을 갱신하는 동시에 자체토큰을 가진 사람이 Google에서도 유효하게 판단하는지를 확인했다.

후기

일주일 남짓한 시간에 우리는 꽤 안정적인 백오피스 플랫폼을 갖게 되었다. 그리고 이제 실제로 비즈니스에 필요한 API를 필요할 때마다 개발하기만 하면 된다.

참고

VirtualBox – Vagrant Disk IO 성능개선 방법

  • VirtualBox 에서 제공하는 공유폴더 기능은 심각한 퍼포먼스 저하가 있다. 따라서 다른 방법으로 파일을 공유하는 것이 성능에 훨씬 유리하다.

    • 참고: Vagrant 개발자인 Mitchell Hashimoto 의 VM 내 파일시스템 성능비교 https://mitchellh.com/comparing-filesystem-performance-in-virtual-machines

    • 해결법 – shared folder 를 NFS 로 공유하기 또는 Rsync 로 공유하기 (Rsync는 Vagrant 1.5 이상에서 동작)

      • NFS 로 만드는 방법 간단요약 – 참고: https://coderwall.com/p/uaohzg https://docs.vagrantup.com/v2/synced-folders/nfs.html

        • Host only Adapter 추가 – Virtualbox > VM

        • private_network 행 추가 – Vagrantfile

        • shared_folder 에 type: nfs 추가 – Vagrantfile

      • Host OS 의 /etc/exports (NFS 설정 파일) 에 Vagrant 가 자동으로 행을 추가하기 때문에 vagrant up 도중 sudoer 패스워드 물어봄

    • Rsync 는 일반적인 Rsync 와 동일하게 동작한다. Guest 와 Host 양쪽에 rsync 명령어가 있으면 활용이 가능하다. 윈도우에서도 msysgit, MinGw, Cygwin 에서 Rsync 기능 제공 중

      • 다른 shared folder 타입들과 달리 rsync 명령어를 활용하기 때문에 Rsync 는 vagrant up 때 한번 싱크. 따라서 자동으로 파일 변화를 감지해 싱크를 맞춰주는 vagrant rsync-auto 를 사용해야 함.

하이브리드 프론트엔드 개발 5원칙

3년 정도 하이브리드 플랫폼들을 경험했다. 하이브리드 플랫폼은 정말 여러 곳에서 쓰이고 있다. 하지만 그 모든 플랫폼이 크롬, 파이어폭스만큼 우수한 디버깅 환경을 제공하지는 않는다. 에뮬레이터를 지원하지 않는 경우는 부지기수이고 한 줄이라도 수정하면 앱을 종료했다 다시 켜야 변경사항이 반영되는 때도 있다.

오늘도 하이브리드 개발 중에 API 서버가 죽었다. 일정도 꼬인 김에 하이브리드 개발 때 도움이 될만한 팁을 공유해본다.

독립된 API 서버를 갖춰라

가능하다면 독립된 API를 갖는 게 좋다. 하이브리드 앱은 API와 통신이 많은 부분을 차지한다. 끊임없이 개발되고 있는 와중에 개발 중인 API가 안정적이기를 바라는 것은 무리다. 하지만 최소한 지금 프론트엔드 개발 중인 API는 안정적이어야 한다. 따라서 독립된 API 서버를 확보해서 현재 개발 중인 부분의 API 는 안정적으로 동작하도록 버전 관리 시스템의 리비전을 변경하면서 개발하자. “가능하다면” 이란 단서를 달았다. 만약 독립된 API 서버가 없다면 원활한 개발을 포기하면 된다.

ps. 단순한 프로젝트라면 API 서버를 대체할만한 목업 서버라도 만들어서 쓰기를 강력히 추천한다.

ps2. Vagrant나 Docker 사용도 추천할만 하다.

프락시를 활용하라

하이브리드 플랫폼에서는 hosts 파일을 변경하기 어렵다. 따라서 다른 방법으로 우회해야 하는데 그것이 바로 프락시다. 단말기의 네트워크 설정에서 프락시 서버를 사용하도록 하고 프락시 서버를 고쳐서 hosts 와 같은 효과를 낼 수 있다. 리버스 프락시와 PAC(Proxy Auto Config) 를 사용한다면 더 편리한 개발이 가능하다. 단말에 탑재된 소스 중 일부만 로컬서버에서 동작하게 할 수도 있고 문제가 있는 API 만 다른 API 서버로 우회하도록 동작시킬 수도 있다. 특히 소스를 곧바로 고칠 수 없는 환경에서 프락시는 활용도가 무척 높다. 가끔 프락시 조차 지원하지 않는 단말기가 있다. 이럴 때는 Wireless AP 를 만들어 해결하는 방법도 있다.

최대한 웹 브라우저에서 확인할 수 있도록 개발하라

하이브리드 애플리케이션은 단말 전용 API를 이용해 브라우저보다 더 많은 기능을 사용할 수 있다. 하지만 전용 API 를 사용한 특별한 부분을 제외하면 거의 모든 기능은 브라우저로도 동작할 수 있다. 어떤 하이브리드 플랫폼도 브라우저만큼 개발환경을 지원해주지 못한다. 따라서 기능 대부분을 브라우저에서 확인할 수 있도록 개발하는 것이 좋다.

가끔은 브라우저용 폴백을 직접 만들어야 할 때도 있다. 예를 들어 화면간에 파라미터를 전달하는 기능은 URL hash에 JSON 을 담아 전송하면 쉽게 파라미터를 전달할 수 있다. 하이브리드 플랫폼에서 데이터베이스를 사용할 수 있다면, Ajax로 데이터베이스에 접근하는 간단한 서버프로그램을 작성해 활용하면 브라우저에서도 쉽게 확인할 수 있다.

플랫폼에서 제공하는 디버깅 도구에 대해 공부하라

브라우저에서 디버깅하는 방법에 대해 잘 알고 있는 만큼 개발중인 디바이스 플랫폼에 대해서도 공부가 필요하다. 예를들어 안드로이드 플랫폼에서는 adb 를 사용할 수 있다. adb 를 통해 개발중인 소스를 기기로 복사할 수 있고 로그도 곧바로 볼 수 있다.

리모트 디버깅 도구를 활용하라

iOS6 와 OSX Lion 이상의 환경에서는 사용한다면 Safari 브라우저를 통해 웹뷰의 디버깅이 가능하고, 최근의 안드로이드 브라우저도 크롬을 통해 디버그할 수 있다. 위 플랫폼에서 벗어난 경우엔 Weinre 를 사용해보는 것을 추천한다.

한마디로 정리하면 – 가능한 모든 것을 추상화하라.

더 효과적인 피어 코드 리뷰를 위한 여섯가지 방법

한빛미디어에서 당신의 동료가 더 효과적으로 코드리뷰를 할 수 있게 만드는 여섯 가지 방법 라는 좋은 글을 접했습니다. 그런데 읽다보니 번역문이 이해하기 어려운 점이 많아서 원문을 다시 읽게되었고 요약하게 되었습니다. 아래 그 내용을 공유합니다.

디자이너에겐 크리틱(critique – 비평) 이 있고, 개발자에겐 코드리뷰가 있다.

좋은 비평이란 나쁜 피드백을 좋은 피드백으로 감싼 샌드위치같은 것이 아니다. 좋은 비평을 위해서는 아래 세가지가 필요하다.

  1. 합의된 검토 수단
  2. 리뷰어의 객관성
  3. 자신의 작업물을 객관적으로 바라볼 수 있는 작성자

정량적 검토

논쟁이 생기기 어려운, 대부분 쉬운 결론이 나는 검토. 한쪽은 옳고, 다른쪽은 그르다. 적절한 정량적 검토수단을 가지면 작업물을 적절한 자동화 테스트에 넣으면 되므로 리뷰어들을 자유롭게 함. 예를들면 코딩 규칙을 지켰는지 등이 정량적 검토에 속함.

정성적 검토

의견들로 가득한, 들어보면 모두 옳은, 쉽게 결론이 나지 않는 검토. 매우 어렵기 때문에 정성적 평가 수단을 만드는 것은 시도조차 하지 않는 경우가 많음. 하지만 작성자, 리뷰어 모두의 스킬을 올려줄 만하다. 정성적 평가 수단은 비 전문가인 리뷰어에게 코드에서 어떤 요소를 찾아야 하는지 알려줌.

정성적 검토에는 아래와 같은 질문들이 포함됨:

  • 이 코드가 이 프로젝트에서 사용된 적 있는 패턴을 구현했는지?
  • 이 코드가 다른 상황에서도 사용할 수 있을 정도로 충분히 추상화 되었는지?

커뮤니케이션의 어려움

평가라는 것은 결국 도덕적인 평가와 닮아있음. 좋은 리뷰어는 위에 언급된 평가 방법 내에서 다른 것을 많이 더하지 않고도 피드백을 표현할 수 있음. 이건 결국 작성자들에게 (항상 비슷한 수준으로) 중립적으로 대응할 수 있도록 함.

이론적으로는 리뷰어가 말하는 의도를 작성자는 완벽하게 알아들을 수 있음. 하지만 사람들이 모국어가 아닌 외국어로 읽고 쓰는 경우가 많기 때문에 그런일은 잘 없음. 커뮤니케이션은 어렵고, 특히나 중립적인 커뮤니케이션은 더더욱 어렵다. 사실 중립적인 의사소통은 종종 적대적 편향 – 모호한 의사소통을 적대적으로 해석하는 인간의 특성 – 때문에 망가진다.

유용한 팁들

아래 가이드 라인을 따르기 전에 평가가 적당한 유형으로 이뤄지는지 확인해야함. 일단 커뮤니티가 기능과 광범위한 세부 구현에 대해 동의하면 코드를 작성하고 그 후엔 리뷰가 진행되어야 한다

리뷰 중에는 아래 사항들을 따르는지 확인할 것:

  1. 리뷰를 작업의 영역으로 제한하기. 효율적인 리뷰는 문제에 대해 레이저로 쏘는 것처럼 한다. 추가적인 고려사항이 필요한, 범위를 넘어서는 문제는 여기에 딸린 액션 아이템(이슈트래커의 티켓 같은)으로 만들것.
  2. 리뷰를 특정하기. 특정 라인에 대해, 컴포넌트에 대해서만 언급할 것. 일반적인 문제로 만들지 말고 명확하게 표현할 것.
  3. 리뷰를 실천가능하게 할 것. 리뷰한 것을 어떻게 변경구현할지 매우 명백해야 함. 그렇지 않으면 해당 리뷰는 지울것.
  4. 빠른 시간 내로 리뷰를 전달할 것. 리뷰를 전달하는데 오래걸리면 자신의 작업물을 왜 그렇게 개발했는지에 대해 잊어버리기 쉽다. 필자는 리뷰하는 시간을 동료와 미리 정해 놓고 함께 작업함. 잘 안되는 경우도 있지만 작은 팀에서 효율적임.
  5. 해당 문제에 대해 코더가 소요할 시간을 리뷰의 일부로 고려해야 함. 세줄을 고치는데 몇시간이 소요될 수도 있다. 인내하고 이슈를 해결하는데 시간이 걸릴 수 있음을 인지해야함.
  6. 감사하라. 오픈소스 프로젝트라면 일반적으로 기여자들에게 기여의 의무는 없었다. 특히 직장에서는 정말 싫어하는 프로젝트의 일원이 되어서 일할 것을 강요받는 경우도 있다. 감사하다고 이야기하라. 이 모든 것이 달라질 것이다.