이번엔 API 설계 원칙과 직렬화 포맷을 결정한다. 대부분의 경우 이 두가지는 '당연히 REST랑 JSON 아님?' 하며 관례적으로 결정하고 넘어가곤 하지만, 빼먹지 말고 이것도 의사결정 과정을 끼워 두자. 사실 이 앞에 프로토콜을 결정하는 챕터를 넣어두려 했는데, 거기까진 너무 TMI인 것 같아서 나중으로 미뤘다. 프로토콜은 일단 현재로선 일반적으로 사용되는 HTTP/1.1을 사용하는 것으로 하자. 물론 추후에 HTTP/2에 대한 이야기도 할 예정이다.

API 설계 원칙의 도입 이유

우리는 결론적으로 웹 어플리케이션 서버를 개발하고 운영하는 것이 목적이므로, '이렇게 HTTP 요청을 보내면, 이렇게 응답해준다'라는 스펙을 기능에 따라 설계해야 한다. API 설계 원칙은 웹 서버의 API 스펙을 어떤 규칙에 따라 정의할 것인지를 나타낸다.

  • 'GET /post는 게시글 목록을 불러오고, GET /post/{id}는 특정 게시글의 내용을 불러온다'같은 설계는 다 아키텍처 기반으로 결정하는 것이 좋다. 아키텍처가 없다고 API 디자인을 못하는 것은 아니지만, 의사결정의 기반이 있는 것이 좋기 때문이다. 나는 옛날에 이런 걸 몰랐어서 모든 요청을 싹 다 POST /로 받고, 101, 102와 같은 동작 code를 가지고 로직을 분기시켰다. '미친놈인가'하는 소리 안 들으려면 아키텍처 베이스가 있어야 한다.
  • '잘 디자인된' API는 불필요한 커뮤니케이션 비용을 줄인다.

직렬화 포맷의 도입 이유

직렬화 포맷에 대해 예를 들면, '게시글'을 나타내는 '제목'과 '내용' 데이터를 표현하기 위해 아래와 같은 방법들을 사용할 수 있다.

데이터를 어떤 방식으로 표현할 지를 결정해 두어야, 클라이언트와 서버 간의 데이터 교환에서 혼란을 줄일 수 있다.

의사결정 - API 설계 원칙 결정

배경과 요구사항

  • 클라이언트 사이드에게 익숙한 아키텍처거나, 러닝커브가 감당 가능한 수준이어야 한다.
  • 개발 일정에 딜레이를 일으킬 여지가 있으면 안된다.

선택지

  • HTTP API
  • REST API
  • GraphQL

의사결정

HTTP API를 선택하겠다. 그 이유는,

  • REST API가 명시하는 모든 원칙을 만족하는 API를 작성하는 것은 쉽지 않다. 결국은 '느슨한 REST' 느낌의 HTTP API가 되기 마련이다. 따라서 괜히 RESTful API 이러면서 깝치다가 정의구현 당하는 수가 있다. 제약조건을 따르던지 다른 단어를 쓰도록 하자. REST의 self-descriptiveHATEOAS 원칙은 만족하기 정말 어렵다.
  • REST는 HATEOAS(hypermedia as the engine of application state)라는 원칙을 지켜야 하는데, 이는 '어플리케이션의 상태가 Hyperlink를 이용해 전이되어야 한다'라는 의미다. 우리의 API는 미디어 타입이 JSON(JSON 형식의 데이터를 위주로 주고받는 형태)일텐데, HATEOAS를 지키기 어렵다.
  • API가 꼭 REST API여야 할 필요가 없다.
  • GraphQL은 사례가 너무 적다. 사용자 인증 처리 로직을 생각하는 것부터 고민이 많아진다.
  • GraphQL은 클라이언트 사이드에서 러닝커브가 생길 수 있다.
  • HTTP API가 이야기하는 아키텍처로 충분할 것이라고 판단했다.

본인이 REST라는 걸 잘못 알고 있었나 싶다면, 그런 REST API로 괜찮은가라는 슬라이드 자료를 읽어보자.

준비

MDN의 HTTP 개요 문서, 이고잉님의 HTTP 강의 영상을 한번쯤 보면 좋을 것 같다. 'HTTP를 하나도 모르겠다'라면, 진도를 더 나가기 어려울 수 있으니 개발 잘하는 친구한테 물어보거나, 구글링을 하거나 해서 조금이라도 배경을 쌓고 넘어가자.

의사결정 - 직렬화 포맷

예를 들어, Kotlin로 이루어진 안드로이드 어플리케이션이 HashMap 객체를 문자열과 같이 특별한 형태로 가공해서 보내면, Node.js로 구성된 WAS가 이를 JavaScript 고유의 Object 타입으로 해석해 사용할 수 있어야 한다. 이를 위해 표준화된 직렬화 포맷이 여럿 존재하며, 그들 중 어떤 포맷을 사용할 것인지를 결정하도록 하자.

배경과 요구사항

  • 이것 또한 클라이언트 사이드에게 익숙한 아키텍처거나, 러닝커브가 감당 가능한 수준이어야 한다.
  • key-value 매핑array와 같은 요소의 나열 표현에 문제가 없어야 한다.
  • 충분한 수준의 데이터 타입을 커버할 수 있어야 한다. - 정수, 실수, 문자열, boolean 등

선택지

의사결정

JSON을 사용하자. 그 이유는,

  • 동일한 데이터를 표현하더라도, JSON이 비교적 더 잘 경량화되어 있으며 가독성도 좋다.
  • XML의 tree 구조는 자원을 표현하는 데에 그리 효과적인 포맷은 아닌 것 같다고 판단했다.(Array의 표현이 어려움) 옛날엔 많이 쓰였다고 하는데, 주관적인 의견을 덧대자면 진짜 이거 왜 쓰는지 모르겠다. 아직도 공공 API의 일부가 XML을 쓰는 게 정말 놀랍다.
  • YAML관례 상 직렬화 포맷으로 잘 사용하지 않는다. 역직렬화 속도도 느리고, YAML을 썼을 때 생기는 메리트가 딱히 없다.
  • Protobuf구글에서 개발한 data exchange format이다. 직렬화/역직렬화 속도가 빨라 성능 상의 이점이 있고, .proto 파일을 정의하는 것만으로 validation rule들을 정리하고, 비교적 적은 노력으로 API 문서화에도 응용할 수 있으며, 클라이언트 단은 proto 컴파일을 통해 이들에 대응되는 클래스(DTO)들을 자동으로 정의할 수도 있어서 시도해볼 가치가 충분하다. 그러나 조직 내에 protobuf를 사용했을 때 문제가 없을지에 대한 신뢰 수준이 낮고, 우리의 서비스 규모가 크지 않으므로 protobuf에 대한 확신이 생기고 난 후에 바꾸더라도 스트레스가 크지 않을 것이라는 생각이다. 당장은 다들 익숙해 하는 JSON을 쓰고, 나중에 바꿔 보자.
  • JSONJavaScript Object Notation의 약자다. JavaScript나 그 형제들(TypeScript 등)로 로직 처리를 하게 될 프론트엔드에게 JSON만큼 편한 구조가 없으며, 모바일 앱과 웹을 포함해 대부분의 프론트엔드 엔지니어들은 이미 JSON에 익숙해져 있다.

준비

Stackoverflow의 'What is JSON and why would I use it?이라는 질문과, json.org의 JSON 개요 문서를 한 번 읽어 보자. 이번 챕터에서는 의사결정에 관례의 비중이 컸다. 기술 선택에 너무 의외성이 크면 조직 전체에 걸쳐서 기술부채가 생길 수 있어서, 관례를 감안할 수밖에 없었던 것 같다.

지금까지

  • 버전 관리 시스템으로 Git을 사용하기 시작했다.
  • Git 웹호스팅 서비스로 GitHub를 사용하기 시작했다.
  • GitHub Issues와 Projects로 이슈 트래킹을 시작했다.
  • 개발 프로세스와 브랜칭 모델을 정립했다.
  • HTTP API 아키텍처 기반으로 API 스펙을 디자인하기로 했다.
  • JSON을 직렬화 포맷으로 결정했다.


이번엔 백엔드 포지션에서 어떤 방식으로 개발을 진행할지를 결정하자. 개발 프로세스 정립에 대해 두 가지의 의사결정을 진행할 것이다.

도입 이유

개발 프로세스는 어떻게 이슈를 관리하고, 어떤 방식으로 작업을 진행하고, 완료된 작업은 어떤 과정을 거쳐서 실제 제품에 반영시킬지와 같은 것들을 규칙화시킨 것이다. 정립이 필요한 이유는 다음과 같다.

  • 프로젝트 관리자 입장에서 각 개발자에게 할당된 작업들이 어떤 상태인지(준비 중/진행 중/완료)를 쉽게 알 수 있다.
  • 이슈를 실제로 처리하는 입장에서, 개발 프로세스에 따라 작업을 진행하면 어느 브랜치에서 어떤 이름으로 브랜치를 생성할지/어느 브랜치로 pull request를 올리며 리뷰를 받아야 할지/master merge 후 어떤 후속 작업을 진행해야 하는지와 같은 고민을 줄일 수 있다. 실제로 작성하는 코드에만 집중하는 환경을 만들 수 있다.
  • 잘 정립된 개발 프로세스는 생산성을 높이는 것에 기여하며, 작업의 진행을 매끄럽게 만들고, 결과물의 퀄리티를 높인다.

이 의사결정에는 별도의 선택지가 없다. 스크럼이고 칸반이고 하는 정해진 프로세스가 많긴 하지만, 조직의 상황에 딱 맞는 개발 프로세스를 찾기는 어렵기 때문에, 기존의 개발 프로세스를 기반으로 하나씩 직접 정립해 보자.

의사결정 - 이슈 관리

이슈 관리 도구를 결정하자. 이슈는 작업이라고 생각하면 된다. 작업을 리스팅하고, 작업을 처리할 작업자를 할당하는 등의 일을 위해 사용한다. 대부분의 조직은 이슈를 기반으로 움직이고, 나도 이런 흐름이 좋다고 생각한다. 이슈 관리 도구는 이슈 트래커라고도 부른다. JIRA를 쓰기도 하고, Trello를 쓰기도 하고, Google 스프레드시트를 쓰는 조직도 봤던 기억이 있다. 나는 과거에 Asana를 많이 썼었다.

배경과 요구사항

  • 이슈 각각에 대해 작업자를 assign할 수 있다.
  • 이슈에 대해 커뮤니케이션할 수 있어야 한다.
  • 태그/라벨 등으로 이슈의 종류를 구분할 수 있어야 한다.(feature, hotfix, enhancement 등)
  • 이슈의 상태(To do, In progress, Review requested 등)를 구분해서 시각화할 수 있어야 한다.
  • 되도록 GitHub 내에서 해결할 수 있으면 더 좋다.

선택지

의사결정

GitHub Issues & Projects를 선택하겠다. 그 이유는,

  • Jira는 무조건 돈이 들어간다. 그렇다고 큰 메리트가 따로 있다고 생각이 들진 않는다. 큰 조직에서 매니저 급이 태스크를 관리할 때나 잘 쓰일 것 같은 트래커라고 생각한다.
  • Trello는 모던한 형태의 태스크 관리 보드인데, 필요한 기능들이 잘 들어가 있고 괜찮은 툴이지만 지금의 요구사항으론 GitHub 외부의 트래커를 따로 쓸만한 이유가 없다.
  • 위의 요구 사항을 모두 만족한다 - assign, issue conversation 기능이 있고, customized labelfilter by label 기능을 통해 이슈 종류 구분이 원활하고, projectsissues 기능을 연동해 이슈를 시각화 가능하며, projects의 automation 기능을 통해 이슈 구분이슈 상태 자동 갱신도 가능하다.(이슈가 close되면, projects에서 'Done' 컬럼으로 자동 이동시키는 등)
  • GitHub Issues에서 이슈를 등록하면 해당 이슈에 대한 번호가 매겨지는데, 커밋 메시지에 #123과 같이 그 번호를 명시해 두면 자동으로 해당 이슈가 링크되며 conversation할 때도 사용할 수 있다.(참고로 이건 Jira와 BitBucket을 연동하는 경우에도 가능하다.)
  • 이슈 트래커를 위해 따로 돈을 내지 않아도 된다. private repository를 생성 가능하게 만들기 위해 Organization 유료 플랜을 결제하는 경우는 있을 수 있지만, GitHub issues와 projects는 항상 무료다. 아래의 캡처들은 순서대로 각각 ProjectsIssues 기능이다.

준비

  1. 저번에 만들어뒀던 레포의 Projects 탭에 들어가 새로운 프로젝트를 하나 만든다. 프로젝트 템플릿은 아무거나 고르자. 나는 None으로 설정하고 직접 구성하려고 한다.
  2. Projects에서 Column을 원하는 대로 만들고 automation을 설정하자. 컬럼을 만들 때 보이는 'Automation'이나, 만들어진 컬럼의 우측 상단에 있는 ... 버튼으로 드롭다운을 열고 'Manage automation' 버튼을 누르면 보여지는 화면에서 Automation preset과 그 하위 카테고리를 선택하여 자동화를 설정할 수 있다. 나는 새롭게 생성된 이슈To Do, 리뷰가 요청되어 approve를 기다리고 있는 pull requestReview Requested, approve된 pull requestReviewed, close된 이슈Done이라는 이름의 컬럼에 자동화할 것이다. 위의 자동화들은 모두 automation preset에서 제공하는 기능이니 잘 찾아서 추가하기 바란다. 진행 중인 작업을 분류하기 위해 In Progress나, 나중에 조직이 커지며 품질 관리 과정이 생긴다면 Pending QA같은 컬럼이 추가될 수도 있을 것이다. 필자와 동일하게 해둘 필요 없으니, 입맛대로 만들기 바란다.
  3. 이슈에서 사용할 custom label을 설정한다. Issues 탭에서 Labels 화면에 들어가면 된다. 사람마다 다른데, 나는 우선순위 관점에서 minor/major, 종류 관점에서 feature/bug/enhancement 정도로 나눠둘 것이다. 참고로 필자가 평소에 프로젝트를 할 때는 major/minor/urgent, Non-code work/On-code work/Discussion 정도로 라벨을 만들어 둔다. 이건 나중에 필요할 때 추가하면 되니 대충 하고 넘어가자.
  4. 위에서 만든 프로젝트를 지정한 채 이슈를 생성하고 close했을 때 이게 Projects에 잘 동기화되는지 확인한다. 아래는 각각 이슈 라벨 설정, 이슈 아이템, automation이 설정된 Projects 화면이다.

의사결정 - 개발 프로세스 정립

이슈의 생성부터 작업이 제품에 반영되기까지의 사이클을 정리하자. Git에서 사용할 브랜칭 모델도 함께 정리될 것이다. 우리가 위에서 했던 걸로 따지면, 이슈는 최초에 'To Do'에 입력되고, 작업의 진척에 따라 오른쪽 컬럼으로 이동하는 식이기 때문에 프로세스는 칸반 기반이라고 생각하면 될 것 같다. 칸반에 대해서는 잘 가요 스크럼, 반가워요 칸반이라는 글을 보면 좋을 것 같다.

배경과 요구사항

  • 너무 복잡할 필요가 없다.
  • 개발 프로세스를 브랜치 lifecycle로 대응(ex. 작업 시작은 브랜치 생성, 작업 완료 후 브랜치 제거 등)시킬 수 있어야 한다.

의사결정

개발은 아래처럼 진행해보려 한다.

  1. 개발자의 공수가 필요한 작업이 생기면, 알맞는 라벨과 함께 이슈를 생성하고, 프로젝트 보드와 동기화시키기 위해 Projects를 선택해 둔다. 이슈가 'To Do'에 위치할 것이다.
  2. 해당 작업을 진행할 개발자가 정해지면, assign해 둔다.
  3. 개발자는 issue/<이슈 번호> 네이밍을 가진 브랜치를 master에서 체크아웃한다. 이슈 번호가 9번이라면, issue/9같은 식이다.
  4. 작업이 완료되면, master 브랜치로 pull request를 올린다. 이후 reviewer 기능을 통해 동료 개발자에게 리뷰를 요청하고, Projects를 선택해 둔다. pull request가 'Review Requested'에 위치할 것이다.
  5. 리뷰가 approve되면 merge하고, 문제 없이 작업이 완료되었다면 issue를 close하고 작업 브랜치를 제거한다. pull request는 'Reviewed'로, 이슈는 'Done'으로 이동할 것이다.

pull request를 올리지 않고 그냥 git merge 커맨드로 병합한 후 push하는 방법도 있지만, 리뷰 과정을 끼워두는 것이 더 안정적이다. 리뷰가 없다면 변경을 작업자만 알게 될 것이기 때문이다. 또한 작업용 브랜치를 체크아웃하는 이유는 master 브랜치의 코드는 항상 '현재 서비스에 적용되어 있는 상태'로 두려고 하기 때문이다. master의 코드는 항상 안정적인 상태로 둔다는 것이다. 나도 옛날엔 그냥 master에서 무작정 다 작업하고 그랬었는데, 대부분의 경우 이런 버릇은 좋지 않다.

위처럼 master에서 이슈 브랜치를 따는 것과 같은 룰을 브랜칭 모델이라고 부른다. git-flow, github-flow 이런 룰들이 많은데, 일단 우린 간단하게 master와 이슈 브랜치만 다뤄보는 걸로 시작하자. 브랜칭 모델에 대해 더 알아보고 싶다면 A Successful Git Branching Model (번역) 글을 읽어보자.

지금까지

  • 버전 관리 시스템으로 Git을 사용하기 시작했다.
  • Git 웹호스팅 서비스로 GitHub를 사용하기 시작했다.
  • GitHub Issues와 Projects로 이슈 트래킹을 시작했다.
  • 개발 프로세스와 브랜칭 모델을 정립했다.


이번엔 버전 관리 시스템과, 이러한 버전 관리 시스템을 사용하는 프로젝트를 지원하는 웹호스팅 서비스를 결정해보도록 하자. 각각 Git, SVN / GitHub, BitBucket같은 것을 예로 들 수 있다. 소스코드 형상관리 시스템이나 소스코드 관리 시스템 같은 명칭으로도 종종 불리는데, 나는 그냥 버전 관리 시스템이라고 부르는 게 더 편한 것 같다.

버전 관리 시스템의 도입 이유

아무튼 이러한 버전 관리 시스템을 도입하려는 이유는 다음과 같다.

  • 백엔드 어플리케이션을 지금 당장은 혼자 개발하겠지만, 미래에 새로운 백엔드 엔지니어가 우리 팀에 들어와서 협업하게 될 수도 있다. 시스템의 도움을 받아 협업을 원활히 하는 것이 첫 번째 이유다. 소스 코드를 USB나 메일로 주고받는 건 엄청난 낭비니 말이다.
  • 변경을 쉽게 되돌릴 수 있다. 에디터 툴들이 일반적으로 지원하는 Ctrl+Z 커맨드와는 차원이 다른 수준의 기록 관리를 지원한다. 소스코드를 과거의 특정 시점으로 되돌리거나, 특정 시점의 변경 사항을 취소하거나, 두 버전의 소스 코드를 비교하는 등의 일이 가능하다.

잘 모르겠다면 생활코딩 git 강의의 '버전관리란?' 챕터 영상를 살펴보자.

버전 관리 웹호스팅 서비스의 도입 이유

  • 협업하고 있는 코드를 저장할 서버가 필요하기 때문이다. 이게 없으면 작업을 서로 공유하기 어려우며 사실 이 이유 하나만으로도 도입 동기가 충분하다. 코드를 USB나 메일로 주고받을 수도 있겠지만, 인생을 그렇게 낭비하지 말자. 버전 관리 서버는 직접 운영할 수도 있으나 에어비앤비, 슬랙, 페이팔같은 큰 조직도 GitHub을 쓰는 마당에 특별한 이유가 없다면 이미 잘 만들어져 있는 서비스를 쓰도록 하자.
  • 버전 관리 시스템을 지원하는 웹호스팅 서비스의 hook 기능을 통해, push나 이슈 추가, pull request같은 이벤트에 반응하여 자동으로 무언가(테스트 실행, 배포 등)가 실행되게 만들 수 있다. 예를 들면, master 브랜치로 push가 이루어졌을 때 자동으로 테스트를 돌리고, 문제가 없다면 배포하는 식이다.
  • 이러한 서비스들은 'pull request'라는 기능을 지원한다. 작업물의 병합을 요청하는 것이다. 여기서 리뷰를 진행하거나, 커뮤니케이션할 수 있다. 개발 프로세스 정립의 자유도가 높아진다.

의사결정 - 버전 관리 시스템

사실 요즘은 게임 업계가 아닌 이상 무조건 git을 쓰고있는 것 같지만, 이것도 의사결정에 포함시켜 보자.

배경과 요구사항

  • 대부분의 개발자가 이미 익숙해져 있는 도구일 수록 좋다.
  • 브랜치 개념이 있어야 한다. 배포용 코드/작업용 코드 등으로 나누어 프로젝트를 원활히 진행하기 위해 꼭 필요하다.
  • 분산형 모델을 사용하는 것을 우선적으로 선택하겠다. 중앙집중식/분산형 버전 관리 시스템의 차이를 모르겠다면 Git SCM의 문서 버전 관리란?을 읽어보자.

선택지

  • git
  • svn
  • mercurial

의사결정

나라면 git을 선택할 것 같다. 그 이유는,

  • SVN클라이언트-서버 모델을 사용한다. 이 때문에 저장소의 사본을 로컬에서 관리하는 git에 비해 매우 느리며(변경 로그 하나 보는 것도 인터넷을 경유해야 하므로) commit이 원격에 즉시 반영되기 때문에 부담이 크다. 게다가 SVN은 변화를 저장하는 방식으로 버전(변경 이력)을 관리하는데, 이것도 git과 비교했을 때 속도 문제를 일으킨다. git은 버전을 스냅샷으로 관리하기 때문이다.
  • 게다가 SVN에는 시스템적으로 브랜치 개념이 없어서, 작업마다 폴더를 만드는 식으로 우회하곤 한다. 스냅샷 기능이 없기 때문.
  • MercurialGit분산 버전 관리 시스템이라는 점에서 꽤 유사하지만, 대부분의 개발자는 Git에 더 익숙하다. 우리 조직에 누군가가 들어왔을 때 불필요한 러닝커브가 생기지 않았으면 하는 바람이고, Git과 비교했을 때 Mercurial이 큰 메리트가 있는 것도 아니라는 판단이다.

준비

Git은 MacOS를 비롯한 대부분의 리눅스 환경에서 기본으로 제공한다. Windows를 사용하는 경우에만 Git for Windows를 설치하면 된다.

의사결정 - Git 웹호스팅 서비스

Git이 참조할 원격 저장소를 관리해줄 장소가 필요하다.

배경과 요구사항

  • 이것도 대부분의 개발자가 이미 익숙해져 있는 도구일 수록 좋다.
  • private 저장소를 무료로 만들 수 있게 해주는 서비스를 우선적으로 검토할 것이다. 나중에 조직이 커지면 돈을 쓰는 게 맞겠지만, 지금은 과금에 대한 부담이 적을수록 좋을 것이니 말이다.
  • 자체적으로 가지고 있는 이슈 트래커(작업 관리 도구)가 있으면 좋다.

선택지

의사결정

GitHub를 선택하겠다. 그 이유는,

  • BitBucket서버가 느리다. 이게 딱히 문제가 안 될 줄 알았는데 생각보다 굉장히 답답하며 자체적으로 제공하는 기능도 GitHub만큼 풍부하지 않다.
  • GitLab은 써본 사람이 얼마 없다. 이것도 딱히 문제가 안 될 줄 알았는데 예전에 GitLab 한 번 썼다가 어떤 기능이 어디에 있는 줄 몰라서 답답해 죽는줄 알았다. Mercurial을 쓰지 않았던 이유와 동일하게, 큰 메리트가 없으면서 굳이 익숙하지 않은 걸 썼다가 괜히 생산성만 조지는 수가 있다.
  • GitHub에 내장되어 있는 이슈 트래커(GitHub Issues)Projects 기능이나 ZenHub 플러그인과 함께 쓰면 생각보다 쓸만 하다. 조직이 웬만큼 커지기 전에는 이슈 트래커로 따로 돈 들어갈 일이 없다. 이슈 각각에 due를 설정하는 게 없는 것 빼고 꽤 만족스럽다.
  • GitHub은 free로 쓰던 돈을 내고 쓰던 거의 모든 면에 있어서 가장 풍부한 서비스라고 생각한다. 별의 별 기능이 다 있는데 그게 다 꼭 한 번씩은 필요한 기능들이었으며 기능 자체도 매우 잘 만들어져 있었다. 특정 브랜치를 향한 pull request에 대해 n개 이상의 리뷰를 받아야 merge할 수 있게 만드는 것이나, Organization에서 팀을 만들어 사용자를 포함시키고, 팀 단위로 저장소 각각에 read/write/admin 권한을 부여하고, 서브 팀을 만들어 더 세부적인 위계를 나누는 등, 팀 관리 면에서도 매우 뛰어났던 것 같다.
  • 서비스 개발을 진행하는, 특히 백엔드 저장소를 public으로 두면 다른 의미로 많은 관심을 끌 수 있으므로 private로 두는 것이 좋다. 이 때문에 한가지 걸렸던 점은, GitHub에서 free 계정은 visibility를 public으로만 설정 가능하다는 것이었다. GitLab과 BitBucket이 무료 계정에게도 private 저장소를 제공하는 것과 대비된다. 이를 위해선 개인이 한 달에 7달러씩 지불하는 developer plan에 가입하거나, 팀 단위로 organization을 만들어서 유료 플랜을 구입해야만 했는데, 최근에 GitHub가 무료 플랜에게도 private 저장소를 만들 수 있도록 과금 정책을 변경하며 이러한 고민이 당장은 사라졌다. 물론 나중에 조직이 커져서 organization 단위로 프로젝트를 진행하게 되면 돈을 써야겠지만, 이건 다른 서비스들에게도 똑같이 적용되는 이야기다. 따라서 돈 때문에 GitHub를 포기할 이유가 없다. 필자는 독자 여러분의 코드 참조를 위해 public으로 두겠다.

준비

GitHub에 계정이 없다면 만들고, 원격 저장소 하나를 만들어 두자. 원래는 팀 단위로 organization을 만들어서 저장소를 만드는 것이 일반적이지만, 같이 프로젝트를 하는 친구들은 사실 가상인물이므로 개인 계정에서 진행하자. GitHub Help의 Create a repo를 참고하면 될 것 같다. 위에서도 말했듯 필자는 독자 여러분의 코드 참조를 위해 저장소를 public으로 만들텐데, 실제로 프로젝트를 하는 중이라면 private로 만드는 것이 맞다.

의사결정 - Git GUI

참고로 이건 조직 내가 아니라 개인적으로 결정해야 할 일이다. CLI를 쓰겠다면 존중하겠지만 나는 그 많은 git 명령어를 자유자재로 외울 자신이 없기에 GUI를 사용하겠다. git 명령어가 익숙치 않은 독자가 git을 조금 더 쉽게 시작할 수 있게 하기 위한 목적도 있다.

배경과 요구사항

  • GUI 단에서 Git이 가지고 있는 대부분의 기능을 모두 사용할 수 있어야 한다.
  • UX가 후지지 않아야 한다.
  • GUI 툴 쓰다가 답답해서 콘솔 여는 경우가 없어야 한다.

선택지

의사결정

조직 단위의 결정이 아니기에 매우 개인적인 관점에서 GitKraken를 선택하겠다. 그 이유는,

  • GitHub Desktop은 가볍고 깔끔하지만 Git의 모든 기능을 지원하지 않는다. 예전에 신규 기능을 개발하다가 버그 핫픽스 작업이 갑작스레 생겨서 git stash 명령을 찾아보려 했는데, GitHub Desktop은 이걸 지원하지 않아서 CLI의 도움을 받았던 적이 있다.
  • 일할 땐 웬만하면 SourceTree를 쓰고, 나름 만족스럽지만 이번에 GitKraken을 찾아보니 UI도 예쁘고 좋아 보였다. 맨날 쓰던 것만 쓰면 꼰대가 되지 않을까 싶어서 이번에 GitKraken을 새로 써보려고 한다. GUI 툴은 쉽게 바꿀 수 있으니 써 보다가 아니다 싶으면 SourceTree로 바꾸려는 생각이다.

준비

다운로드를 받아 설치해 두자. 그리고 처음 실행하면 'Sign in with GitHub' 버튼을 통해 본인의 GitHub 계정을 연동할 수 있다. 물론 내가 GitKraken을 쓰기로 결정했다고 해서 꼭 이걸 써야 되는 건 아니다. 말했듯이 조직 단위의 결정이 아니기 때문이다.

그리고 Git GUI를 쓰던 bash를 쓰던 접근하기 편한 장소에 위에서 만들어 뒀던 레포를 clone해 두자. 나도 내 개인 계정에 Sampleapp-for-blog라는 이름의 저장소를 만들고, GitKraken을 통해 clone받았다.

지금까지

  • 버전 관리 시스템으로 Git을 사용하기 시작했다.
  • Git 웹호스팅 서비스로 GitHub를 사용하기 시작했다.


필자는 고등학교 1학년 말에 처음으로 백엔드 포지션에서 프로젝트를 진행했다. 내 블로그에서 자주 우려먹는 교내 공식 기숙사 관리 시스템인데(지금 파업 상태다 ㅡㅡ), 같은 동아리였던 선배와 함께 Vert.x라는 서버 프레임워크가 지원하는 Vert.x-Web 툴킷으로 Java 8 런타임에서 돌아가는 웹 어플리케이션 서버를 개발했었다. 지금 생각해보면 참 많은 문제가 있었다.

배포 방식

첫 번째는 배포 방식이다. 테스트 코드가 없는 건 둘째 치더라도, 신규 기능 개발이나 버그 해결을 하고 배포하기까지 사람 손이 정말 많이 들어갔다.

  1. Maven으로 WAS를 .jar 형태로 빌드한다.
  2. WinSCP라는 FTP 클라이언트로 서버에 원격 접속한다.
  3. jar 파일을 덮어쓴다.
  4. Putty라는 SSH 클라이언트로 서버에 원격 접속한다.
  5. screen으로 돌아가고 있는 WAS 세션에 들어가서 서버를 껐다 켠다.

다운타임 문제도 있고, 자동화할 수 있는 부분이었지만 자동화할 생각을 하지 못했다.

물리 서버가 한 대

리눅스에서 물리적인 터미널을 가상 터미널 여러개로 다중화해주는 screen이라는 도구를 써서, screen 하나는 서버 돌리고, 하나는 DB서버 돌리는 식으로 운영했다. 이게 사실 비용적인 문제 때문에 어쩔 수 없긴 했지만, 물리 서버가 한 대인 게 문제임을 인지하지 못했던 것이 더 컸다. '서버가 한 대인데 그게 고장나면?'이라고 누군가 질문하면, '아 그럼 고쳐야죠'라고 대답했을 것 같다. API 서버와 DB 서버를 분리해야 하는 필요성을 느낄만한 경험도 없었고, 아직 혼자 뭔가를 알아보기 어려운 내게 누군가가 가르쳐주지도 않았으니 말이다.

테스트 코드 없음

테스트를 코드가 아니라 PostMan으로 했다. happy path(가장 이상적인 로직)에 대해서 요청 몇 번 해보고, 잘 되면 배포했다. 이런 습관 때문에 문제가 아주 많았는데, 한가지 예를 들면 사용자가 2015년 이전의(파싱된 데이터가 없는 날짜의) 급식 정보를 로드하려 할 때 서버가 500을 내려줬던 적이 있었다. 테스트 코드가 없는 서버를 운영하는 것에 대해서는 말이 더 필요할까 싶다.

브랜칭 모델, 리뷰 없는 배포

먼저, 기능 개발이나 이슈 대응에 대한 별도의 티켓팅 과정이 없었다. 같이 서버를 개발하던 선배와 메신저로 얘기하면서 작업을 분배했고, 나는 할당받은 작업들을 wunderlist라는 to-do list 앱에 적어두고 하나씩 진행했다. 작업은 master 브랜치에서 진행했고, 배포는 리뷰 없이 각자 알아서 진행했다. 아래는 2017년 11월에 적어뒀던 작업 목록이다.

까먹어서 처리하지 못한 이슈가 생길 때도 있고, 우선순위 조절도 매우 힘들었다.

로그 시스템 없음

Vert.x라는 프레임워크에는 미들웨어 개념이 있음에도 불구하고 제대로 된 로그 시스템을 구축하지 않았다. '제대로 된 로그 시스템'이라고 한 이유는, 파일 형태로 요청 로그를 남기기는 했기 때문이다. 아무튼 '로그를 남겨서 뭐하나' 싶었다. 로그 시스템이 없다는 건, 서버에 에러가 나고 있어도 인지하지 못한다는 것이다. 따라서 버그는 '보고 받는' 식으로 발견했다. 사용자가 원하기 전에 먼저 대응해두는 것이 더 좋은 흐름인데 말이다.

raw string 형태의 쿼리

코드를 보면 이해가 될 거다. ORM이 논쟁이 많은 기술이고 뭐고 다 떠나서 ORM을 적용했더라면 저 때 조금은 덜 열불나지 않았을까 싶다. 물론 저 때엔 문자열 결합으로 쿼리 문자열을 만들어내고 나서 '와 진짜 잘만들었다'라고 생각했다.

그 외에도 API 문서가 엑셀로 정리되어 있다거나, 서버가 살아있는지 체크하는 health check가 없다거나, 데이터베이스 백업 안해 뒀다가 쿼리 잘못 날려서 계정 정보 다 날아가거나 하는 것도 있었다. 지금 보면 왜 저랬나 싶은데, 당시엔 그게 당연한 건줄 알았다.

왜?

백엔드를 조금 경험해본 사람이라면 사용자를 어떻게 인증할지, 암호화는 어떻게 할지, API 문서는 어떻게 작성해야할 지 같은 걸 비교적 쉽게 의사결정할 수 있지만, 그렇지 않은 사람은 이런 게 필요하다는 것조차 깨달을 수 있는 경로가 별로 없다.

  1. 어플리케이션 레벨의 경우, 언어와 프레임워크의 선택지가 너무 다양해서 예제를 찾기 힘들다. 안드로이드는 Java나 Kotlin, iOS는 Objective-C나 Swift, 웹 프론트엔드는 React, Vue처럼 그래도 손에 꼽는 수준인데, 백엔드는 그렇지 않다. Java+Vert.x 예제는 정말 찾기 어려웠다. 요즘 갖고 노는 Python+Sanic도 그렇고.
  2. 당연하겠지만, 애초에 production level의 코드를 뜯어볼만한 기회가 별로 없다. 웹이라면 개발자 도구를 켜고, 앱은 종종 GitHub에 올라오는 코드로 얼추 알아볼 수 있을텐데 말이다.
  3. 백엔드는 코드만 봐서 되는 포지션이 아니다. 배포와 배포 패턴, 모니터링, 로깅, 스토리지, 트래픽 변동의 대응처럼 코드 외적으로 신경써야 할 부분이 많다.
  4. 어느 기술이던 경험해보지 않으면 필요성을 못 느끼는 것이 맞다. 그러나 백엔드는 그런 경험조차 해보기 어렵다. 예를 들어, 변동이 적은 데이터는 캐싱해 두면 좋지만, 굳이 캐싱해두지 않아도 데이터베이스에 쿼리하는 시간이 워낙 짧으니 필요성을 못 느낀다. 학교에서 조별 과제로 하는 프로젝트는 사용자가 매우 적거나 없으니 말이다. 로깅도 그렇고, 백업도 그렇고, 데이터베이스 서버 분리도 그렇다. 동기부여의 기회가 별로 없다.

이런 배경에서 어째저째 백엔드 엔지니어라는 이름을 단 채 일하기 시작하고 나니, 이런 걸 도와줄 수 있는 컨텐츠를 만들어보고 싶었다. 필자의 경우엔 뭔가 하나 깨우치는 걸 혼자 하려면 엄청 어려웠는데, 누가 옆에서 5분이라도 설명해 주면 꽤 쉬웠던 것 같아서, 그런 설명충 컨텐츠를 기획했다. 고등학교 곧 졸업하는데 백엔드 좋아하는 후배들한테 뭐 하나 남겨주고 싶기도 했고, 맨날 SQL이나 파이썬 얘기만 하다가 좀 색다른 컨텐츠를 진행해보고 싶기도 했다. 

배포 전에 테스트를 돌리고, 배포를 자동화하고, 로그를 남기고, 시각화하고, 로그 파이프라인 중간에 버퍼나 proxy를 끼워넣는다거나 하는 게 누군가에겐 당연한 일일 수도 있겠지만, 그렇지 않은 사람들이 많다. 그래도 배경지식이 좀 생기면 혼자 찾아볼 수 있는 능력이라도 생기니, 같이 프로젝트 하나 해본다고 생각하면 될 것 같다. 컨텐츠 제목이 좀 싸가지 없어 보일 수도 있는데, 그냥 저게 평소 필자의 말투라서 저렇게 지은거고 딱히 뭐 의미를 부여하고자 한 건 아니다.

이 컨텐츠의 룰

'백엔드가 이정도는 해줘야 함'이라는 컨텐츠는,

  1. 친구 서너명과 '게시판 기반의 커뮤니티 포털' 프로젝트를 진행해서 서비스화 시킨다고 가정한다. 이 배경을 기반으로, 백엔드의 관점에서 기술스택 의사결정부터 시작해 '백엔드가 이정도는 해줘야하지 않나'싶을 때까지 이것저것 붙여볼 것이다.
  2. 의사결정은 '다 ~로 하니까'가 아니라 상황에 맞춰 유동적으로 해볼 것이다. 트렌드를 따르거나, 추측을 기반으로 한 기술 선택은 멍청한 것 같다는 생각이다. '로그 분석에 ELK가 일반적이어서'같이 '당연히 이래야지'같은 이유로 기술을 도입하는 것은 본인에게나 조직에게나 좋지 않은 것 같다. 컨텐츠를 진행하며 계속해서 '내 결정이 옳다는 근거는 무엇인가?'라는 고민을 글로 옮길 것이다.
  3. 프로젝트에서 이런저런 외부 도구들을 쓰기 때문에, 백엔드로 프로젝트에 두세번 정도 참여해 본 사람 정도의 수준에 맞춰서 진행한다. 컨텐츠의 후반으로 갈 수록 이런 경향이 심화될 것이다. git, SQL, HTTP 등을 경험해본 적 없다면 초반 몇 챕터를 읽는 것도 어려울 수 있다. A to Z 모두 설명해보곤 싶지만, 필자의 역량이 부족하기도 하고, 그렇게 들어가기 시작하면 끝이 없기도 하니 이건 각자가 여기저기서 알아보며 공부하도록 하자. 주는대로 받아먹기만 잘하는 사람에겐 어울리지 않는 내용일 것이다.
  4. 조직 내 문서화 툴, 메신저와 같이 '프로젝트 팀 전체'에 해당하는 의사결정은 하지 않을 것이다.
  5. 의사결정의 흐름을 부드럽게 하고, 설명을 더 알차게 하기 위해 되도록 필자가 가장 잘 쓰는 기술을 선택할 것이다. 의사결정에서 '조직에게 가장 익숙하다'라는 이유를 붙이는 경우가 그런 예다. 물론 정말로 '이 기술이 조직에게 익숙한가'는 의사결정의 기준이 되어야 하는 것이 맞지만, 이 컨텐츠에 한해서는 '익숙함'이라는 기준이 의사결정의 우선순위가 조금 더 높을 뿐이라는 것이다.
  6. 모든 의사결정은 배경과 요구사항-선택지-의사결정 순서의 소분류로 나누어 정리할 것이다.

컨텐츠를 생성하는 필자의 배경과 관점 안에서 의사결정이나 코딩을 하게 될테니, 주관적인 부분이 조금 있을 것이다. 그러니 '얘는 프로젝트에서 백엔드를 이렇게 하는구나' 하며 이해해도 될 것 같다. 무작정 싹 다 설명하면서 도와주기보단 자립을 돕는 식으로 글을 쓰도록 노력해 보겠다. 모바일 기준 7~8화면 정도 분량으로 4~50편 정도에 걸쳐 진행될 예정이며 적어도 2019년 말까진 글이 활발히 올라올 것 같다.

컨텐츠를 진행하며 의사결정이 필요할 때마다 '이 방법은 이래서 구리고, 저 방법은 이래서 선택하지 않겠다'같은 이야기를 많이 하게 될텐데, 필자가 '구리다'라고 하는 걸 본인이 썼거나 쓰고 있다고 해서 부끄러워할 필요는 없다. 다 필자의 주관적인 생각이고 '더 나은 방법'은 각자가 고민해야 하는 일이니까, '더 나은 방법이 없을까?'하는 고민을 멈추지만 말자. 그리고 가끔 가다가 예의없이 대충 설명하고 넘어가는 부분이 분명 있을테니, 맘에 안 드는 부분이 있다면 어떤 경로로든 얘기해주기 바란다. 더 좋은 글을 작성하는 데엔 독자의 피드백만한 게 없다.

+ Recent posts