T

TooLate

개발, 스타트업, 프로덕트에 대해 고민하는 블로그입니다.

팀장의 역할에 대한 단상 (2)

팀의 실행 방안 수행

업무 수행 중에 최초 계획했던 대로 일이 진행되지 않는 것은 10개의 일 중에 9개정도는 해당될 만큼 잦은 일입니다. 계획을 가지고 움직일 때 우리는 보통 3가지 선택사항에 놓이게 되는데 이중 올바른 답을 찾기는 쉬운일이 아닙니다.

1-1. 해당 이슈를 예외로 두고, 이번만 별개로 진행
1-2. 해당 이슈를 포함하는 계획을 재수립

  1. 포기

포기는 많은 경우에 선택하는 그러나 정답일수도 오답일수도 있는 선택지이기에 논외로 두고자 합니다. 반면 1번에 위치한 2개의 답 사이에서 우리는 많은 고민을 합니다. 이번 이슈가 진행과정에서 또 발생할지, 또 발생한다고 해도, 별개로 진행할 수 있을지, 그 횟수는 얼마나 될지 등 고민해야 할 조건이 많습니다.

이러한 경우가 결정하기 가장 난감한 편입니다. 경우에 따라서는 팀 전체가 이번 이슈에 허덕일 수도 있습니다. 계획을 변경하거나 다시 수립할 경우, 우리는 일정과 리소스의 벽에 부딪히게 됩니다. 최초 계획시점과 현재는 다르고, 지금까지 했던 결과들이 유의미하지 않을 수 있습니다.

대부분의 경우 포기보다는 1번의 두가지 방향에서 선택합니다. (가끔 포기를 선택하는 팀장에게서 뿜어져 나오는 쿨내를 감당하지 못하기도 합니다.) 1-1과 1-2의 사이에서 팀장은 아래와 같은 상황들을 검토해야 합니다.

  1. 앞으로 동일한 이슈가 발생할 확률
  2. 현재 있는 계획안에서 해당 이슈를 포함할만한 대안이 있는지
  3. 계획 변경 시 고려되어야 할 현재까지의 투입 리소스 / 앞으로의 리소스
  4. 지금까지의 결과와 상태
  5. 이외에 또다른 이슈가 발생할 여지가 있는 취약한 부분
  6. 해당 이슈를 별개로 진행할 경우 리스크

이외에도 많은 사항들이 있겠지만 결국 이전과 같은 뻔한 이야기입니다.
정답이 없는 문제에 대해서 최종 결정에 대한 책임은 팀장이 지어야합니다.

실행 결과 확인

중간 중간 발생하는 이슈들을 넘어 최종적인 결과를 얻게 되었지만, 이 결과가 원하는 결과일 수도, 아닐수도 있습니다. 가시적인 성과가 나왔다 -> 보고한다 -> 행복하다 의 프로세스가 이루어지는 경우는 흔치 않습니다. 애매한 결과가 나왔다 -> 포장한다 -> 보고한다 -> 씁쓸하다 의 프로세스가 보통 친숙한 방식입니다.

결과가 나왔을 때 팀을 어느 방향으로 이끄느냐는 최종적으로 팀장이 수행해야할 가장 큰 결정입니다. 다른 결과를 얻기위해 새로운 프로젝트를 수행할지, 기존 결과를 가공하여 유의미한 다른 가치를 찾을지, 있는 그대로 보고할지 등 여러 방식이 또다시 산재합니다.

보통 이러한 상황은 팀원들의 의견을 얻기가 쉽지 않습니다. 보통 팀장의 업무 범위에 따라 진행되는 프로젝트인만큼 정해진 일정이 모두 지났거나, 혹은 애초에 일정이 없었거나, 유의미한 다른 결과값이 회사에 도움이 되거나, 타팀에서 감당할수 없는 결과이거나 등 여러 경우의 수를 알지 못하는 팀원이 의견을 주기는 쉽지 않습니다.

간단히 상상해보면,

마케팅팀에서 준비한 광고일정이 도래했을 때, 생산팀에서 목표 물량을 뽑아내지 못하는 경우
대규모 업데이트 시점을 고지하였으나, QA 결과 개발 결과물이 MVP 수준인 경우
500만건의 생산 물량을 확보하였으나, 100만건밖에 수주해오지 못한 경우

대부분 크리티컬 하거나, 상상하기도 싫은 경우가 많습니다.

결과가 나왔을 때 단순히 상사에게 까이거나, 회사에서 욕하는 상황을 걱정하는 것은 당연히 있을 수 있는 부분이고, 좋은 팀장에게는 더 나아가 이를 극복하기 위한 결정이 필요합니다. 애초에 결과가 목표치를 상회할 경우에는 팀 전체가 좋은 결과를 만들어 낸 부분이니 중대한 결정을 할 필요는 없습니다. 항상 문제가 되는 것은 결과가 나쁜 상황입니다.

예를 들어, 업데이트 시점을 고지한 후, QA 결과가 차마 업데이트 하기에 비참한 정도의 결과물이라고 가정해봅니다. 아래와 같은 고민들을 해볼 것 같습니다.

  1. 업데이트 시점을 조정하기 위해, 문제 상황들을 개선하는데 필요한 소요시간을 산정하고, 여유 일정을 감안하여 n일을 연기 요청한다.
  2. 현재 준비된 상태로 업데이트를 수행하고, 순차적 개선한다.
  3. 문제 상황들 중 야근 등 조금이라도 개선 가능한 부분들을 반영하여 약간이라도 퀄리티를 높인다.

마찬가지로 정답은 없고, 회사 상황과 타 팀과의 연계를 통해 최종 결정이 이루어집니다. 어떠한 결정을 하던 문책을 피할 수 없겠지만 회사의 분위기나 방향에 따라 앞으로 어떻게 할지는 팀장이 정해주어야 합니다.

그래서 결론은?

팀장은 어떠한 일 하나를 수행할 때 그 일의 경중과는 상관없이 시작부터 끝까지 결정하는 자리에 있는 사람입니다. 그 결정이 회사에 도움이 되는 방향으로 간다면 좋은 팀장이고, 팀원에게 도움이되는 방향으로 간다면 좋은 팀장이다 라는 이분법이 아닌, 매 순간 최선의 결정을 하는 팀장이 필요한 시기입니다.

때로는 팀원들을 다독여 일정을 맞추고, 때로는 회사에게 강하게 어필하여 일정을 확보하는 여러가지 결정을 그 상황에 맞추어 결정해야 합니다. 그러기 위해 팀장은 그팀의 메인 업무에 대하여 스페셜리스트여야 하면서, 동시에 회사 전체의 제너럴리스트여야 합니다.

타팀의 일정을 미뤄서라도 우리팀의 일정을 맞춰야 한다는 근거를 낼수 있거나, 반대로 우리팀의 일정을 앞당길수 있는지 판단할수 있어야 합니다.

경우에 따라서는 팀원들의 문제 상황들을 대신 해결해주어야 하고, 반대로 자신의 업무를 과감하게 팀원에게 넘겨줄 수 있어야 합니다.

결국 모든 것은 팀장의 결정에 의해 움직입니다. 회사와 팀 사이에서 최대한의 효과와 효율을 만들어 낼수 있는 신의 한수를 두는 사람이, 더욱 필요해지는 시기입니다.

A와 B가 있을 때 C를 이야기하는 사람이 팀장일 필요는 없습니다.
하지만, 팀장은 그 셋 중 한가지를 결정하는 사람이어야 한다고 생각됩니다.
과거와 미래, 현재를 감안하여 결정할수 있는 능력은 쉬운 일이 아닙니다.
좋은 개발자가 팀장이 되어도 좋은 팀장이 될수 없다는 것은 이러한 점이 영향을 주지 않을까 싶습니다.

팀장의 역할에 대한 단상 (1)

팀원과 팀장이 되려면 무엇을 잘해야할까 라는 이야기를 나눈적이 있습니다. 팀장은 무슨 일을 하는 사람일까? 라는 질문은 언제든 답을 내리기 쉽지 않은 질문입니다. 아무리 오래 팀장을 하더라도, 좋은 팀장이 아닐 수 있고, 처음 팀장을 맡은 사람이어도 좋은 팀장일 수 있습니다.

처음 스타트업에서 팀장으로써의 역할을 수행하기 시작했을 때의 그 막막함은 혼자뿐이라는 상황속에서 더욱 무겁게 다가왔었습니다. 조금이나마 과거를 돌아보며 생각을 적어봅니다.

좋은 팀장의 기준?

팀원의 관점에서 좋은 팀장은 그 기준이 불명확하고, 성향이나 성품으로 좋은 팀장을 구분 짓기에도 잣대를 세우기 애매합니다. 그래서 이번에는 좋은 팀장에 대해서 조직 구조와 회사의 관점에서 생각해보고자 합니다. 회사와 전체 조직의 성장을 위해 팀장이 어떤 롤을 수행할 수 있을지. 그리고 그 롤을 원활하게 수행할 경우와 그렇지 못할 경우 어떤 결과들이 일어나는지 등을 상상해보았습니다. 물론, 좋은 팀장이 회사와 조직 구조만의 관점으로 볼 경우, 팀원들에게 있어서는 최악의 팀장일 수 있습니다. 다만, 이 관점으로 보아야만, 팀원이 팀장이 되기 위해 어떠한 역할 능력을 갖춰야 하는지는 알기 명확해 집니다. 회사는 회사의 성장에 보탬이 될 수 있는 팀장을 선호하는 것이 당연하기 때문입니다.

회사와 조직 구조의 성장에 보탬이 되는 팀장이라는 관점에서 하나의 조건을 넣어보았습니다. 스타트업과 대기업 모두에게 동일한 관점일까? 에 대한 의문에서 나오는 조건입니다. 우선은 그 기준을 스타트업에서 잡아보고, 이 기준이 대기업에도 도움 될지 생각해보았습니다.

처음으로 돌아가 회사가 성장하고 조직 구조가 원활히 운영되기 위해서는 각각의 조직이 맡은 바 업무를 잘 수행했느냐에 달려있다고 생각됩니다. 맡은 일이 잘 수행되었다는 이야기는 회사의 성장 목표에 도움되는 결과를 만들어 냈는지 또는 회사가 원하는 결과물이 만들어졌는지로 주로 판단할 수 있습니다. 이 외에도, 목표한 수치를 달성했다거나, 전달된 지시사항을 모두 수행했는지 일 수 있습니다. 즉, 회사가 원하는 목표에 부합하는 결과물을 그 조직이 만들어 냈는가로 정의해볼 수 있습니다. 상위 조직에서 내려온 목표가 올바른지, 잘못되었는지에 대한 가정을 떠나, 그 조직이 원하는 목표를 달성했다면 회사 차원에서의 목표에 도움이 되며, 조직 구조내에서는 신뢰 관계가 생기게 됩니다. 이를 바탕으로 좋은 팀장의 기준을 회사가 원하는 목표에 부합하는 결과물을 내도록 이끄는 사람으로 잡을 수 있겠습니다.

어떻게 목표를 달성하나

위의 생각들을 바탕으로 팀장이 조직에게 방향성을 제시하고 좋은 결과물이 나올 수 있도록 하는 것은 가장 기본적인 기준이라고 볼 수 있습니다. 이에 따라 일을 하는 순서 별로 한번씩 고민해보았습니다.

  1. 팀의 방향 설정
  2. 팀의 실행 방안 수립
  3. 실행 방안의 수행
  4. 실행 결과 확인
  5. 유지

팀의 방향 설정

상위 조직에서 내려오던 회사 차원에서 내려오던 달성해야 하는 목표가 존재합니다. 매출이거나, 또는 수익이거나 여러가지가 있을 수 있지만, 세부 팀으로 내려올 경우, 그 목표를 달성하기 위해 우리팀이 할 수 있는 일을 고민하기 시작합니다. 그 과정에서 첫번째로, 우리가 어떤 것을 개선/수행 하여 목표 달성에 도움되도록 할지 그 방향을 잡습니다. 별도의 회사 목표가 없다면, 팀은 내부에서 발생하는 문제들 중 한가지를 개선하기도 합니다. 정말 마이크로팀이 아니라면 대부분의 팀은 회사의 KPI 를 달성하기 위해 어떠한 방향을 설정해야 하는지 결정하기 쉽지 않습니다. 아래 예를 보겠습니다.

회사의 목표는 매출 20% 증가입니다.
영업팀은 이를 위해 여러가지 방향을 세울 수 있지만, 그중 2가지를 예로 들어 보았습니다.

  1. 영업 팀원들의 업무 효율을 개선하여, 한번에 더 많은 일을 할 수 있게 하자.
  2. 영업 채널을 확대하여 더 많은 고객에게 우리를 알리자.

1번의 방향은 업무 효율 개선이 영업팀의 방향이 되겠고, 2번의 방향은 영업 채널의 확대가 방향이 되겠습니다. 회사 입장에서는 어떤 방향으로 영업팀이 수행하던 20% 이상의 매출 증가가 이루어지면 원하는 목표 달성에 문제가 없습니다.

이 상황에서 좋은 팀장은 어떤 방향을 선택해야 할까요. 저 2가지 방향 말고도 좋은 팀원들이 다양한 방향을 제시했고, 5가지가 넘는 좋은 방향이 주어진다면 어떤 방향을 선택할까요. 여기서 팀장의 능력 수행이 필요하다고 판단되었습니다. 팀장은 여기서 결정을 해야 합니다. 회사에서 달성해야할 수치를 제공했고, 그 수치 달성을 하기 위한 첫걸음에서 결정을 해야합니다. 이 방향이 올바른지 아닌지에 대한 고민과 검증은 팀원과 함께하던, 스스로 하던 결국 상위 조직이나 회사에게 결정된 팀의 방향을 말해야 합니다. 그 방향이 왜 회사의 목표 달성에 도움이 되는지 설명해야 합니다.

물론 팀원들과 함께 방향 검증을 할 수 있습니다. 방향 결정을 유보하고 1개씩 검증해볼 수도 있습니다. 이 과정도 사실은 결정입니다. 팀의 방향으로 결정하기에 모두 애매하니, 1개씩 검증해보자라는 결정을 내린 상황입니다. 그렇게 검증을 수행하여, 나온 1개의 방향이 팀의 방향이 되어도 좋다라는 최종 결정은 결국 다시 팀장에게 돌아옵니다.

팀의 실행 방안 수립

방향이 결정되어 상위 조직과 회사에게도 공유가 되었습니다. 이제 팀은 그 방향으로 나아가기 위해 실행 방안을 수립합니다. 또한, 그 방향으로 가는 것에 있어서 걸림돌이 되는 것들을 찾기 시작합니다. 마찬가지로 예를 들어보겠습니다. 업무 효율 개선이 팀의 방향이 되었습니다. 이를 위해 팀원들과 함께 지금 불편한 것들을 모아봅니다.

고객사의 위치가 다양해 이동에 소요되는 시간이 너무 길다.
회의 시간이 너무 길어 고객사 전화를 놓치는 경우가 많다.
고객사와의 상담 내역을 관리하기 불편하다.

다양한 이야기가 나올 수 있습니다. 여기서 더 나아가 있으면 업무 효율에 좋은 것들을 찾아봅니다.

검토중인 고객사에게 보내줄 회사 소개서가 있으면 좋겠다.
미팅 중일 때 전화를 받아 줄 내부 인력이 있으면 좋겠다.
다른 팀원이 영업 시도한 고객사에 2달 내에 다시 시도하지 않도록 관리 방안이 있으면 좋겠다.

수많은 개선 사항들을 팀원들이 제시합니다. 그동안 불편했던 것들, 번거로웠던 사항들이 쏟아져 나옵니다. 타 팀에게 도움을 요청해야 하거나, 예산이 투입되어야 하는 것 등 다양한 방안들이 논의됩니다. 또다시 가장 효과가 좋은 실행방안을 결정해야 할 상황에 놓입니다. 모든 실행 방안을 수행할 수 있다면 좋겠지만, 팀 안에서 이를 모두 진행할 수는 없습니다. 상위 조직에 요청하거나, 타 조직에 도움을 요청해야 하는 일들이 생기고, 예산이 추가적으로 필요할 수 있습니다.

실행 방안을 결정하기 위해 여러가지 방법들이 있지만, 결국 최종적인 결정은 팀장이 수행해야 합니다. 그 실행방안에 대한 지원 근거와 지원했을 때 어느 정도의 효과가 발생할지 등 다양한 관점에서 고민하여 결정합니다.

(1/2 끝)

번역글 The Fundamentals of REST API Design

이 글은 계정 관리 API 서비스, Auth 서비스를 제공하는 Stormpath의 블로그 글인, The Fundamentals of REST API Design 문서를 번역한 글입니다.
원문은 여기 에서 확인 가능합니다.

Stormpath에서는 개발자들이 REST API 및 언어 별 SDK를 통해 인증, 권한 부여 및 비밀번호 재설정을 포함한 사용자 관리 기능에 쉽게 액세스 할 수 있게 돕고 있습니다. 이 서비스를 구축할 때 Stormpath 내부 팀은 REST + JSON API 디자인에 대한 기존의 보편적인 정보들을 바탕으로 시작했습니다. 이번에 REST API를 구축하는 과정에서 그들은 더 많은 것을 배웠습니다. 그리고 이 경험을 바탕으로 우리의 CTO 인 Les Hazelwood는 REST + JSON API 디자인 모범 사례에 대해 Java 개발자 그룹에게 프리젠테이션을 진행했습니다. 이 사례는 여기서 볼 수 있습니다.

위 프리젠테이션 외에도 REST API와 관련하여 REST API의 보안 REST API의 링크 및 리소스 확장 에 대한 게시물을 작성했습니다.
이 글에서는 Les가 그의 이야기에서 다루는 주요 요점, 특히 우수한 REST + JSON API 디자인의 기본 사항에 대한 높은 수준의 요약을 제공합니다.

왜 REST가 필요한가요?

API의 빠른 결정이라는 목표를 염두에두고 RESTful API가 매력적인 이유는 무엇일까요? REST 패러다임에 관한 Dr. Roy Fielding의 논문에 따르면, REST에는 6가지 뚜렷한 이점이 있습니다.

  1. 확장성 - 반드시 성능이 보장되는 것은 아니지만, RESTful API가 다른 시스템에 적응하고 확장하여 연결되는 것은 정말 간단하고, 쉽습니다.
  2. HTTP 사용 - HTTP 메소드를 사용하여 자원을 관리하면 RESTful API를 외부의 다른 응용 프로그램에서도 쉽게 연결하고 사용할 수 있습니다.
  3. 독립성 - REST API를 사용하면 전체 애플리케이션 또는 전체 웹 서버를 종료하지 않고도 애플리케이션의 특정 부분을 재배치하거나 축소 할 수 있습니다.
  4. Caching으로 인한 대기 시간 감소 - REST API는 Caching의 우선 순위를 지정 및 활용 가능하기 때문에 대기 시간이 향상됩니다. 따라서 REST API를 개발할 때는 항상 캐싱을 최우선으로 생각해야 합니다.
  5. 보안 - HTTP 통신을 사용하면 특정 HTTP 헤더를 통해 보안을 파악할 수 있으므로 이를 활용하여 API를 안전하게 할 수 있습니다.
  6. Encapsulation - REST를 사용하면 숨김이 필요한 일부 리소스에 대해서, 이러한 세부 정보를 캡슐화하고 표시해야 하는 항목만 제공 할 수 있습니다.

왜 JSON인가?

  1. 보편성 - 모든 웹 기반 애플리케이션의 57 % 이상이 JSON, JavaScript 기반 또는 JavaScript 구성 요소를 사용합니다.
  2. Human Readable - 아주 간단한 문법과 언어를 사용하므로 소프트웨어 개발을 하는 사람을 포함하여 많은 사람이 쉽게 읽을 수 있습니다.
  3. 새 필드를 쉽게 변경하거나 추가 할 수 있습니다.

REST 디자인을 어렵게 만드는 요인은?

REST는 개발 과정에서의 설계 방식이며 특정한 스펙이나 기술이 아니기 때문에 RESTful API를 초기 설계하는 것은 어렵습니다. 마찬가지로 REST는 설계 방식이기 때문에 스탠다드한 표준이나 기관이 없고, 이에 따라 명확하거나 효율적인 디자인 규칙이 없습니다. 또한, REST에는 HTTP 프로토콜 작동 방식에 대한 해석이 포함되어있어 REST API를 설계 할 때마다 다양한 방식으로 설계할 수 있습니다.

HTTP 메소드를 사용하는 것이 REST 접근법의 핵심 이점이지만, 이는 또한 다양한 RESTful API 설계가 있다는 것을 의미합니다. 이로인해 REST를 디자인하기 어려운 상황에 놓인 개발자들을 위해, 내부에서 판단하기에 가장 중요하고 핵심적인 부분만을 추려서 가이드라인으로 정리한 내용입니다.

REST API 디자인 가이드라인

아래는 Stormpath 의 내부 개발팀이 제안하는 REST API 에 대한 디자인 가이드라인 입니다. 위에서 언급된 바와 같이 REST API 는 엄격한 규약이나 룰이 있는 것이 아닌, 설계 방식의 한 갈래 이기 때문에, 본 번역글에 포함된 가이드라인이 반드시 지켜져야 하는 룰은 아닙니다.

  1. REST API 에서는 제공하는 리소스를 세분화하지 않고 거친 데이터로 유지합니다.
    기본적으로 개발자/시스템 관리자는 사용자와 서비스의 리소스가 어떻게 상호 작용 하는지를 모릅니다. 즉, 사용자는 서비스의 리소스를 다양한 방식과 형태로 활용 가능하다는 것을 의미합니다. Stormpath의 경우 서비스의 리소스라 함은 계정, 그룹 또는 디렉토리입니다(Auth 및 계정 관리 서비스). 이러한 리소스들에 대해 사용자가 다룰 수 있는 여러 가지 작업이 있으며, 특정 리소스에 대해 작성한 하나의 메서드에 많은 변수와 기능을 추가하는 경우 관리하기가 어려울 수 있습니다. 따라서 REST API의 특정 리소스에 대한 Method를 작성할 때는 리소스 자체를 인수로 사용하는 메서드로 정의하고, 메서드에 해당 리소스에 필요한 모든 기능이 포함되도록하는 것이 좋습니다.
    (REST API 에서는 하나의 도메인(리소스)에 대하여 다양한 인수와 메서드를 작성하여 제공하는 것 보다는, 한번에 해당 도메인의 모든 정보와 기능이 포함되도록 제공하는 것이 더 나은 방향이라는 의미)
    메인 리소스 외의 다양하고 복잡한 조합(coarse grain)들은 어떻게 처리해야 할까요? collection 및 instance 리소스로 작업할 수 있습니다. collection은 유사한 리소스들이 함께 묶인 리소스이며, instance 리소스는 상위 리소스의 단일 인스턴스입니다(A를 참조하는 B들의 묶음=collection, A=상위 단일 리소스). 이렇게 하면 end point 에 영향을 주는 HTTP 동작을 사용할 수 있지만 실제로 각 인스턴스와 동작의 조합에 대해 다른 URL을 만들지는 않습니다. 이러한 설계를 통해 실제 엔드 포인트 정의에 동작을 추가하지 마십시오.
    (즉, 여러 리소스를 함께 변경하기 위한, 혹은 부모 객체를 변경하기 위한 Method를 제공하는 것보다, 부모 객체를 제공하는, 여러 리소스를 함께 제공하는 Method를 제공하는 방식으로 설계하는 것을 추천.)

  2. POST를 사용하여 부분 업데이트를 제공 및 활용
    REST API는 표준 HTTP Method에서 실행되므로 PUT 또는 POST를 사용하여 리소스를 생성하거나 갱신 할 수 있습니다. PUT을 사용하여 리소스를 만들고 POST를 사용하여 리소스를 업데이트하는 방법을 생각할 수도 있지만 실제로 POST를 둘 다 사용할 수 있습니다.

    • 왜 POST를 사용하여 리소스를 만들고 업데이트해야합니까? POST를 사용하면 PUT을 사용하는 것과 달리 호출 할 때마다 해당 데이터 자원의 모든 필드를 보낼 필요가 없기 때문입니다. 모든 업데이트에 대해 업데이트하지 않는 필드를 보내면 데이터 수정을 위한 서버 자원이 필요한 것보다 많이 소모되기 때문에 이 방식은 중요합니다. PUT 대신 POST를 사용하면 트래픽 양을 제한하면서 REST API를 측정 할 때 유용 할 수 있습니다. 또한 한 달에 수백만 또는 수억 개의 요청을 받으면 REST API의 성능에 영향을 미치므로 POST를 사용하여 트래픽을 제한하는 것이 좋습니다.
    • PUT으로 왜 똑같이 할 수 없습니까? HTTP 사양에 따라 PUT은 멱등원(하위 항목까지 모두 동일한 객체 구조를 갖는)이므로 모든 속성을 포함해야합니다. 예를 들어 description을 지정하지 않고 처음으로 응용 프로그램을 만든 다음 description을 추가하여 네 번째 호출을 보내면 상태가 서로 다를 수 있으므로 멱등원의 요구 사항을 위반하게됩니다.
  3. REST API 문서를 미디어 형식 기반의 다른 문서에 연결시켜 두어야 합니다.
    미디어 형식은 간단히 표현하여 데이터 형식이자, REST API의 사양에 대한 규칙과 정의를 담는 문서 형식입니다. REST API를 사용할 때, 클라이언트 프로그램을 만드는 경우 accept-header에 반환 할 기본 데이터 형식을 포함해야합니다. 마찬가지로 서버가 데이터를 실제로 반환되는 방법을 기록한 내용과 유형에 대한 헤더를 반환해야 합니다. 또한 사용중인 모든 데이터 유형에 parsing rules을 추가 할 수 있습니다. 예를 들어 application/JSON+foo 라는 미디어 유형을 두어 클라이언트에 이 JSON 형식의 데이터임을 알려줄 뿐만 아니라, foo 를 통해 해당 데이터를 어떻게 parsing 해야 하는지 알려줄 수 있습니다.
    따라서 클라이언트에서는 데이터를 받을 때 원하는 미디어 유형을 지정할 수 있도록 하고, 서버에서는 Content-Type 헤더를 통해 반환 될 데이터 형식을 알려주도록 합니다. 이러한 Content-Type 커스터마이징은 API를 고객에게 보다 융통성있게 만듭니다.

결론

이 글에서는 개발자 친화적인 REST API를 작성하기 위한 기본 사항은 물론 RESTful API 디자인 방식의 이점에 대해 이야기했습니다. 이것은 Les Hazelwood 의 프리젠테이션에 대한 요약 일 뿐이며 처음 30 분간의 설명이므로 나머지는 영상을 확인하십시오.

번역글 Quickstart: Compose and Django

이 글은 docker 공식 문서의 Quickstart 문서를 번역한 글입니다.
원문은 여기 에서 확인 가능합니다.

이 빠른 시작 가이드는 Docker Compose를 사용하여 간단한 Django / PostgreSQL 응용 프로그램을 설치하고 실행하는 방법을 보여줍니다. 시작하기 전에 Compose가 설치되어 있어야합니다. (Compose 는 Docker Compose 를 의미하며, Docker Compose Install에서 설치방법을 볼 수 있습니다.)

Step 1. 프로젝트 구성 요소 정의

Compose 를 활용하여 장고 프로젝트를 시작하기 위해서는 Dockerfile, Python 종속 파일 및 docker-compose.yml 파일을 만들어야합니다. docker-compose 파일에는 .yml 또는 .yaml 확장자를 사용할 수 있습니다.

  1. 빈 프로젝트 디렉토리를 만듭니다. 디렉토리 이름을 기억하기 쉽도록 지정하는 것이 좋습니다. 이 디렉토리는 응용 프로그램 이미지의 컨텍스트 저장공간이 됩니다. 디렉토리에는 해당 이미지를 빌드하는 데 필요한 자원만 포함되어야합니다.
  2. 프로젝트 디렉토리에 Dockerfile이라는 새 파일을 만듭니다. Dockerfile은 해당 이미지를 구성하는 하나 이상의 빌드 명령을 통해 응용 프로그램의 이미지 내용을 정의합니다. 일단 빌드되면 컨테이너에서 이미지를 실행할 수 있습니다. Dockerfiles에 대한 자세한 내용은 Docker 사용자 가이드 및 Dockerfile 참조를 참조하십시오.
  3. Dockerfile에 다음 내용을 작성합니다. FROM python:2.7 ENV PYTHONUNBUFFERED 1 RUN mkdir /code WORKDIR /code ADD requirements.txt /code/ RUN pip install -r requirements.txt ADD . /code/ 이 Dockerfile은 Python 2.7 기본 이미지로 시작합니다. 기본 이미지는 새 코드 디렉토리를 추가하여 수정됩니다. 기본 이미지는 requirements.txt 파일에 정의 된 Python 요구 사항을 설치하여 추가로 수정할 수 있습니다.
  4. Dockerfile을 저장하고 닫습니다.
  5. 프로젝트 디렉토리에 requirements.txt를 만듭니다. 이 파일은 Dockerfile의 RUN pip install -r requirements.txt 명령에 사용됩니다.
  6. 파일에 필요한 소프트웨어를 추가하십시오. Django psycopg2 
  7. requirements.txt 파일을 저장하고 닫습니다.
  8. 프로젝트 디렉토리에 docker-compose.yml이라는 파일을 만듭니다. docker-compose.yml 파일은 앱을 만드는 서비스를 설명합니다. 이 예에서 이러한 서비스는 웹 서버 및 데이터베이스입니다. 작성 파일은 또한이 서비스가 사용하는 Docker 이미지, 연결 방법, 컨테이너 내에 마운트해야 할 볼륨을 설명합니다. 마지막으로, docker-compose.yml 파일은 이러한 서비스가 공개하는 포트를 설명합니다. 이 파일의 작동 방식에 대한 자세한 내용은 docker-compose.yml 참조를 참조하십시오.
  9. 다음 구성을 파일에 추가하십시오. version: '2' services: db: image: postgres web: build: . command: python manage.py runserver 0.0.0.0:8000 volumes: - .:/code ports: - "8000:8000" depends_on: - db 이 파일은 db 서비스와 웹 서비스라는 두 가지 서비스를 정의합니다.
  10. docker-compose.yml 파일을 저장하고 닫습니다.

Step 2. 장고 프로젝트 만들기

이 단계에서는 이전 절차에서 정의한 빌드 컨텍스트에서 이미지를 빌드하여 Django 프로젝트를 만듭니다.

  1. 프로젝트 디렉토리의 루트로 변경하십시오.
  2. docker-compose 명령을 사용하여 장고 프로젝트를 만듭니다. docker-compose run web django-admin.py startproject composeexample . 이렇게하면 Compose가 웹 서비스의 이미지와 구성을 사용하여 컨테이너에서 django-admin.py startproject compose_example을 실행하도록 지시합니다. 웹 이미지가 아직 존재하지 않기 때문에 Compose는 빌드에 지정된대로 현재 디렉토리에서 빌드합니다. (만일 빌드 타겟 경로를 변경하고 싶다면, docker-compose.yml 파일의 build: 항목을 타겟 경로로 변경하면 됩니다.) 웹 서비스 이미지가 생성되면 Compose가 이를 실행하고 컨테이너 내부에서 django-admin.py startproject 명령을 실행합니다. 이 명령을 통해 Django가 Django 프로젝트에 필요한 기본적인 파일과 디렉토리를 생성하도록 할 수 있습니다.
  3. docker-compose 명령이 완료되면 프로젝트의 내용을 나열하십시오. $ ls -l drwxr-xr-x 2 루트 루트 composeexample -rw-rw-r-- 1 사용자 사용자 docker-compose.yml -rw-rw-r-- 사용자 1 명의 사용자 Dockerfile -rwxr-xr-x 1 루트 루트 manage.py -rw-rw-r-- 1 사용자 사용자 요구 사항 .txt Linux에서 Docker를 실행하는 경우 django-admin 파일은 root가 소유합니다. 이는 컨테이너가 루트 사용자로 실행되기 때문에 발생합니다. 문제를 예방하기 위해 파일의 소유권을 변경하는 것이 좋습니다. sudo chown -R $ USER : $ USER. Mac 또는 Windows에서 Docker를 실행하는 경우 django-admin에서 생성 된 파일을 포함하여 모든 파일의 소유권이 compose 를 실행한 계정으로 할당되는 것이 기본입니다. (ls -l 명령어로 체크하고 넘어가는 것이 좋습니다. $ ls -l 합계 32 -rw-r - r-- 1 사용자 스태프 145 Feb 13 23:00 Dockerfile drwxr-xr-x 6 명의 사용자 스태프 204 Feb 13 23:07 composeexample -rw-r - r-- 1 사용자 직원 159 2 월 13 23:02 docker-compose.yml -rwxr-xr-x 1 명의 사용자 스태프 257 2 월 13 23:07 manage.py -rw-r - r-- 1 사용자 직원 16 Feb 13 23:01 requirements.txt

Step 3. 데이터베이스 연결

이 섹션에서는 위에서 생성한 Django 프로젝트에 대한 데이터베이스 연결을 설정합니다.

  1. Django 프로젝트 디렉토리에서 composeexample/settings.py 파일을 편집합니다.
  2. DATABASES = ...를 다음으로 변경합니다. DATABASES = {     'default': {         'ENGINE': 'django.db.backends.postgresql',         'NAME': 'postgres',         'USER': 'postgres',         'HOST': 'db',         'PORT': 5432,     } } 이 설정은 docker-compose.yml에 지정된 postgres Docker 이미지에 의해 결정됩니다.
  3. 저장하고 파일을 닫습니다.
  4. docker-compose up 명령을 실행합니다. $ docker-compose up Starting composepracticedb1... Starting composepracticeweb1... Attaching to composepracticedb1, composepracticeweb1 ... db1 | PostgreSQL init process complete; ready for start up. … db1 | LOG: database system is ready to accept connections db1 | LOG: autovacuum launcher started .. web1 | Django version 1.8.4, using settings 'composeexample.settings' web1 | Starting development server at http://0.0.0.0:8000/ web1 | Quit the server with CONTROL-C.

이 시점에서 Django 앱은 Docker 호스트의 포트 8000에서 실행되어야합니다. Docker Machine VM을 사용하는 경우 docker-machine ip MACHINE_NAME을 사용하여 IP 주소를 가져올 수 있습니다.

Heroku x Django 배포부터 정적(STATIC)파일 관리까지

지난 글에서 Heroku를 통해 Django를 업로드 하고 It worked! 를 보는 부분까지 진행해보았었습니다. 사내에서 사용하는 조편성 시스템을 heroku를 통해 배포하고자 이번엔 static file, views, file read/write 등을 포함하는 기능으로 글을 작성해보았습니다.

진행하기 전 알아야 할 중요한 Django의 특징들을 몇가지 먼저 짚어보고 다음 단계로 진행하려고 합니다.

  1. Django는 직접적으로 static file을 제공하지 않습니다.
  2. Django는 collectstatic 명령어를 통해 static file들을 settings.STATIC_ROOT에 복제합니다.
  3. Django의 collectstatic은 settings.STATIC_ROOT 경로가 존재하지 않을 때 폴더를 강제로 생성하지 않습니다.

Heroku X Django 기본 구조

Django의 django-admin.py startproject {{app_name}}을 통해 프로젝트를 생성하면 app_name을 바탕으로 한 폴더가 manage.py 파일과 함께 생성됩니다. heroku가 소스코드 루트로 인지하게 되는 경로는 이 manage.py 파일이 위치하는 곳이어야 합니다. 지난 글에서도 이러한 문제로 인해 git init을 진행하기 전, 장고 프로젝트를 생성하여 폴더를 만들고 해당 폴더 안에서 git 명령어를 수행하였습니다.

heroku는 이 manage.py가 존재하는 소스코드 루트(django settings.py 에서 BASE_DIR)에서 requirements.txt, runtime.txt, Procfile 등을 확인하고 각 파일들 내에서 제공되는 코드를 이용하여 서비스를 배포합니다. 즉, heroku가 Django 프로젝트를 찾아 실행할 수 있도록 3개의 파일에 대한 설정이 필요함을 의미합니다. 아래에서는 기본적인 웹 환경 구성을 위해 runtime.txt와 Procfile을 설정하는 법을 알아보겠습니다.

runtime.txt

runtime.txt는 배포될 소스코드의 heroku 환경을 지정하는 파일입니다. 대문자 없이 소문자로만 작성하여야 하며, Django, Flask와 같은 Python 프로젝트에서는 python의 버전 지정을 할 수 있습니다.

python-2.7.15

Heroku는 기본적으로 python의 버전을 3.x로 제공하기 때문에 2.x 버전을 사용하기 위해서는 위 코드와 같은 내용을 runtime.txt에 작성하여 BASE_DIR에 저장해야 합니다.
(단, heroku는 서비스 특성상 2.x, 3.x 각각의 최신 버전만을 제공하고 있으니 이점을 참고하여 runtime.txt를 작성해야 합니다.

Procfile

Procfile은 app의 startup에 필요한 command를 기록하는 파일입니다. Process 실행과 관련된 다양한 설정값을 주로 전달합니다. (Worker 갯수, web server 지정 등)
runtime.txt와 마찬가지로 BASE_DIR에 존재해야 하며, 파일을 생성할때는 Procfile명을 그대로 사용해야 하고, 확장자를 입력하지 않아야 합니다.

기본적으로 지정할 수 있는 옵션 항목이 추가적으로 존재하지만 이전 글에서의 예시처럼 기본적으로 web: gunicorn {{app_name}}.wsgi(web: 뒤에 띄어쓰기 조심)와 같이 gunicorn을 실행시키는 설정 값이 들어갑니다. 이 명령어의 정상적인 실행을 위해requirements.txt에 gunicorn을 추가하고 gunicorn을 통해 장고에 접근할 수 있도록 설정하는 것이 heroku를 통해 배포할때에 권장되는 방법입니다.

Procfile 명령어를 통해 프로세스가 시작될때 필요한 일부 설정 값을 지정할 수 있습니다. $PORT와 같은 명령어로 포트를 지정하거나, log file의 설정 등을 수행할 수 있습니다. 아래는 이에 대한 상황별 코드입니다.

# 특정 포트로 생성되도록 지정
web: python manage.py runserver 0.0.0.0:$PORT

# 로그 파일을 생성하도록 설정
web: gunicorn {{app_name}}.wsgi --log-file -

# release 프로세스 타입으로 설정
release: gunicorn {{app_name}}.wsgi

Procfile에서 세부 내용을 더 설정할 수는 있지만, 주로 heroku run명령어로 세부적인 조작 및 제어가 가능하기 때문에, 일반적으로는 위와 같은 설정 정도만 진행한 후 heroku run 명령어를 통해 제어합니다.

Heroku X Django 의 staticfile

글의 처음에서 언급한 staticfile 이슈는 heroku를 처음 사용할 때 Django 개발자들에게 난감함을 줍니다. Static file의 호스팅을 S3 등의 storage로 구성하지 않는다면, Django는 스스로 static file을 제공하지 않습니다. 이러한 문제는 settings.py의 설정값을 변경하거나 조정하는 것으로는 해결이 불가능한 문제입니다.

이러한 상황을 해결해줄 친구로 WhiteNoise가 있습니다. WhiteNoise는 Django와 연동되어 사용될 라이브러리로써 정적 파일의 hosting 없이 장고가 직접적으로 파일을 제공할 수 있도록 도와줍니다. 설정은 settings.py 안에서 모두 가능합니다.

# whitenoise 설치 및 requirements.txt 반영
$ pip install whitenoise
$ pip freeze > requirements.txt
# settings.py 설정 변경
MIDDLEWARE = [
  'django.middleware.security.SecurityMiddleware',
  'whitenoise.middleware.WhiteNoiseMiddleware',
  ...
]

...

# STATICFILES_STORAGE, STATIC_ROOT 추가
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(DJANGO_DIR, 'static'),
]

manage.py collectstatic은 Django의 static 파일들을 settings.STATIC_ROOT 경로에 수집하고, whitenoise는 STATIC_ROOT경로에 있는 파일을 읽어 제공하기 때문에 위 2가지 설정값(STATICFILES_STORAGE, STATIC_ROOT)이 같이 제공되어야 합니다.

collectstatic 명령어는 지정된 경로에 폴더를 만들지 않습니다. (권한 이슈) 이로인해 위 설정값을 지정한 후 heroku 배포시 오류가 발생합니다. 이를 막기 위해 BASE_DIR 경로에 staticfiles 폴더를 미리 만들고, 해당 폴더에 임의의 파일을 추가하는 방법이 좋습니다.

$ mkdir staticfiles
$ echo ' ' > staticfiles/init

Heroku 와 Django 쓸만 한가?

다른 프레임워크들과 다르게 Django에는 여러 특징적인 부분들이 존재하고 이러한 특징들은 Heroku의 방식이나 로직과는 일부 다를 수 있습니다. Heroku의 특성상 토이프로젝트를 운영 및 배포하기 위한 용도로써의 사용은 나쁘지 않지만, Database를 함께 이용하거나, 관리하는 측면에서는 아무래도 불편함이 있을 수 밖에 없습니다.

다음번에는 Database (PostgreSQL)를 이용하는 Django 서비스를 Heroku에 올리는 방법에 대해 이야기해보겠습니다.

Heroku CLI 설치부터 배포까지

몇일 전 항상 많은 도움을 주시는 이지호님과 만나 Heroku CLI의 여러 기능들을 보게 되었습니다. 기존에 인지하고 있던 Heroku는 생성부터 배포까지 번거로운 과정이 있다라는 이미지였고, 이로 인해 잘 사용하지 않았었는데, CLI로 간편하게 프로젝트 시작부터 배포까지 가능한 것을 보고 한번 사용해보기로 결심했습니다.

Heroku란?

git을 base로 Cloud PaaS 를 제공하는 곳으로, 홈페이지에서 간단한 이메일 인증으로 가입할 수 있습니다. 기본적으로 배포되는 서버는 무료로 제공되지만 사용자의 접속 여부에 따라 가동이 정지(30분간 이용이 없으면 sleep)되기 때문에, 활성 서비스로 운영하기 위해서는 유료 결제가 필요합니다. 유료 결제(월 $7)시 SSL을 기본 제공하고 domain을 커스텀할수 있어, 블로그 처럼 항상 운영 중인 서버가 없다면 유료 결제도 고려해볼만한 옵션입니다.

server(Heroku 내에서의 명칭은 Dyno)외에도 Database를 함께 사용할 수 있는데 마찬가지로 사용량에 따라 비용이 책정되어 저렴하게 이용 가능합니다. Heroku-cli 를 통해 해당 Database에 대한 관리와 연동이 가능하기 때문에 server와 함께 사용할 경우 더욱 편리합니다.

설치

맥에서는 brew를 통해 손쉽게 설치가 가능합니다. brew install heroku/brew/heroku 명령어로 바로 설치가 가능하고, 설치되는 즉시 CLI의 기능을 사용할 수 있습니다. 설치가 완료되면 heroku --version 명령어로 정상적으로 설치가 이루어졌는지 확인할 수 있습니다.

설치가 완료되면 heroku의 자동완성기능을 사용할지 여부에 대해 묻는데, oh-my-zsh와 bash 의 설정 방식이 다릅니다. Oh-my-zsh 를 기본 쉘로 사용할 경우 아래 코드를 .zshrc 파일 하단에 넣어준 후 명령어를 입력해야 합니다.

if type brew &>/dev/null; then
  FPATH=$(brew --prefix)/share/zsh/site-functions:$FPATH
fi

명령어 입력 후 저장한 뒤 heroku autocomplete --refresh-cache를 입력하면 브라우저 로그인을 통한 인증이 수행된 뒤 autocomplete 기능이 활성화 됩니다. (최초 설치시 위와 같은 과정으로 인해 로그인이 자동으로 진행되지만, 이후에는 heroku login 명령어를 통해 최초 로그인 후 계정 정보를 로컬(~/.netrc)에 보관합니다. )

Django App 배포해보기

설치가 완료되어 로그인을 진행하고 나면 간단하게 배포해볼 프로젝트를 만들어보고 시작할 수 있습니다. 로컬의 코드를 관리하는 부모 폴더에서 사용하는 virtual 환경(pyenv 혹은 virtualenv 혹은 둘다)를 이용하여 python 환경을 셋팅하고 Django와 gunicorn을 설치해줍니다. 이어서 명령어로 django project 생성과 git initialize 해줍니다.

pip install django gunicorn
django-admin startproject heroku
cd heroku
git init

생성된 경로에서 git remote -v를 통해 아무 저장소가 연결되어 있지 않음을 확인하고 heroku의 repository에 연동하여줍니다.

# heroku app 생성 명령어를 통해 heroku service에 배포할 프로젝트를 생성
heroku create {{app_name}}
# 현재 initialize 된 프로젝트의 git repository를 heroku로 연결
heroku git:remote -a {{app_name}}

# git repository가 연동되었는지 확인
git remote -v

Git 명령어의 결과로 fetch, push remote 서버가 등록된 것을 확인할 수 있습니다.

위 과정을 거치고 나면 heroku 서버에서 django가 실행될 수 있도록 gunicorn을 실행할 수 있는 파일을 제공해주어야 합니다. Django root 폴더에서 아래와 같이 수행할 수 있습니다.

echo 'web: gunicorn {{django_project_name}}.wsgi' > Procfile

또 heroku 서버가 이 프로젝트의 배포에 필요한 python 패키지를 인스톨할 수 있도록 아래 명령어로 requirements 파일을 생성하여 줍니다.

pip freeze > requirements.txt

Heroku 에서 배포시에는 heroku 앱의 이름으로 도메인이 제공되기 때문에 django settings.py 에서 이를 수정해주고 반영하는 것이 좋습니다.

# 변경 전 코드
ALLOWED_HOSTS = []

# 변경 후 코드
ALLOWED_HOSTS = ['{{heroku_app_name}}.herokuapp.com']

작업이 마무리되면 최종적으로 배포를 진행해줍니다. git add -A 로 소스코드를 git에 반영한 후 git commit -m "init"으로 커밋을, git push heroku master를 통해 소스코드를 heroku repository에 푸시합니다. 푸시를 하고나면 collect static 과정에서 오류가 발생할 수 있는데 heroku config:set DISABLE_COLLECTSTATIC=1 명령어를 통해 임의로 막은 후 다시한번 git push heroku master 명령어를 수행하여 배포를 마무리 할 수 있습니다. 배포가 완료되면 아래와 같은 알림문구 들이 뜨는데 released version 하단에 제공되는 링크로 접속하면 Django의 It worked! 를 보실 수 있습니다.

그래서 Heroku?

과거에 Heroku를 제대로 사용하는 방법이 무엇인지 몰랐을 때는 heroku에서 제공하는 Document만 보며 이해하려 했었는데, 직접 쓰는 걸 보고 해보니 이렇게 편한걸 이제 알았나 싶습니다. 글을 쓰기전 배포 과정을 한번 수행해본 후 바로 $7 카드결제를 질렀고, 어떤 프로젝트들을 올릴지, Database 연동은 언제해볼지 등을 고민하게 됩니다. 당장 회사에서 점심 식사 조 편성 페이지를 heroku로 전환할까 생각이 들만큼, 좋은 서비스라는 생각입니다.

Heroku에서는 서버에 접속하지 않아도 Django Shell 이나 manage.py 를 활용한 명령어를 전송할 수 있는데 다음 글에서는 이와 관련된 내용을 담아보려 합니다.

스타트업의 개발자 채용, 그리고 스택 변경에 대한 단상

스타트업의 개발자 채용은 어려운가?


 많은 스타트업 경영인들과 유니콘 기업들의 리더들은 스타트업에게 중요한 핵심 덕목 중 하나로 빠른 시장 변화에 대응하기를 꼽곤 합니다. 기업에겐 빠른 시장 변화에 대응하는 것이 당연히 기업의 생존성을 높이고 시장 경쟁력을 확보할 수 있는 요소이니, 스타트업 뿐 아니라 대부분의 기업에게도 중요한 요소일 수 있겠습니다.

빠른 시장 변화에 대응한다는 것은 스타트업의 개발 조직에게는 조금 다른 느낌으로 전달 됩니다. 개발된 시스템은 방향 전환에 시간이 소요되고, 이러한 시간을 줄이게 되면 개발 부채라는 저장고가 차게됩니다. 이러한 문제를 최소화 하고자 소수의 개발조직으로 빠르게 생산성을 높일 수 있는 언어/시스템 프레임워크 등이 스타트업에게 고려됩니다. Ruby on Rails나 Django와 같은 프레임워크부터 node.js, Python 과 같은 개발 언어를 시스템 언어로 선정하는 등을 통해 소수의 개발 인력으로도 운영과 구축이 가능한 시스템을 만들도록 설계합니다.

스타트업의 확장면에서는 앞서 선택한 전략이 잘 맞아들어가는 시기가 존재합니다. 서비스가 확장하고 유저수가 늘어날수록 회사에 수익이 돌기 시작하고, 개발조직을 포함한 모든 조직이 성장합니다. 성장된 조직들은 다양한 시스템 니즈를 요구하고, 이러한 니즈들에 즉시 대응해 나가며, 기존의 시스템 언어/프레임워크를 선택한 결정에 대해 만족할 수 있을 것입니다. 그 과정에서 개발조직의 확장은 언제나 그렇듯 기존 시스템에 종속적이고, Java 위주의 국내 IT 시장에서는 적합한 개발자를 찾는데에 문제를 겪게 됩니다.

우리나라의 많은 기업들은 Java를 그 시스템의 코어 언어로 활용합니다. 대기업 뿐 아니라 중견기업을 포함하여 SI/SM 을 포함한 많은 기업들의 시스템이 Java로 구성됩니다. 스타트업이 Java로 초기부터 시스템을 구축하는 것은 불가능에 가깝습니다. 최초 자본금이 몇억씩 있는 경우가 아니라면, Java 기반의 웹서비스를 운영/제공하기 위해 필요로 하는 개발자의 최소 구성수를 감당할 수 없기 때문입니다. 서비스가 성공할지, 실패할지도 모르는 상황에서 혼자서 많은 것을 해결할 수 있는 개발자를 데려오기도 어려울 뿐더러 설사 방향성에 공감하는 개발자를 찾더라도 비용적인 문제를 감당하기 쉽지 않습니다.

지금 제가 속한 곳 역시 Python/Django를 최초 기술 스택으로 선정하였고, 지난 6년간 많은 성장을 거듭해오면서 수많은 기술 부채와 수백가지의 비즈니스 로직을 가진 수십가지의 도메인들이 개발 효율을 저하할만큼 쌓여있습니다. 이러한 상황속에서 개발자의 채용에 어려움을 겪기 시작하면서 Java로의 시스템 포팅이 화두에 올랐습니다.

무엇이 문제인가?

사실 대부분의 스타트업이 현재의 속도를 유지하거나 현재의 성장 만큼 앞으로도 진행하기만을 바란다면 이러한 문제를 겪지는 않을 것입니다. 기술 부채와 정량화되지 않은 비즈니스 로직은 개발 효율을 저하할 수는 있지만 주기적으로 관리가 가능합니다. 쉽게 생각하여, 2:1의 비율로 확장 목적의 시스템 개발과 부채 해결 및 안정화 목적의 리팩토링을 병행해 나갈 수 도 있습니다.

그러나 궤도에 오르기 시작하는 스타트업들은 더욱 빠르게 성장하고 더욱 기민하게 움직이고 싶어합니다. 흔히 말하는 j-curve의 시작점이 다가왔을 때 그 곡선의 높이를 더욱 크게 만들 수 있는 방안을 빠르게 찾고 빠르게 적용하고 싶어합니다. 더 빠르고 기민한 개발 조직을 만들기 위해서는 더 많은 개발자의 채용과 효율적인 조직 구성이 필요합니다. 문제는 이 과정에서 더 많은 개발자의 채용이 가능하게 할만큼 기업의 인지도, 채용에 투입될 자금력, 국내 개발자 Pool의 3박자가 딱 떨어지는 일이 없다는 것입니다.

우리나라의 IT 시장에서의 개발자 Pool은 100명의 Java 개발자를 채용하는 것이 30명의 Ruby 개발자를 채용하는 것보다 쉽고, 효율적인 조직이 구성될 수 있도록 할만큼의 적당한 경력과 업무 능력을 갖춘 개발자는 그 연봉이 적지 않습니다. 더군다나 기업의 인지도가 국내 업계에서 얼마나 알려져있느냐(물론 서비스가 B2B이냐 B2C냐는 큰 기준이 있지만) 역시 개발자의 이직에 영향을 주는 강한 요소 중 하나입니다. 물론 기업의 인지도에 있어 가장 큰 문제는 개발자들이 기업의 채용 공고를 관심갖고 볼만큼 기업을 잘 알지 못한다는 문제입니다.

개발자를 제외한 조직 구성원이 20~40명 내외인 스타트업 정도의 규모라면 개발조직도 그에 상응할 만큼 확보가 되어 있어야 겠지만, 위 3가지 요소 중 하나라도 어긋난 다면 채용은 쉽지 않습니다. 그중에서도 기업이 컨트롤 하기 어려운 유일한 영역이 개발자의 Pool입니다. 이는 기업이 기존 시스템의 언어를 변경하는 것에 대해 고민하게 되는 가장 기본적인 이유입니다.

문제를 더 뜯어보았는가?

해결이 가능한지에 대한 부분은 무엇이 문제인지에 대해 조금 더 바라보아야 합니다. 기업의 인지도와 채용에 투입될 자금력은 당연하게도 기업이 해결에 나서야 할 부분입니다. 이 두가지가 부족하다면 기업 스스로 해결방안을 모색해야 합니다. 돈이 많다면 채용 공고에 압도적으로 많은 비용을 쏟음으로써 인지도를 조금이나마 높이고, 돈이 부족하다면 투자나 비용 최적화 등을 통해 개발자에게 더 사용하는 방향으로 조율이 가능하겠습니다. 또는 개발자에게 기존의 직책/직급보다 더 높은 것을 제공함으로써 얻어낼 수도 있습니다.

개발자의 Pool 문제에 있어서는 기업이 해결하기 조금 어려운 문제입니다. 위에서의 언급처럼 기업이 사용하는 시스템 언어를 보편적인 것으로 변경하는 옵션이 그렇기 때문에 고려되는 것으로 보입니다. 저 역시 이점을 고민하면서 당연스럽게 떠올린 옵션이었던 것 같습니다. 유일한 대안이자 가장 문제 해결에 가까운 것으로 생각했습니다.

그러나 이 생각에 간과한 것이 있었습니다. 최초의 문제로 돌아가 왜 이 언어/프레임워크를 스타트업의 언어/프레임워크로 정했는가? 입니다. 시장 변화에 더 빠르게 대응할 수 있도록 시스템 구축 비용이 저렴하고 소수의 개발자로 기능 개선/구축 및 운영이 가능하도록 하는 것 입니다. 이러한 특성은 지금까지 팀 내부에게 자연스러운 모습이었지만, 시스템 언어의 변경은 이러한 특성을 없애버릴 수 있는 리스크가 존재합니다. 기능 하나를 구성하기 위해 필요로하는 Django 개발자 한명의 생산성은 같은 경험을 가진 java 개발자의 생산성보다 효율적입니다.

또, 현재의 IT 개발자 시장은 위에서 언급한 바대로 대기업과 중견기업에 의해 구축되고 안정화된 Java 시장이 주류입니다. SI 시장에 존재하는 많은 Java개발자를 포함하여 구축된 시스템의 운영을 위해 필요로 하는 Java개발자까지 원인과 과정 모두에 있어서 대기업이 얽혀있습니다.

두가지 생각으로 정리될 수 있었습니다.

  1. Java와 같은 주류 언어로 변경할 경우 지금과 같은 생산 퍼포먼스를 유지하기 위해 필요로하는 인력은 어느정도인가?
  2. 대기업이 타겟으로하는 Java 개발자 시장에 우리 정도의 인지도와 자금력으로 채용이 가능한가?

생각의 결론

채용 문제를 해결하기 위해 시스템 언어를 변경하는 것이 좋은 솔루션인가에 대해서는 위 두가지 이슈에 대해 x배, 네라고 대답할 수 있어야겠습니다. 2번째 생각에 대해 기업문화나 사무실의 위치, 팀원들과의 관계 등을 통해 ‘네’라고 대답이 가능하더라도 x의 숫자 크기가 어느정도인지가 중요한 듯 합니다. 아직 Java에 대해 많은 공부가 이루어지지 않았고, 어느정도의 생산성 차이를 갖길래 많은 개발자들이 Python, Ruby와 Java의 생산성 차이에 대해서도 크다고 이야기 하는지 명확하지 않습니다.

1, 2번의 생각들에 내린 결론이 ‘시스템 언어 변경은 좋지 않아’ 라면 몇가지 방법이 더 있는 것 같습니다. 물론 검증이 필요하고 생각보다 오랜 시간이 걸릴 수도 있겠지만 크게 2가지 방안이 보입니다. 두가지 중 한가지만 해서는 문제 해결이 불가능할 것이고, 두가지 모두 오랜 시간이 소요되는 방법입니다.

  • 주니어 개발자를 채용하여 원하는 능력 수준만큼 성장시키는 방안
  • 개발 조직의 효율성을 개선하는 방안 이 두가지를 모두 수행하는 것이 새 시스템 언어를 선정하여 채용하고, 차세대 시스템을 구축하는 과정에 들어가는 비용보다 적은 것이 명확해야만 시도해볼 방법이겠습니다.

결국 시스템 전환에 얼마만큼의 비용이 들어가는 가를 알아야 비교가 가능하다는 생각으로 이어집니다. 다가오는 다음 주말에는 Java의 생산성에 대한 검토와 Spring MVC를 좀더 해본 뒤에 생각을 정리해보아야 겠습니다.

분투력

생각을 만드는 문구들.

성공으로 가는 길목에서 생기는 많은 문제는 어마어마한 연습량이 대성공으로 직결된다는 생각에서 비롯된다. 연습은 성공의 요소 중 하나이지 차별화 수단은 아니다.
성공하기까지의 과정에서 기회는 분명 불평등하게 찾아온다. 하지만 기회의 윰무가 성공을 결정짓지는 못한다. 더 중요한 것은 현재의 나보다 뛰어난 존재가 되기위해 위험을 감수하고 전력을 다하는 것이다.
역사상 가장 큰 성공을 거둔 사람, 기업, 단체, 국가는 그 중요성과 가치를 유지하기 위해 반복적으로 스스로를 파괴했다. 편안한 상태에 안주하면 안일해지기 마련이다. 성공을 거두려면 계속해서 자신을 불편에 빠드리거나 끊임없이 스스로의 한계에 도전해야 한다.
신뢰하되 검증하라. – 로널드 레이건

생각들.

자기계발서적을 선정할 때 앞으로 주의해야 할 것은, 저자의 성공 경험이 나에게 어떻게 다가올지 생각해보고 정해야 한다는 것. 저자의 예시가 나에게 충분한 설득이 되기 어려울 수 있다.

읽고 난 후.

저자가 전달하고자 하는 내용은 명확했고, 빠르게 이해 되는 편이었다. 스스로가 원하던 나에 대한 망치질 같은 것이 있었다. 저자가 전달하고자 하는 내용에 당위성을 주기 위한 예시들은 적합하지 않았다고 생각한다. 일부 내용에서는 불필요한 예시로 인해 오히려 혼란스럽기도 했다.

항상 책을 구매할 때 이 책을 통해서 무엇이 얻어질까? 라는 생각을 하고, 그 무엇이 명확할 때 책을 결정하는 편이지만, 이 책만큼 명확하게 얻어지는 경우는 드물다. 목표에 도달하기 위한 방법론을 제시하는 것처럼 보이지만 결과적으로 내 태도와 방식에 변화를 주어야 함을 강하게 어필한다. 내 머리속에서 당연시 하던 몇몇 전제조건들을 대놓고 틀렸다고 말하며, 그것이 왜 틀린 전제인지로 두들겨 팬다. 그러고 나서, 그것에 대한 대안을 제시한다.

책을 다 읽고나서 생각해보아도, 내가 가지고 있던 전제조건들의 무지함과 나태함이 쉽게 돌아오지 못하도록, 강한 인지를 주는 책이었다. 개인적으로 적절한 시기에 적절한 책을 접하게 되어 기쁘다.