CODEOK.NET

Page history of 스타트업을 위한 기술 스택



Title: 스타트업을 위한 기술 스택 | edited by Youngrok Pak at 9 years, 2 months ago.

필자가 스타트업 개발자들에게 가장 많이 받는 질문은 기술 스택을 어떻게 구성할 것인가이다. 심지어 개발자가 아닌 창업자들도 기술 스택에 대해 자문을 구해오곤 한다. 그래서, FAQ 삼아 스타트업에게 적합한 기술 스택을 정리해보기로 했다.

본론으로 들어가기에 앞서 잠시 스타트업의 정의에 대해 합의하고 싶다. 필자는 린 스타트업의 저자 에릭 리스의 정의를 좋아한다.

A startup is a human institution designed to create a new product or service under conditions of extreme uncertainty.

쉽게 말해서 스타트업은 아주 불확실한 일을 하는 팀이라는 것이고 이 글에서 제시하는 기술 스택 역시 이런 특성을 고려할 것이다.

경고:이 글은 코덕넷의 다른 기사와 마찬가지로 객관적이고 중립적인 관점이 아니라, 필자의 경험과 지식에 따라 과감하고 단정적으로 기술을 평가할 것이다. 만일 자신이 사용하는 기술에 대한 부정적인 견해를 접했을 때 기분이 상하는 성격의 소유자라면 이 글은 더 이상 읽지 않기를 권한다.

서버 프로그래밍 언어와 프레임워크

보통 스타트업의 기술 선택에서 가장 중요한 것은 서버 프로그래밍 언어와 프레임워크다. 기술 스택의 다른 모든 것들을 합한 것보다 더 중요하다. 스타트업의 서버 프로그래밍 언어로 선택할 만한 것은 다음 세 가지 중 하나다.

  • 파이썬
  • 루비
  • 자바스크립트 (Node.js, io.js, ...)

한 때 자바도 웹 개발용으로 각광을 받았으나, 현 시점에서 자바와 위의 세 가지 대안의 생산성 차이는 몹시 크다. 쉬운 일은 쉬운 도구로 하고 자바는 이제 좀더 어렵고 미션 크리티컬한 분야로 넘기도록 하자. 혹시 PHP를 생각하고 있을지 모르겠는데, PHP는 이제 미래가 없다. 간혹 간단한 웹사이트는 PHP가 가장 빠르다는 주장도 있었으나 요즘은 PHP가 간단한 웹사이트를 만드는 시간에 Rails나 Django는 제법 복잡한 웹사이트를 만들고 시간이 남아서 어드민까지 붙여놓을 수 있다. PHP는 진입 장벽이 낮은 언어지 생산성이 좋은 언어가 아니다.

물론 어떤 언어를 선택하든 별로 중요하지 않다는 주장을 하는 사람도 있다. 그런 사람들은 실제로 어떤 언어를 쓰든 별반 차이가 없다. 그런데 필자라면 아이언맨 슈트를 줘도 K2 소총이랑 똑같은 전투력을 내는 병사를 전장에 내보내진 않을 것 같다. 좋은 도구를 선택하는 안목 역시 개발자의 중요한 능력 중 하나다.

언어를 선택하고 나면 프레임워크도 거의 결정되는 거나 마찬가지다.

  • 파이썬 - Django
  • 루비 - Ruby on Rails
  • Node.js - express.js

물론 다른 선택도 있으나, 이 세 가지 프레임워크는 널리 쓰이기도 하고, 딱히 큰 단점이 있는 것도 아니라서 다른 대안을 선택한다고 해도 특별히 나은 선택이 되기는 어려울 것이다. 

Ruby on Rails

셋 중에 개발 생산성이 가장 뛰어난 것은 Ruby on Rails다. 다만, 그 높은 생산성을 얻기까지는 Rails를 깊이 파고 들어야 하고, 트렌드를 잘 따라가야 한다. 루비 언어를 깊이 아는 것보다 Rails를 깊이 아는 것이 더 중요하다. 서드 파티 코드를 넣기가 쉬워서 뭔가 구현하고 싶은 게 있을 때 찾아보면 거의 다 있고, 아주 쉽게 설치할 수 있기 때문에 얼핏 어려워 보이는 기능을 순식간에 구현하곤 한다. 그래서 이 맛에 길들여진 Rails 개발자들은 다른 프레임워크를 거부하는 경우를 흔히 볼 수 있다. 설령 여러 가지 이유로 다른 언어를 쓰더라도 웹 개발만큼은 Rails를 버리지 않는다.

Python, Django

반면, Django는 초기 학습 장벽은 비슷하지만 그걸 넘어서고 나면 그 다음에는 별로 학습할 게 없다. 그 다음부터는 Django보다 파이썬이 중요하다. Ruby on Rails는 문제를 Rails로 해결한다면, Django는 문제를 파이썬으로 해결한다. 그래서 중급 이상으로 올라서는 것은 Django가 더 빠르나, Django를 계속 깊이 판다고 해서 큰 이득이 있는 것은 아니다. 개발 생산성의 성장 그래프를 그린다면 다음과 같을 것이다. Django에서도 물론 많은 기능을 제공하지만, 그런 디테일을 알고 모르고가 큰 차이를 내진 않는다.

Node.js

Node.js는 요즘 인기를 끌고 있긴 하나, io.js의 분화 같은 일도 있고, 콜백지옥도 아직 완전히 해결된 상태가 아니다. express.js도 훌륭하지만 아직 Django나 Rails에 비하면 부족함이 있다. 그래서 일반적인 웹사이트나 API 서버를 개발한다면 Node.js는 파이썬이나 루비에 못 미친다. 그럼에도 불구하고 세 가지 권고안 중에 들어가는 이유는 전통적인 스타일의 웹과 다른, Single-page_application의 경우에는 Node.js에 유리한 점이 많기 때문이다. 특히 AngularJS 등의 자바스크립트 MVC 프레임워크와 결합하면 클라이언트와 서버의 코드를 매끄럽게 이어지게 개발할 수 있다. 그리고 웹앱 뿐 아니라 채팅처럼 실시간으로 커뮤니케이션이 일어나는 기능을 가장 손쉽게 구현할 수 있는 방법인 socket.io도 Node.js에 기반하고 있다. 타 언어도 socket.io를 지원하지만 Node.js에서 가장 사용하기 쉽다. 

단, Node.js를 사용할 때는 신경써야 할 점이 두 가지 있다. 하나는 콜백지옥에 대한 해법을 선택해야 한다는 것이다. node-fibers가 비교적 일찍 나와서 여러 라이브러리에 적용이 되어 있으나, 자바스크립트 커뮤니티에서는 generator를 더 밀고 있다. generators-vs-fibers 같은 글이 선택에 도움을 줄 것이다. 사용하려는 다른 라이브러리가 둘 중에 뭘 사용하느냐에 따라 선택하는 것도 방법이다. 참고로 step이나 promise는 대안처럼 보이지만 별 도움이 되진 않는다.

두번째 고민해야 하는 것은 자바스크립트를 그대로 쓸지, 아니면 커피스크립트 같은 자바스크립트 기반의 다른 스크립트 언어를 쓸지이다. 자바스크립트를 콜백지옥과 함께 쓰다보면 코드 가독성이 심각하게 떨어지기 쉬운데, 이럴 때 커피스크립트가 도움이 될 수 있다. 

 

셋 중에 필자의 추천은 파이썬과 Django다. 그 이유는 단점이 적기 때문이다. Rails는 최고의 생산성을 낼 수 있다는 뚜렷한 장점이 있고 진입장벽은 높지 않지만, 높은 수준에 도달하기까지는 학습 분량이 적지 않고 - 물론 자바와 스프링에 비할 바는 아니지만 - 아직도 성능 이슈가 약간 있다. Node.js는 single page application이나 채팅에서 강점을 보이지만 그 외에는 Django나 Rails보다 많이 약하다. 이에 비하면 파이썬과 Django는 엄청난 장점이 있는 것은 아니지만 단점이 별로 없어서 두루 쓸 수 있고, 비교적 짧은 시간에 학습 장벽을 넘을 수 있다.

데이터베이스

RDBMS vs NoSQL

데이터베이스는 우선 RDBMS를 쓸 것이냐 NoSQL을 쓸 것이냐부터 선택해야 한다. 물론 RDBMS가 여러 모로 유리하다. 많은 스타트업들이 자신들의 서비스가 대박이 나면 그냥 RDBMS로 버티지 못하는 건 아닐까 걱정을 하고, 처음부터 대용량을 커버할 수 있는 설계를 하고 싶어한다. 하지만 RDBMS의 한계는 생각보다 아주 멀리 있다. 우선, 리플리케이션이나 여타 RDBMS를 스케일업할 수 있는 수단을 쓰지 않고 순수하게 RDBMS 서버 한 대에 쿼리 튜닝만 잘 해놓아도 국내에서 방문자 10위권 사이트 정도는 커버가 된다. 간혹 하루 방문자 10만명 내외의 사이트인데 DB가 부하를 못 견딘다고 NoSQL로 가야하는 거 아니냐고 묻는 팀도 있는데, 이건 전적으로 쿼리 튜닝의 문제다. 쿼리 튜닝도 고난이도의 튜닝이 필요한 게 아니라, 잘 정규화가 되어 있고 기본적인 인덱스만 잘 걸려 있으면 된다. 그러면 Django나 Rails에서 사용하는 ORM을 그대로 쓰면서 성능 문제를 해결해갈 수 있는 것이다.

만약 정말로 비즈니스가 대박이 나서 저 정도로는 감당할 수 없는 상황이 되어간다고 해도 방법이 있다. 캐시를 추가하면 캐시 전략에 따라 다르지만 수 배에서 수십 배의 부하를 감당할 수 있다. 이것만으로도 많은 상황이 해결될 것이다. 그리고 read-only 리플리케이션을 붙이면 수 배 확장 가능하고, 샤딩을 하면 데이터 사이즈에서의 대용량도 커버 가능하다. 이 정도 수준까지 하면 사실상 국내 최고 수준의 트래픽도 감당할 수 있다. 그리고 여기까지 하는 것도 이미 사례들이 많아서 그리 어려운 일이 아니다.

이걸로도 해결이 안되는 스케일이 되더라도 여전히 솔루션은 존재한다. 미들웨어를 투입할 수도 있고, 테이블 설계를 단순하게 할 수도 있고, 부분적으로 NoSQL을 사용할 수도 있다. 어쨋든 용량이 커진다고 해서 RDBMS로 만든 것을 갈아엎고 NoSQL을 도입해야 하는 것은 아니고 RDBMS를 주력으로 쓰면서 계속 성능을 개선해갈 수 있다.

그렇지만 사실 RDBMS의 한계보다 스타트업에게 더 중요한 것은 그 한계를 만날 수 있게 되느냐일 것이다. 그 한계를 만나려면 어쨋든 제품이 성공해야 하는 것이고, 제품을 성공시키려면 개발 생산성을 우선할 수 밖에 없다. 그렇게 해서 비즈니스가 성공하고 나서 회사의 자원이 넉넉해지면 나중에 성능을 개선할 방법은 얼마든지 있다. 그래서 초기 단계의 스타트업이 성능을 이유로 NoSQL을 선택할 필요는 없다.

MySQL vs PostgreSQL

RDBMS 중에 선택지는 사실상 둘 뿐이다.

  • MySQL
  • PostgreSQL

둘다 나쁘지 않은 선택이다. MySQL이 관리하기는 좀더 쉽다. phpmyadmin 같은 훌륭한 관리 도구도 있고, 세계적으로 가장 널리 쓰이는 DBMS이기 때문에 문서나 각종 레퍼런스도 많다. MySQL에 문제점도 아주 많이 있지만, 그 문제점에 대한 대안조차도 검색하면 다 나올 정도다. 다만 SQL 표준을 지키지 않는 부분이 좀 있고, 개발자의 합리적인 예상을 벗어나는 동작들이 가끔 있기 때문에 간혹 어이 없는 상황을 겪기는 할 것이다. 그리고 MySQL이 오라클에 넘어갔기 때문에 오픈소스 계열은 MySQL을 fork해서 MariaDB를 쓰고 있어서 이 혼란도 부담이 될 수 있다. 한 가지 중요한 포인트는 위치정보를 다루는 기능이 부족하다는 것이다. 스토리지로 MyISAM을 쓸 때만 위치정보 인덱스가 지원되는데 요즘은 MySQL 스토리지 엔진으로 InnoDB를 쓰는 게 대세인지라 위치정보를 제대로 활용하기 어렵다. 

PostgreSQL은 좀더 순수한 오픈소스로 자리를 지켜왔고, 표준 SQL을 잘 준수하고 있으며, RDBMS로 갖춰야 할 기능들을 잘 갖추고 있다. GIS 지원이라든지, JSON type, Array type 등 여러 가지 기능 면에서도 PostgreSQL이 앞선다. 다만 사례가 부족해서 MySQL보다 좀더 정보를 찾는데 어려움을 겪을 수는 있다.

성능 면에서는 단순한 쿼리일수록 MySQL이 더 낫고, 복잡해질수록 PostgreSQL이 낫다고 하나, 최신 버전에 대해 객관적으로 비교할 수 있는 벤치마크는 드물다. 필자의 경험에서는 MySQL이 이 잘 돌아갔던 사이트보다 10배 정도 작은 규모의 사이트에서 PostgreSQL로 성능 때문에 고생했던 적이 있다.

MongoDB?

앞서 굳이 NoSQL을 쓸 필요가 없다고 했으나, 그래도 MongoDB는 한 번 검토해볼 가치가 있다. RDBMS가 전반적으로 유리함에도 불구하고 MongoDB가 괜찮을 수 있는 이유로 다음의 이유들을 꼽을 수 있다.

  • 위치정보를 다루기 쉽다.
  • 스키마와 인덱스의 자유도가 높다.
  • Node.js와 궁합이 좋다.

MongoDB는 기본으로 2차원 인덱스인 2d가 탑재되어 있고, 최근에는 구의 표면 위치를 다룰 수 있는 2dsphere 인덱스가 추가되어 더욱더 정확하게 위치정보를 처리할 수 있다. 위치정보를 다룰 때 중요한 것 중 하나가 현재 위치 주변에서 거리 순서로 정렬한 N개의 결과를 보여주는 KNN 쿼리인데 MongoDB는 이 KNN 쿼리가 문법도 편하고 성능도 가장 좋다. 그래서 데이터베이스를 MySQL로 쓰더라도 위치정보는 따로 MongoDB에 저장하기도 한다. 물론 PostgreSQL도 PostGIS로 지원하긴 하지만 MongoDB가 성능도 더 좋고 더 쉽다.

스키마의 자유도가 높다는 것은 장점일 수도 있고 단점일 수도 있는데, 필자의 경험에서는 Node.js에서 사용할 경우에 자바스크립트 객체를 그대로 데이터베이스에 넣고 쿼리할 수 있어서 상당히 편리했다. 특히 복잡한 구조의 데이터이지만 어떻게 활용할지 정책이 정해지지 않은 상황에서 데이터를 대충 밀어넣고 나중에 인덱스를 걸 수 있다는 점은 장점이다. 예를 들면 위치기반 앱을 만들 때 포스퀘어 연동을 해서 API에서 받은 장소의 데이터를 받았을 경우 RDBMS라면 나중에 이 데이터에서 어떤 부분이 필요할지 미리 예상해서 위치좌표라든가, 카테고리 분류명 등을 따로 필드로 만들어서 빼놓아야 하는데, MongoDB에서는 처음부터 고민할 필요 없이 일단 넣어놓고 나중에 필요해질 때 데이터 구조를 바꾸지 않고 인덱스를 걸 수 있다. 

다만, Django에서는 스키마 변경을 자동으로 해줄 수 있기 때문에 상대적으로 스키마 변경의 자유도는 큰 장점이 아닐 수 있다. 그리고 PostgreSQL은 최근에 JSON 타입에 인덱스를 걸 수 있는 기능을 추가해서 결과적으로 MongoDB가 할 수 있는 일은 PostgreSQL도 다 할 수 있게 되었다. 

정리하면 다음과 같이 권고할 수 있겠다.

  1. 저장하려는 데이터가 단순하고, 데이터베이스에 대한 지식이 적다면 MySQL을 사용하라.
  2. 위치정보를 많이 다루고 Node.js를 사용한다면 MongoDB가 장점이 있다.
  3. 데이터베이스를 깊이 있게 배워가면서 할 생각이 있다면 PostgreSQL이 좋다.

프론트엔드

여기서 말하는 프론트엔드는 웹의 프론트엔드로 HTML, CSS, JavaScript의 역할을 말한다. 프론트엔드에서는 다음과 같은 기술을 알고 시작하면 좋다.

jQuery는 설명이 필요하지 않을 것이다. Bootstrap은 CSS 프레임워크인데 다음과 같은 이득을 얻을 수 있다.

  • 디자인 역량이 부족한 엔지니어 중심의 팀이 그럭저럭 괜찮은 디자인의 웹사이트를 만들 수 있다.
  • 웹사이트의 UI를 컴포넌트 기반으로 설계할 수 있는 기반이 되어 디자인 및 개발 비용을 절감할 수 있다. 

https://wrapbootstrap.com/처럼 Bootstrap 테마를 판매하는 곳도 있다. http://bootsnipp.com/에서는 Bootstrap 기반의 다른 컴포넌트들도 가져다 쓸 수 있다.

Font Awesome은 다양한 아이콘을 폰트처럼 쓸 수 있는 라이브러리다. 자체적으로 아이콘을 디자인하기 어려운 경우에도 사용하지만, 디자인 역량이 풍부한 팀의 경우에도 효율성 때문에 사용하는 경우가 있다.

Bower는 위와 같은 프론트엔드 라이브러리들을 쉽게 가져다 쓸 수 있게 해주는 패키지 매니저다. 대부분의 프론트엔드 라이브러리가 이미 등록되어 있을 뿐더러, 인기순으로 정렬해볼 수도 있기 때문에 비슷한 카테고리의 라이브러리 중에서 골라야 될 때도 도움을 준다.

이외에 less나 sass처럼 CSS를 유연하게 작성할 수 있게 해주는 것도 있지만, CSS를 충분히 배우기 전까지는 쓰지 않기를 권한다.

Angular.js 등의 MVC 프레임워크는 단일 페이지에서 많은 기능을 소화하는 웹앱 스타일로 개발하는 경우를 제외하면 권하지 않는다. 그냥 전통적인 웹에 약간의 동적인 기능이 들어간 페이지를 개발할 때는 jQuery와 Ajax를 이용하는 방식에 비해 별다른 장점이 없다. 더구나 Angular.js를 잘 쓰려면 자바스크립트와 CSS, DOM을 잘 아는 걸로는 소용이 없고 Angular.js 자체를 많이 알아야 하는데, 이건 오로지 Angular.js를 쓸 때만 도움이 되는 지식인지라 ROI가 낮은 투자가 된다. 다른 MVC 프레임워크로 넘어가면 또 새로 배워야 한다. 

서버 배치(Deployment) 

OS

개발이 완료되면 다음으로 부딪히는 문제는 프로덕션(production) 환경에 애플리케이션을 배치하는 일이다. 그 중에 제일 먼저 고민하게 되는 문제는 아마도 OS일 것이다. 맥도 서버 솔루션이 있지만 아마 비싸고 가성비 낮은 맥을 프로덕션 서버로 쓰는 사람은 없을 것이고, 선택은 윈도우와 리눅스로 갈릴 것이다. 게임 서버를 만든다거나, 혹은 개발자들이 모두 윈도우에서만 개발을 할 줄 안다면 윈도우를 선택하게 될 것인데, 그게 아닌 대부분의 경우는 물론 리눅스가 좋다. 라이선스 비용도 안 들고, 서버 기술들이 모두 리눅스를 중심으로 발전하고 있기 때문이다. 리눅스 중에서 어떤 배포판을 고르느냐도 중요한데, 이건 두 가지로 이야기할 수 있겠다.

  1. 리눅스에 대해 깊이 이해하고 있는 시스템 엔지니어가 있다면 그 엔지니어에게 선택을 맡긴다.
  2. 잘 모르면 우분투를 선택한다.

아마도 리눅스를 잘 아는 SE라면 필자의 조언이 필요 없을 것인데, 이런 엔지니어를 보유하고 있는 스타트업은 거의 없다. 그러니까 아마도 대부분의 스타트업은 우분투를 고르는 게 현명할 것이다. 우분투가 가장 좋은 배포판이라는 뜻은 아니다. 그냥 제일 관리하기 편한 배포판이기 때문이다. 보안이나 성능 같은 이슈가 중요해지면 다른 배포판을 찾아야할지도 모른다. 

웹 서버 및 애플리케이션 서버

OS를 고르고 나면 다음은 웹 서버와 애플리케이션 서버를 선택하는 것이다. OS를 리눅스로 골랐다고 가정하면 웹 서버는 두 가지 선택이 있다.

  • Nginx
  • Apache

웹 서버의 역할은 요청을 애플리케이션 서버로 라우팅해주는 것과 정적인 자원을 서비스하는 것이다. 원래는 Apache가 절대 강자였으나, 이제 Nginx로 대세가 넘어간 듯 하다. Nginx는 등장 초기부터 성능이 좋았고 설정도 조금 더 간단해서 인기를 끌었다. 부하가 높은 상황에서 에러가 많다는 주장도 있었으나 지금은 충분히 안정화된 상태이다. Nginx를 추천한다.

애플리케이션 서버는 프로그래밍 언어에 따라 나뉘는데, Node.js는 별도의 애플리케이션 서버가 필요 없다. 자체적으로 내장된 http 모듈의 서버가 이미 성능과 안정성 면에서 인정을 받아서 프레임워크에서도 그대로 쓰는 경우가 많다. 파이썬은 웹 서버를 Apache로 쓰는 경우는 mod-wsgi를 사용하며 그 외의 경우는 uWSGI와 gunicorn으로 갈린다. 요즘은 성능 면에서 좀더 뛰어난 uWSGI가 대세이므로 uWSGI를 추천한다. Ruby on Rails는 최근 추세를 잘 모르지만 Passenger, Unicorn, Puma가 경합하는 듯 하고, JRuby도 간혹 쓰인다고 하며, Rails 커뮤니티에서 1옵션으로 권고하는 안은 Passenger인 듯 하다.

이미지 서버

많은 이미지를 전송하는 서비스는 이미지 서버도 중요하다. 이 문제에 대해 잘 모르는 개발자들은 흔히 다음과 같은 과정을 겪는다.

  1. 이미지 업로드하면 서버의 특정 디렉토리에 저장하도록 코딩한다.
  2. 업로드한 이미지를 저장하는 디렉토리를 웹 서버에서 연결해서 서비스한다.
  3. 서버 1대일 때는 잘 동작한다.
  4. 서버 2대가 되면 가끔 이미지가 안 뜬다.
  5. 이미지가 안 뜨는 이유는 서버 2대에 각각 업로드된 파일이 다른데 이미지 파일에 대한 요청은 두 서버에 같이 오기 때문. 서버 간 이미지 동기화가 필요하다는 점을 깨닫는다.
  6. 업로드를 하고 나면 동기화를 하도록 코드를 작성한다.
  7. 업로드 직후 동기화하기 전까지 또 이미지가 가끔 안 뜬다.
  8. 동기화 코드가 끝나야 데이터베이스에 기록하도록 변경한다.
  9. 느려진다.

물론 더 나은 방법이 있다. AWS S3에 업로드를 하는 것이다. S3는 무한대의 저장 공간과 무한대의 서비스 용량을 가졌지만 한 대의 서버처럼 다룰 수 있다. 동기화 문제는 S3가 알아서 한다. 다만 S3는 다소 비싸다. 이 문제를 해결하는 더 저렴한 방법은 CDN을 사용하는 것이다. CDN은 축약된 원어(Content Delivery Network)에서 보듯, 컨텐츠를 전송하는데 특화된 서비스다. 이미지나 동영상 같은 컨텐츠를 서비스할 때 사용하며 S3보다 저렴하고 성능도 높다. AWS에도 CloudFront가 있고, 아카마이, CDNetworks 등의 CDN 업체가 있다. 다만, S3보다는 설정이 조금 더 많이 들어가고, 별도의 계약이 필요한 경우도 있기 때문에 스케일이 크지 않을 때는 S3, 혹은 그와 유사한 서비스를 이용하는 것이 좋다. 거듭 이야기하지만, 이미지 서버를 직접 구축하는 것은 좋은 생각이 아니다. 생각보다 할 일이 많을 것이다.

작업 큐

작업 큐(Task Queue)는 비동기로 처리해야 하는 작업들을 담당한다. 예를 들어서 쇼핑몰에 주문이 접수되었을 때 확인 문자를 고객에게 보내준다고 해보자. 그럼 주문 요청이 애플리케이션 서버로 들어오면 주문을 DB에 기록하고 문자를 보내고 나서 주문 완료 페이지를 띄울 것인가? 문자를 보내려면 보통 SMS 업체의 서버에 요청을 보내게 되는데, SMS 업체 서버가 응답이 늦으면 쇼핑몰의 주문 응답도 같이 늦어지게 된다. 그래서 이런 건 일단 주문 처리를 완료하고 나서 고객에게 주문 완료 페이지를 띄우고, 문자 전송은 작업 큐에 넣어둔다. 그러면 작업 큐가 문자 전송을 처리하게 된다(이런 작업 큐를 제공하는 SMS 업체도 있긴 하다). 그 외에도 앱에 푸시를 보낸다든지, 동영상 업로드를 받고 나서 인코딩을 한다든지 하는 작업은 고객의 요청으로부터 시작되는 작업이지만, 고객에게는 일찍 응답을 주고 잠시 후에 비동기로 작업을 처리하게 된다. 이런 일을 담당하는 것이 작업 큐다.

기술 추천보다 설명이 길었는데, 아무튼 작업 큐로 쓸만한 것의 목록은 queues.io에서 볼 수 있다. 파이썬을 사용한다면 celery가 좋다. 다른 언어도 언어마다 있으니 검색해보기 바란다. 어쨋든 중요한 건 시간이 오래 걸릴 수 있는 작업은 사용자의 요청 시점에 바로 처리하지 말고 작업 큐에 넘겨서 비동기로 처리해야 한다는 것이다.

간혹 rabbitmq나 activemq 등의 메세지 큐로 이런 기능을 구현하기도 하는데, 이것도 가능하긴 하지만 작업의 상태를 직접 관리해야 해서 다소 번거롭다. celery처럼 메세지 큐를 래핑한 작업 큐를 쓰는 것이 보다 편리하다. 성능이 중요하고 큐의 동작을 깊이 컨트롤하고 싶을 때는 메세지 큐를 직접 사용하고, 그 외의 일반적인 경우에는 작업 큐를 사용하자.

주기적인 작업

서버를 운영하다보면 주기적으로 실행해야 하는 일이 있다. 데이터베이스 백업이라든지, 자동으로 보내는 주기적인 메일링, 통계 처리 등등은 주기를 정해놓고 실행하게 된다. 이런 일을 소화할 수 있는 도구가 여러 가지 있지만 가장 추천하는 것은 cron이다. 가장 안정적이고 성능 문제도 없고 모든 리눅스 서버에 기본으로 설치되어 있기 때문에 셋업도 간단하다. fabric 같은 배치(deploy) 자동화 도구를 이용하면 설정을 변경하는 일도 간단하다.

cron 사용시 주의할 점 하나는 환경변수가 반영되지 않는다는 것이다. 필요한 환경 변수를 cron에서 실행하는 스크립트 안에 넣거나, 실행 명령에 넣도록 하자.

간혹 cron으로 실행하면서 즉시 실행하지 않고 작업 큐에 넣는 경우가 있는데, 어차피 cron은 사용자의 요청과 무관하게 실행되므로 그럴 필요는 없다.

호스팅

호스팅은 크게 세 가지로 나눌 수 있다. 

IaaS와 PaaS의 차이에 대해서는 IaaS, PaaS, SaaS란 무엇인가요?를 참조하기 바란다. 

비용은 국내 호스팅 업체가 비교가 안될 정도로 저렴하다. 하지만 서버를 자유롭게 추가하거나 빼거나 하기는 어렵다. AWS는 가장 안정적이고 사용하기 편리하지만 비싸다. 초기에는 AWS를 사용하다가 비즈니스가 궤도에 오르고 서버 비용이 중요해지기 시작하면 국내 호스팅으로 옮기는 것도 방법이다. PaaS는 추천하지 않는다. 상당히 편리하긴 하지만 여러 가지 제약 때문에 자유도도 떨어지고 비싸다. 클라우드 중에서는 가격 대 성능비를 열심히 따져보고 고르...지 말고 그냥 AWS를 쓰는 게 좋을 것이다. 레퍼런스도 많고 한국 담당자도 있기 때문에 한국어로 질문의 답변을 받을 수도 있다.

물론 자금은 부족한데 수익이 나기 전까지 트래픽이 많이 발생할 가능성이 높다면 무조건 국내 호스팅이 좋다.

자동화

서버에 배치하는 작업을 어떻게 진행할까? ssh로 로그인해서 apt-get으로 패키지 설치하고 vi로 설정파일 고치고... 이러던 시대는 끝났다. 다음 중에 하나를 사용하라.

  • fabric
  • ansible
  • chef
  • puppet

fabric은 가장 간단하고 쉽다. 실제로 서버에서 실행할 명령을 fabric에서 기술해주면 ssh로 여러 대의 서버에 그 명령을 실행해주는 것이다. fabtools 같은 도구를 쓰면 fabric 스크립트를 좀더 쉽게 작성할 수 있다. 필자의 추천은 fabric이다.

chef와 puppet은 괴물이다. 며칠 씩 이것만 공부하고 싶으면 모를까, 그 외에는 추천하지 않는다.

만일 fabric 같은 스크립트 방식이 아니라 선언적인 방식으로 하고 싶다면 ansible이 괜찮다. 하지만 이것도 학습 장벽은 제법 있다.

간혹 docker를 배치 자동화 도구로 사용하는 경우가 있는데, 이건 추천하지 않는다. docker의 핵심 장점은 리눅스 서버 인스턴스를 VM보다 적은 부하로 띄울 수 있다는 것인데, 이건 서버를 제공하는 관점에서 유리한 거지 서버를 이용하는 개발자 관점에서 유리한 게 아니다. 단순 리부팅은 빨라도 설정 변경했을 때 적용되는 건 느리고, 설정 도구도 dockerfile보다 위의 도구들이 더 편리하다. 

서버 운영

모니터링

서버를 배치하고 나면 그 다음으로 가장 중요한 일은 서버가 잘 떠 있는지 모니터링하는 것이다. 서버는 수많은 이유로 죽고, 서버가 죽으면 매출도 정지되고 평판도 타격을 입으므로 서버가 안 죽게 만드는 일은 보통 모든 일보다 최우선으로 처리해야 하는 일이다.

과거에는 nagios, zabbix, zenos 같은 오픈소스 모니터링 툴을 설치해서 사용했다. 하지만 서버 규모가 커지면 모니터링 서버를 운영하는 것도 엄청난 데이터를 다루어야 하기 때문에 제법 큰 일이 된다. 그래서 요즘은 서버 모니터링도 외부 서비스를 사용한다. 필자는 다음 두 가지를 써보았는데, 둘다 괜찮았다.

오픈소스 도구도 기능적으로는 큰 부족함이 없지만 주로 초기에 시스템 엔지니어들이 만들어서 그런지 시스템에 대해 아주 상세한 정보를 제공하지만 핵심적인 정보만 모아보려면 수동으로 설정을 많이 해야 하는데, newrelic의 서버 모니터링은 웹 개발자 입맛에 맞게 핵심정보만 잘 제공하고 있다. 그리고 newrelic의 진짜 강점은 성능 모니터링에서 나온다. 실시간 프로파일링을 통해서 느린 코드의 트레이스를 볼 수도 있고, 통계적인 성능 병목 지점을 찾아내기도 좋다. 데이터베이스별 플러그인도 제공하고 그 외에 여러 가지 서버에 대한 모니터링을 플러그인으로 제공하며, 장애가 발생했을 때 알람 기능도 잘 갖추고 있어서 모니터링 종결자라 할 만하다. 다만, 해외 서비스라서 국내에서 쓸 때는 다소 느린 감이 있다.

와탭(whatap)은 국내 회사의 작품인데 시스템 모니터링만 놓고 보면 newrelic 못지 않고, 국내망에 있기 때문에 더 쾌적하다. 다만, 아직 초기라서 성능 분석 기능이 없는 것은 아쉽다.

굳이 오픈소스로 직접 구축하고 싶다면 collectd가 가장 성능이 좋고 기초 설계가 잘 되어 있어서 살펴볼 만 하다. 그러나 타 모니터링 도구에 비해 프론트엔드가 부실해서 당장 쓰기는 적합하지 않다. munin도 많이 쓰이지만 시스템 자원을 너무 많이 잡아먹어서 추천하지 않는다.

Failover

모니터링이 장애가 발생하는지 감시하는 역할이라면 failover는 장애가 발생했을 때 빠르게 대처하는 방법이다. 원칙적으로는 장애가 발생하지 않도록 방지하는 노력을 기울여야겠으나, 장애율을 10%에서 1%로 만들기는 쉬워도 1%에서 0.01%로 만들려면 많은 기술이 필요하기 때문에 스타트업은 적절한 수준에서 타협할 필요가 있다. 웹 서버는 로드밸런싱으로 손쉽게 SPOF에서 벗어날 수 있지만 데이터베이스, 메세지 큐 등등까지 클러스터를 구축하는 것은 과하다. 그렇게 어려운 일은 아니지만, 그렇다고 쉬운 일도 아니다. 그래서 현실적인 방법은 장애가 발생했을 때 큰 손실 없이 대응할 수 있는 시스템까지만 만드는 것이다.

데이터베이스는 주기적으로 백업하고 데이터베이스를 바로 셋업하고 백업된 데이터를 복원할 수 있는 스크립트를 만들어놓는 것이 출발점이다. 이것만으로도 대형사고는 막을 수 있다. 백업 주기는 초기에 하루 한 번으로 하다가 트랜잭션이 늘어남에 따라 하루 3~4번에서 1시간에 1번까지 늘려가면 된다. 한 가지 오해하기 쉬운 것은 리플리케이션(replication)으로 백업을 대신할 수 있다고 착각하는 것인데, 백업은 시스템이 다운되었을 때 복구하는 용도이기도 하지만, 데이터베이스에 뭔가 중대한 실수를 했을 때 복구하기 위한 것이기도 하다. 예를 들어 개발자가 마이그레이션 중에 실수로 컬럼 하나를 날려버린다면 리플리케이션으로는 어쩔 방법이 없다. 날린 상황도 그대로 복제되기 때문이다. 그런데 1시간 전 데이터가 백업되어 있다면 적어도 1시간 전까지의 사용자 데이터는 복구할 수 있기 때문에 손실은 1시간 동안의 트랜잭션으로 최소화할 수 있다. 그래서 백업 주기를 점차적으로 짧게 하더라도 1시간 이내로 줄일 필요는 별로 없다.

단순 백업보다 조금 더 좋은 방법은 데이터베이스에서 제공하는 redo log를 통해서 특정 시점을 재현(PITR)할 수 있게 하는 것이다. PostgreSQL은 Continuous Archiving and Point-in-Time Recovery (PITR)을 제공해서 이걸 이용하면 원하는 시점으로 복원이 가능하다. 단, 이걸 쓰더라도 1일 주기의 백업은 해두는 것이 여러 모로 안전하다. MySQL 역시 Point-in-Time (Incremental) Recovery Using the Binary Log를 제공한다. 다만 이것들도 설정하고 데이터를 안전하게 보관할 방안을 마련하고 테스트도 하려면 하루이틀 정도는 소요될 수 있다. cron에 백업 걸어두는 건 10분 이내로 할 수 있으므로 처음에는 그냥 백업만 하는 것도 나쁘지 않다.

데이터 관점에서의 failover는 시스템의 규모와 상관 없이 여기까지(PITR)하면 충분하다. 그 다음 단계는 서버 관점의 failover인데, 앞서 언급한 것처럼 데이터베이스를 셋업하고 복원할 수 있는 스크립트를 준비하는 것이 1단계, 그리고 리플리케이션이 2단계다. 리플리케이션도 보통 PITR에서 사용하는 기술을 이용하게 되므로 설정이 크게 어렵지는 않다. 일방문자가 수십만을 헤아리는 시점을 넘어서면 리플리케이션 정도는 설정해주는 게 좋다.

로그 분석

구글 웹로그 분석을 쓰면 된다. 예전엔 mixpanel도 섞어서 썼으나 점차적으로 구글 웹로그 분석의 기능이 좋아지고 있어서 별로 다른 것을 같이 쓸 필요가 없다. 과거에는 webalizer나 awstats로 웹서버의 로그를 분석했으나, 요즘은 클라이언트에서 실시간으로 로그를 로그 분석 서버로 바로 보내는 방식이 더 많이 쓰인다. 이 방식이 좀더 세부적인 추적이 가능하다. 구글 웹로그 분석만으로도 코호트 분석, AARRR, 퍼널(funnel, 깔대기) 분석, 유입 경로 분석 등 대부분의 지표 분석이 가능하다.

근데 로그분석이라고 하면 사실 하나 더 있다. 서버의 에러 로그를 수집해서 추적하기 쉽게 보여주는 것인데, graylog2나 loggly 같은 도구가 있다. 하지만 아마도 당장은 그냥 서버에 들어가서 tail이나 grep, vi로 찾아보는 정도로 충분할 것이다. 용량이 문제가 된다면 로테이트 시키고 지워도 별 상관 없다.

어떻게 결정할 것인가

이 글에서는 고민할 시간을 아끼면서도 그럭저럭 괜찮은 선택을 할 수 있도록 분야별로 선택지를 최소한으로 줄여서 제시했다. 그러나, 이것은 한 사람의 의견에 불과하다. 그러므로 다른 회사가 어떻게 하고 있나 찾아보는 것이 도움이 될 것이다. stackshare.io에서 다양한 회사들의 기술 스택을 찾아볼 수 있다. 페이스북, 트위터 같은 큰 회사도 있고, stripe나 slack처럼 핫한 회사들의 스택도 볼 수 있다.

국내의 경우 여전히 자바가 가장 널리 쓰인다. 네이버나 엔씨소프트 같은 대기업도 자바를 많이 쓰고, 쿠팡도 자바를 사용한다. 다만, 스타트업의 경우는 해외와 유사하게 파이썬과 루비가 많이 쓰이며, PHP도 많이 쓰인다. 파이썬을 사용하는 대표적인 서비스로는 요기요, 번개장터가 있고, 루비는 텀블벅, 배달의 민족에서 일본에 진출한 라인와우가 있으며 카카오도 과거엔 Ruby on Rails를 많이 썼으나, 2~3년 전부터 성능 문제로 자바로 옮기는 추세다. 

물론 다른 회사의 사례를 참조하는 것도 혼자만 뒤쳐지지 않기 위해 중요한 일이지만, 보다 중요한 것은 개발할 당사자들이 납득할 수 있는 선택을 하는 것이다. 기술 선택의 책임은 결국 개발자들이 지는 거지 개발자가 아닌 그 누구도 그 책임을 대신 져줄 수 없다. 이 글 역시 필자가 최선에 가깝다고 생각하는 선택지를 제시하지만, 개발자 본인 스스로 납득할 수 있는 선택을 해야 한다. 큰 회사는 기술 선택의 자유도가 낮은 경우가 많은데, 기술 선택의 자유도가 높을수록 개발자도 더 책임감을 가지게 되고 신이 나서 일할 수 있다는 점을 명심하자. 카카오 같은 회사는 각 팀에게 모든 기술 결정권을 맡기는데도 별 문제 없이 잘 성장해왔다. 당신의 회사가 카카오보다 크지 않다면 기술 선택을 강제해야 할 당위성은 어디서 찾을 수 있겠는가?

참고로 필자는 이런 기술 선택의 순간이 오면 다음과 같은 절차를 따른다.

  1. 제약사항들을 수집한다.(여기는 꼭 자바를 써야해 등등)
  2. 제약사항 내에서 선택 가능한 방안들을 살펴보고 충분한 정보를 얻기 위해 노력한다.
  3. 충분한 정보가 입력되었으면 하루이틀 숙성시킨다.
  4. 충분히 숙성되었다면 그 다음에 마음 가는대로 결정한다.

물론 이건 필자가 이렇게 한다는 거지 이 방식을 추천하는 건 아니다.

Wiki at WikiNamu