문서화도 다 끝났으니 로직을 코드에 옮기기만 하면 된다. 그러나 아직 어떤 언어를 쓸지/의존성을 어떻게 관리할지/어떤 데이터베이스를 어디서 운영할지와 같은 것들이 정해지지 않아서 바로 개발에 착수하기는 어렵다. 이번에는 어플리케이션 기술스택(프로그래밍 언어와 프레임워크)을 결정하고, /GET 요청 시 'Hello World'text/plain으로 반환하는 서버를 작성해 보자.

도입 이유

웹 어플리케이션 서버를 작성할 수 있는 언어는 수없이 많고, 프레임워크도 그렇다. 그만큼 조직의 현재 상황과 미래, 서비스의 형태, 요구사항, 일정 등에 맞춰서 매우 신중하게 결정해야 한다. '요즘 다 파이썬 쓰니까 파이썬', 'Node.js는 한글 문서가 잘 돼있으니까', '장고가 쓰는 사람도 많고 쉬우니까'같은 트렌드 차원의 이유로 어플리케이션 기술스택을 가볍게 결정하고 나면, 조직에게 매우 큰 기술 부채가 되어 돌아올 수도 있다. 기술 선택은 팀과 비즈니스를 고려해야 한다. 오늘은 두 의사결정 모두 하나의 요구사항 집합으로 정리된다.

배경과 요구사항

  • 조직에게 익숙하거나, 러닝커브가 감당할 수 있는 수준인가? - 생산성이 보장되며 유지보수에 큰 문제가 없는가?
  • 조직이 납득할 수 있는가?
  • 너무 마이너한 기술스택은 아닌가? - 라이브러리나 예제는 결국 그 언어/프레임워크의 사용자가 만들기 마련인데, 인기가 없으면 커뮤니티의 사이즈가 작고, 직접 만들어야 하는 도구의 범위가 넓어질 수 있다.(memcached 클라이언트, StatsD 클라이언트, PostgreSQL ORM 라이브러리 등등) 그리고 커뮤니티 파워가 약하면 먼 미래를 보장하기 어려울 수 있다.
  • 안정적인 응답 속도에 rps(requests per second)를 잘 뽑아주거나, TTL/LRU Cache, 경량 스레드, 비동기 프로그래밍 지원처럼 성능 최적화를 위한 도구가 잘 준비되어 있는가? - 비용 최적화를 위해 매우 중요하다.

쭉 써놓고 나니, 대충 인기 많은 스택이라면 이 조건에 대부분 부합하지 않을까 싶다. 데이터 티어나 모니터링 티어의 인프라를 결정할 땐 조금 더 자유도 높게 선택지를 둘 수 있을텐데, 이번 의사결정은 어쩔 수 없이 인기를 좀 따르게 될 것 같으니 양해 바란다.

의사결정 - 프로그래밍 언어

선택지

WAS 개발에 있어서 주류 언어로 꼽히고 + 필자(조직)가 조금이나마 경험해본 언어들을 선택지로 두자.

  • Python
  • Scala
  • Java
  • Go
  • Node.js

의사결정

Python을 선택하겠다. 그 이유는,

  • 조직(필자)에게 가장 익숙하다.
  • 커뮤니티 파워가 강하다.
  • 언어가 강력하다. 대부분 언어들이 어디 하나씩은 맘에 안드는 부분이 있는데, Python은 이런저런 면에서 평타 이상 쳐준다.
  • unpacking, comprehension, 비교 연산자 chaining과 같은 syntactic sugar나 arrow같은 drop-in replacement 라이브러리들이 생산성을 잘 커버해 준다.
  • 라이브러리 풀이 꽤 크고, 의존성 관리와 가상 환경 지원도 꽤 쓸만하다.
  • 공식 코드 컨벤션 가이드라인PEP8 덕분에 코드 퀄리티 유지도 괜찮게 할 수 있다.
  • Scala액터 모델 기반의 짱 좋은 동시성을 가지고 있고, 동시성 모델 자체가 Immutability에 기반되어 있어서 동기화에 관한 삽질이 비교적 적다. 그러나 조직 내에 Scala를 잘 다루는 개발자가 없어서 생산성을 보장할 수 없다.
  • Java는 비즈니스 관점에서 인력풀이 풍부하고 레퍼런스가 많지만, 필자의 기준으론 JSON 리터럴이 없어서 꽤 불편하다. Go의 [string]interface 타입 map이나, Scala의 List/Map 타입, Kotlin의 hashMapOf 함수처럼 JSON 리터럴을 우회할만한 방법도 딱히 없다. Map 객체를 만들어 put문을 발라두거나 별도로 VO(Value Object)를 정의하고 JSON string을 VO 객체로 역직렬화/VO로 만든 객체를 JSON string으로 직렬화하는 식으로 사용한다. VO를 사용하는 방식은 꽤 직관적이지만, WAS의 로직과 데이터 모델이 복잡한 게 아니기 때문에 JSON 리터럴 표현식이 언어 차원에서 지원되는 게 좋을 듯 싶었다. 게다가 나중에 VO를 쓰는 방식으로 스타일이 바뀌더라도, 파이썬이라고 못하는 것도 아니고 말이다.
  • Go는 웬만하면 성능 정말 잘 뽑아주고, 사용자 층도 세계적으로 꽤 많아지는 추세지만, 이게 생각보다 생산성이 좋지 않고 의존성 관리가 마음아프다. 그래도 트래픽 처리 비용을 낮추려고 시도하게 된다면, 고성능 파이썬 웹 프레임워크들과 Go를 벤치마킹해 볼 것 같다. 내가 Go를 많이 안 해봐서 삽질하는 게 아닐까 하는 믿음이 있다(아직은).
  • Node.js는 JavaScript 런타임인데, 자바스크립트라는 언어가 생각보다 잘 쓰기 정말 어렵고, 굳이 러닝커브를 감수하고 Node.js를 선택하는 것에 당장의 메리트가 별로 없다.

결론은 조직에게 익숙한 Python을 선택하게 됐다. 물론 조직에게 아무리 익숙한 기술이더라도, 그 기술이 의사결정의 기준에 부합하지 못한다면 보류시키는 게 맞다.

만약 백엔드 개발자가 여럿 있고 각각에게 익숙한 기술스택끼리의 교집합이 없거나 적은 상황이라면, 서비스와 개발 조직의 요구사항(예산, 처리량, 개발 일정 등)을 기반으로 서로를 설득하는 시간을 갖자.

의사결정 - 웹 프레임워크

선택지

써볼만한 프레임워크는 이정도가 있다.

의사결정

Sanic을 사용하겠다. 그 이유는,

  • Flask마이크로 프레임워크이기에, 자유도가 매우 높다. 원하는 설계 방향대로 프레임워크를 유연하게 써먹을 수 있다. 그러나 멀티스레드 모델의 원천적인 한계(컨텍스트 스위칭) + Python의 GIL 구조로 인해 여러 스레드가 시분할해서 동작하기 때문인지, 성능이 조금 아쉽기도 하고 요청 객체가 의존성 주입 형태가 아니라 global 객체로 제공되어서 자칫하면 안티패턴 코드를 작성하게 된다. Flask를 가장 많이 써봤지만, 얘를 열심히 튜닝하기 전에 대체제를 찾아보고 싶었다.
  • Sanicuvloop: Blazing fast Python networking이라는 글에서 영감을 받아, Flask-like한 APIasync 개념을 넣고 uvloop로 asyncio 이벤트 루프를 튜닝한 비동기 웹 프레임워크다. 벤치마크 상황에 따라 다르지만, 동일 조건에서 웬만하면 Sanic이 Flask보다 2~4배정도 처리량이 많다. 그리고 요청/응답 객체가 의존성 주입 형태로 제공되고, 미들웨어 개념이 잘 잡혀 있다.
  • Vibora도 Flask-like API에 뭔가 이것저것 붙여서 퍼포먼스를 높이는, Sanic과 비슷한 컨셉을 가지고 있다. 그리고 실제로 정말 빠르다(Flask를 기준으로, Sanic이 2~4배, Vibora가 5~6배 정도). 그러나 레퍼런스가 너무 없다. Sanic은 공식 문서라도 잘 작성되어 있는데, Vibora의 문서는 '이게 다인가?' 싶다.
  • Japronto는 내가 아는 파이썬 웹 프레임워크 중 rps가 가장 높다(웹 프레임워크 전체에 대한 벤치마크에서도 거의 선두권). 그런데 뭐 프레임워크가 딱히 제공해주는 게 별로 없다. 라우팅 메소드 하나랑 요청 객체, 응답 헬퍼 몇 개 정도. 2017년 2월에 미들웨어 관련 이슈가 올라왔지만 '계획이 있다'고만 하고 아직도 open되어 있다. 그리고 Vibora보다 크게 빠르지도 않고, 사용 사례가 많지 않다.
  • Falcon은 사용 사례도 꽤 있고 + 성능도 Japronto, Vibora와 함께 파이썬 웹 프레임워크 중에서 선두권 + 문서도 잘 만들어져 있지만 프레임워크 디자인 자체가 꽤 새로운 느낌이라 러닝커브를 감당하기 힘들 수 있을 듯 싶었다.
  • aiohttp는 생각보다 퍼포먼스가 안나온다. Sanic이나 aiohttp나 async/await 기반인데, 굳이 aiohttp를 선택할 이유가 없다.
  • Tornado는 이름만 보면 엄청 빨라 보이는데, 파이썬의 주류 웹 프레임워크들 중에 퍼포먼스가 가장 안나온다. 퍼포먼스를 포기하고서라도 쓸만한 이유도 없다. 생각보다 커뮤니티 파워도 약하다.
  • DjangoDjangoRestFramework가 있긴 하지만, Django에 익숙한 조직이 아니므로 제외했다. DjangoRestFramework를 쓰는 후배한테 물어봤는데, 이것저것 챙겨주는 게 많아서 익숙해지기 시작하면 생산성이 아주 괜찮다고 하니 구미가 당긴다면 배워봐도 좋을 것 같다.

작업 - Hello World 서버 작성

이슈를 생성하고, /GET 요청 시 'Hello World'text/plain으로 반환하는 서버를 작성해 보자. 서버 작성 직후의 스냅샷

지금까지

  • 버전 관리 시스템으로 Git을 사용하기 시작했다.
  • Git 웹호스팅 서비스로 GitHub를 사용하기 시작했다.
  • GitHub Issues와 Projects로 이슈 트래킹을 시작했다.
  • 개발 프로세스와 브랜칭 모델을 정립했다.
  • HTTP API 아키텍처 기반으로 API 스펙을 디자인하기로 했다.
  • JSON을 직렬화 포맷으로 결정했다.
  • Authorization 헤더로 인증 정보를 명시하기로 했다.
  • 인증 스키마에 JWT 기반의 Bearer를 사용하기로 했다.
  • API 스펙을 정의했다.
  • GitBook으로 API를 문서화했다.
  • Python + Sanic 조합으로 WAS를 개발하기로 했다.


+ Recent posts