기고문2014.07.05 15:09

본 내용은 월간 마이크로소프트웨어 2014년 7월호에 기고된 내용입니다 :)


--


배달음식 주문 중계 서비스 '철가방'의 핵심 기능중 하나인  실시간 알림(PUSH)을 구현하기 위해 Zookeeper와 Vert.x를 적용한 사례에 대해 이야기합니다.



배달음식 주문 중계 서비스의 정의


배달음식 주문 중계 서비스란, 일상생활에서 여러분들이 많이 사용하고 있는 ‘배달의 XX’, ‘요X요’ 등의 스마트폰 어플리케이션을 떠올려 보면 쉽게 알 수 있습니다. 일반 소비자가 스마트 디바이스(스마트폰, 스마트TV 등) 또는 웹과 같은 인터넷 사용이 가능한 매체를 통해 음식 주문을 발생 시키면, 해당 정보를 배달음식 가맹점에게 전달하여, 음식 주문 거래가 성립케 하는 서비스입니다.


전통적으로, 일반 소비자가 배달음식 가맹점과 가장 쉽게 커뮤니케이션할 수 있는 방법은 전화를 이용하는 것 이였습니다. 하지만 전화를 통한 커뮤니케이션 방식에서 일반 소비자는 배달음식 가맹점 연락처를 미리 알고 있어야 한다는 불편함을, 배달음식 가맹점에는 일반 소비자에게 해당 가맹점을 홍보하기 위한 마케팅 비용의 부담이 발생하게 됩니다.


스마트 디바이스를 활용한 배달음식 주문 중계 서비스는 이처럼 전화를 기반으로 하는 서비스에서 일반 사용자와 배달음식 가맹점 양측 모두가 지니는 불편과 부담을 최소화하기 위한 대체 서비스입니다. 물론 최근 유명한 배달음식 주문 중계 스마트폰 어플리케이션을 운영하는 업체에서 주문 중계 수수료에 대한 논란이 있었던 만큼 시장 상황이 그렇게 녹녹치 만은 않고, 앞으로 해결해야할 과제가 아주 많은 분야이기도 합니다.



배달음식 주문 중계 서비스 구조


배달음식 주문 중계 서비스에 대한 포괄적인 이야기는 여기까지 하고, 주문 중계 서비스를 구축하기 위한 기술적 제반 사항에 대해 이야기 해보도록 하겠습니다.


배달음식 주문 중계 서비스를 구성하는 핵심 티어는 <화면 1>과 같이 일반 사용자와 배달음식 가맹점, 그리고 이 둘을 이어주는 주문 중계 서비스업체로 구분할 수 있습니다. 여기에 부가적인 기능을 수행하기 위해 통신사 및 결제대행업체(VAN)가 참여할 수 있습니다.


<화면 1. 주문 중계 서비스 티어>


각 티어의 고유 기능과 티어들 간의 연결을 위해 <리스트 1>과 같이 다양한 플랫폼과 기술이 개발되고 운영되고 있습니다. 모든 내용을 살펴보기에는 분량이 매우 많기 때문에, 핵심 티어인 일반 사용자와 배달음식 가맹점, 그리고 이 둘 사이를 실시간으로 연결하기 위한 방법에 대해 보다 자세히 알아보도록 하겠습니다.


 

 주문 중계 서비스 업체

배달음식 가맹점 

일반 사용자 

 솔루션

 Java 기반 

Spring, Vert.x 

서버 시스템

 Windows 응용 어플리케이션

iOS

Android

Web 

서버 연동방식 

RemoteObjectCall

REST API

연동방식 제공 

RemoteObjectCall 

REST API 

 실시간 알림

TCP PUSH

TTS

SMS/LMS

알림 방식 제공 

TCP PUSH

TTS

SMS/LMS 

APNS

GCM 

<리스트 1. 티어별 시스템 구성>


가장 먼저 일반 사용자가 관심 있어 하는 지역(현재 위치 또는 미리 등록된 지점 등)에 위치한 배달음식 가맹점 정보를 제공하고, 특정 가맹점으로 주문정보를 발생시키는 기능을 수행하는 어플리케이션이 필요합니다. 일반적으로 안드로이드/iOS 운영체제에서 동작하는 스마트폰 어플리케이션은 그 역할을 매우 훌륭하게 수행해 냅니다. 

물론 스마트폰 어플리케이션에 전국의 배달음식 가맹점 데이터 모두를 담을 수 없고, 또한 실시간으로 변동되는 배달음식 가맹점 상태 정보를 수집하고 제공하기 위해 주문 중계 서비스 업체는 스마트폰 어플리케이션과 연동될 수 있는 서버를 운영해야 합니다. 스마트폰 어플리케이션과 서버는 인터넷을 통해 데이터를 송/수신하는 할 수 있는데, RESTful하고 Stateless한 인터페이스(이하 REST API)를 통해 통신하는 것이 일반적인 선택사항 이라 할 수 있습니다.


일반 사용자는 이러한 스마트폰 어플리케이션을 통해 배달음식 가맹점 정보를 확인하고, 최종적으로 선택한 가맹점에 주문정보를 발생시킵니다. 주문정보는 REST API서버에서 최종적으로 확인되고 검증됩니다. 검증 정보에는 주문 금액, 결제방식 등이 포함되며, 경우에 따라 결제대행업체(VAN)와 연동될 수도 있습니다.


자 이제부터 핵심적인 문제가 시작됩니다.

일반 사용자로부터 발생한 주문정보는 현재 REST API서버에서 확인되었고, 주문정보를 해당 배달음식 가맹점에 전달해야 합니다. 주문정보를 배달음식 가맹점에 전달하는데 걸리는 시간은 실시간에 가까울수록 좋습니다. 주문 후 30분 넘도록 음식이 오지 않을 경우 여러분의 모습을 생각해보면 쉽게 그 이유를 알 수 있습니다.



방법 1. 전화 재 주문 방식


먼저 엔지니어 입장에서 가장 간단한 방법이지만, 서비스 운영의 측면에서는 비용이 가장 많이 드는 방법에 대해 이야기 해보겠습니다.


그것은 바로, 발생된 주문정보를 사람이 확인하고, 해당 배달음식 가맹점에 전화를 해서 주문을 대신해주는 방법입니다. 주문정보가 언제 발생할지 예측할 수 없기 때문에, 해당 업무를 수행하는 사람은 24시간 새로운 주문정보가 있는지 확인해야 합니다. 간혹 주문정보를 잘못 확인하고 엉뚱한 주문을 넣는 바람에 일반 사용자는 시키지도 않은 음식을 받아보거나, 배달 장소가 바뀌어 음식이 오지 않는 경험을 할지도 모릅니다. 


스마트폰에서 스마트하게 주문하는 것은 좋았지만, 이 후 과정은 전혀 스마트하지 않은 이 방법이 아이러니하게도 실제 현장에서는 가장 폭 넓게 사용되고 있기도 합니다. 24시간 주문정보를 모니터링하기 위한 직원을 수십 명(또는 수백 명)씩 배치하기 때문에 엄청난 인건비가 지출되기도 합니다. 근본적으로 전통의 전화를 기반으로 하는 커뮤니케이션 방법에서 크게 나아지지 않은 방법입니다.


하지만, 우리 같은 엔지니어는 별로 할 일이 없습니다. 스마트폰 어플리케이션과 이와 연동되는 REST API서버, 그리고 주문정보를 알려주는 윈도우 하나만 개발하면 모든 게 끝납니다.



방법 2. 배달음식 가맹점 프로그램 사용


사실 방법 1의 주문내용 전달 오류, 고비용이란 치명적인 단점에도 불구하고, 많은 서비스 업체에서 해당 방법을 고수하는 이유는, 전국의 모든 배달음식 가맹점에 실시간으로 주문정보를 전달할 수 있는 표준적인 방법도 없고, 그렇다고 전국의 모든 가맹점에 주문정보 알림 프로그램을 설치하기도 어렵기 때문입니다. 현실에는 주문정보 알림 프로그램의 개발부터 배포, 교육, 지속적 관리 등 해결해야할 문제가 산재해 있지만, 지면을 통해서는 ‘Zookeeper, Vert.x를 활용한 실시간 PUSH 알림 구현’이라는 주제에 맞게 주문정보 알림 프로그램 개발이라는 문제에만 집중해 보겠습니다.


가장 먼저 고민해야 할 것은 바로 주문정보 알림 프로그램 그 자체를 무엇으로 어떻게 만들 것인가? 하는 것입니다. PC환경에 독립적이게 웹 기반으로 만들 수도 있고, Windows 네이티브 어플리케이션으로 만들 수 도 있습니다. 반드시 고려해야할 점은 배달음식 가맹점 현장의 컴퓨팅 환경은 상당히 열악한 조건이 많으며, 사용자의 연령대를 고려해봤을 때 사용하기 어렵지 않아야 하고, 사용자에게 많은 조작을 요구하면 주문 받고 요리하는데 바쁜 가맹점 사장님들이 싫어할게 당연하기 때문에 웬만한 건 다 자동으로 처리 되어야 한다는 것입니다.


그래서 우리는 유행이 좀 지난 기술이긴 하지만 위와 같은 요구사항을 수용하기 위해 FLEX AIR를 기반으로 <화면 2>의 배달음식 가맹점 프로그램을 개발하였습니다. (사실 FLEX AIR Windows 어플리케이션은 지금도 충분히 좋은 대안이라 생각합니다.)



<화면 2. 철가방 가맹점 관리 프로그램>


가맹점 관리 프로그램의 주요 기능은 2가지로 요약할 수 있는데, 첫 번째 기능은 스마트폰 어플리케이션처럼 서버와의 연동을 통해 각종 정보 처리가 가능해야 하는 것이고, 두 번째 기능은 실시간 주문정보 알림을 받을 수 있어야 하는 것입니다.



Spring BlazeDS Integration


스마트폰 어플리케이션이 서버와 REST API통해 연결될 수 있는 것처럼, FLEX AIR 어플리케이션 또한 서버와 REST API를 통해 연결될 수 있습니다. 그러나 여기에는 좀 더 나은 대안이 존재하는데 그것은 바로 Adobe에서 제공하는 BlazeDS란 오픈소스 솔루션을 통해 AMF3(Action Message Format 3) 기반의 RemoteObjectService를 사용하는 것입니다. RemoteObjectService에 대해 간단히 설명하자면, FLEX 어플리케이션에서 서버에 정의된 JAVA Method를 Call할 수 있도록 하는 일종의 RPC(Remote Procedure Call) 서비스입니다. AMF3은 현재 폭넓게 사용되고 있는 JSON이나 XML보다 작은 메모리를 사용하고 훨씬 빠른 객체 직렬화 속도를 보여줍니다. 또한, Spring Source에서 Spring BlaseDS Integration을 제공하기 때문에 기존 Spring 기반 어플리케이션에 쉽게 통합할 수 있기도 합니다.



<화면 3. Spring BlazeDS Integration>

출처 : http://10panther01.blogspot.kr


한 가지 주의 사항은, Google App Engine처럼 Load Balancing 되는 환경에서 BlazeDS를 운영하다 보면 중복되는 Flex 세션이 있다는 오류를 만나게 되는데, 해당 오류를 발생시키는 flex-messaging-core.jarlex.messaging.endpoints.BaseHTTPEndpoint를 패치하고 다시 컴파일하면 문제를 해결할 수 있습니다.

 


PUSH서버


BlazeDS의 RemoteObjectService를 통해 첫 번째 요구사항을 충족시켰다면, 두 번째 요구 조건인 실시간 주문정보 알림을 구현하기 위해 PUSH서버를 개발하게 됩니다. PUSH서버와 가맹점 관리 프로그램은 TCP를 통해 연결됩니다. 주문정보가 발생할 경우 PUSH서버에서는 해당 가맹점의 관리 프로그램 연결을 확인하고, 주문정보를 전송하게 됩니다. 여기까지만 본다면 PUSH서버는 별로 어렵지 않게 구현할 수 있을 것 같습니다.



<화면 4. 시스템 구성도>


그러나 <화면 4>와 같은 시스템 구성을 보면 파악할 수 있듯이 위와 같이 간단한 구조에서는 PUSH서버가 SPOF(Single Point Of Failure)이며, PUSH서버와 연결되는 가맹점 프로그램 개수가 적게는 수백 개에서 많게는 수천/수만 개 까지 확장될 수 있다는 점을 감안해보면 결코 가볍게 넘어갈 수 있는 문제가 아님은 자명해 보입니다.


그래서 PUSH서버의 다중화와 부하 분산, 장애극복을 실현하기 위한 시스템 구성을 다시 생각해 보았습니다.



<화면 5. 개선된 시스템 구성도>


이전 시스템 구성과의 차이점이라면, PUSH서버가 다중화 됨에 따라 로드밸런서(Vert.x 기반)가 추가되었고, PUSH서버의 온라인 상태를 체크하기 위해 Zookeeper가 사용된 것입니다. 또한, PUSH서버와 가맹점 관리 프로그램은 간단한 상태체크 알고리즘을 통해 TCP연결 상태를 확인하고, 연결에 문제가 있을시 가맹점 관리 프로그램은 장애극복 모드로 전환되어 PUSH서버로의 TCP연결을 복구합니다.



Zookeeper


먼저 Zookeeper에 대한 간략한 소개를 하자면, ‘분산 작업을 제어하기 위한 트리 형태의 데이터 저장소’라고 할 수 있습니다. Zookeeper에서 관리 되는 트리의 특정 노드에 데이터를 저장하고 변경할 수 있는데, 특정 노드에 감시자(Watcher)를 등록하면 Callback을 통해 클라이언트에게 노드 변경 여부를 알려줍니다. 노드에는 한 번 저장한 데이터가 영구적으로 유지되는 영구 노드(Permanent Node) 외에도 클라이언트 세션이 유효한 동안만 살아있는 임시 노드(Ephemeral Node)와 저장하는 순서에 따라 자동으로 일련번호가 붙는 순차 노드(Sequence Node)가 있습니다. Zookeeper의 기능은 사실상 이게 전부인데, 여기서 우리가 눈여겨봐야 하는 것은 바로 임시 노드(Ephemeral Node)입니다. 


PUSH서버는 Zookeeper와의 세션을 생성하고, 해당 세션이 유효한 동안에만 유지되는 임시 노드를 등록함으로 PUSH서버의 온라인/오프라인 상태를 Zookeeper를 통해 즉각적으로 파악할 수 있도록 합니다. 즉, 새로운 PUSH서버가 추가되면 Zookeeper에 신규 임시 노드가 등록될 것이고, 기존 PUSH서버에 장애가 생겨 세션이 끊어지면 해당 임시 노드는 Zookeeper에서 제거 될 것입니다. 



<화면 6. Zookeeper 임시노드와 PUSH서버>


가맹점 관리 프로그램은 이제 직접적으로 PUSH서버와 연결되지 않고, 로드밸런서를 통해 현재 온라인 상태의 PUSH서버 중 한 개를 배정받아 커넥션을 연결하게 됩니다. 로드밸런서는 알고리즘에 따라 적당한 PUSH서버를 선택함으로서 PUSH서버의 룩업 기능뿐 아니라 부하 분산 장치로서의 역할을 하게 됩니다.


가맹점 관리 프로그램과 PUSH서버는 일정 주기로 Ping을 주고받는 Healthcheck 기능을 지니고 있는데, Healthcheck 알고리즘을 통해 상태 이상이 감지되면 가맹점 관리 프로그램은 PUSH서버와의 연결을 즉각 종료하고, 처음의 과정으로 돌아가 로드밸런서를 통해 새로운 PUSH서버를 룩업하고 연결을 시도함으로서 장애극복을 실현할 수 있습니다.


PUSH서버로의 트래픽 분산 제어를 위해 Zookeeper가 사용되는 만큼, Zookeeper자체가

중단되면 PUSH서버로의 신규 연결 요청이 모두 마비가 됩니다. 따라서 Zookeeper자체도 최대한 정상 동작을 보장해야 하는데, 이를 위해 여러 대의 Zookeeper 서버를 클러스터로 구성해 고가용성을 확보해야 합니다. 이것을 Zookeeper 앙상블(Ensemble)이라 합니다.


앙상블로 묶인 Zookeeper 인스턴스 중 한 대는 쓰기 명령을 총괄하는 리더 역할을 수행하고, 나머지는 팔로어 역할을 수행하는데, 클라이언트가 전달한 읽기 명령은 현재 연결된 Zookeeper 인스턴스에서 바로 반환되지만, 이에 비해 쓰기 명령은 앙상블 중 리더 역할을 수행하는 Zookeeper 인스턴스로 전달되며, 리더 Zookeeper는 모든 팔로어 Zookeeper에게 해당 쓰기를 수행할 수 있는지 질의하게 됩니다. 만약 팔로어 중 과반수(> n/2)의 팔로어로부터 쓸 수 있다는 응답을 받으면 리더는 팔로어에게 데이터를 쓰도록 지시합니다. 즉, 앙상블의 구성하는 Zookeeper 인스턴스 중 과반수가 살아있다면 데이터 읽기/쓰기를 정상적으로 처리할 수 있습니다. (3대를 사용한다면 1대가 중단되어도 문제가 없습니다.)



Vert.x 기반 로드밸런서


가맹점 관리 프로그램과 PUSH서버, 그리고 Zookeeper까지 완료되었다면 최종적으로 로드밸런서만 준비되면 모든 것이 다 갖춰지게 됩니다. 로드밸런서는 앞서 설명한 대로 PUSH서버의 룩업 기능과 부하 분산 역할을 하게 됩니다. HTTP, TCP, WebSocket 등 다양한 프로토콜을 통해 로드밸런서에 접근할 수 있어야 하며, Zookeeper와 마찬가지로 최대한의 동작을 보장하기 위한 클러스터 구성이 가능해야 합니다. 그리고 이에 대한 해답으로 Vert.x를 선택하게 됩니다.


Vert.x는 요즘 핫한 Node.js처럼 비동기 이벤트 방식의 프로그래밍 모델을 제공하는 플랫폼으로, Javascript뿐만 아니라 Java, Groovy 등 다양한 언어를 지원하고, Node.js보다 효율적으로 멀티코어 시스템을 활용할 수 있다는 장점이 있습니다.



<화면 7. Vert.x 기본 구조>

출처: http://www.javaworld.com/article/2078838/mobile-java/open-source-java-projects-vert-x.html


Vert.x의 기본구조에 대해 설명하자면, 하나의 Vert.x 인스턴스는 다 수의 Verticle을 포함할 수 있으며, Vert.x 인스턴스는 동일한 JVM 머신 또는 네트워크상의 JVM 머신들과 클러스터로 구성될 수 있습니다. 클러스터로 묶인 Vert.x 인스턴스 내의 Verticle들은 분산 이벤트 버스를 통해 메시지를 주고받을 수 있습니다. (Vert.x의 이벤트 버스는 HazelCast 라는 In Memory Data Grid 오픈소스 솔루션을 사용합니다.)


Verticle은 Vert.x에서 하나의 실행 단위(또는 배포단위)로 생각할 수 있는데, 쉽게 말하자면 Main 메소드를 포함하는 하나의 Java 클래스라 할 수 있습니다. 즉, 실제적인 어플리케이션 코드를 담고 있는 것이 바로 Verticle이며, 서로 상호작용 하는 1개 이상의 Verticle들의 조합으로 Vert.x 어플리케이션을 만들 수 있습니다. 각각의 Verticle들은 서로 독립적인 클래스로더를 사용하기 때문에, Verticle 내의 선언된 지역번수는 물론 전역변수, 정적변수 까지 독립적인 상태를 유지하며, Thread 경합 상태를 발생시키지 않습니다. (Single Thread 기반이라 생각하고 코드를 작성해도 됩니다.)


Vert.x 어플리케이션을 개발할 때 주의할 점은, 표준 Verticle내의 코드들은 절대 Thread block을 유발해서는 안 된다는 것입니다. JDBC코드와 같이 Thread block 코드를 어쩔 수 없이 사용해야 하는 상황이 있다면, 표준 Verticle이 아닌, Worker Verticle을 사용해야 합니다. Worker Verticle은 표준 Verticle과 다르게, Event Loop Thread를 점유하지 않으며, Vert.x 인스턴스내의 별도의 Worker Thread Pool에서 동작하게 됩니다. 


이상 Vert.x의 특징을 빠르게 살펴보았는데, 보다 자세한 정보는 Vert.x의 공식 홈페이지에서 제공하는 매뉴얼을 통해 파악하실 수 있습니다. Vert.x를 프로젝트에 도입할 예정인 분들은 반드시 해당 자료를 참조하시기 바랍니다.


그럼 구체적으로 Vert.x를 사용해 개발한 로드밸런서의 구조를 살펴보겠습니다. 로드밴런서는 <화면 8>에서와 같이 5개의 Verticle로 구성되있으며, 이중 1개는 앞서 설명한 Woker Verticle로서 Zookeeper 서버와 통신을 담당하며, 3개의 Verticle은 각각 HTTP, TCP, WebSocket을 통해 클라이언트의 요청을 수신하는 역할을 담당하고 있습니다. 마지막 1개의 Verticle은 실질적은 어플리케이션 로직을 담고 있지는 않지만, 나머지 4개의 Verticle들에 대한 설정정보와 이들을 배포(deploy)시키는 역할을 담당합니다.


<화면 8. 로그 밸런서의 Verticle구성>

 

3개의 Verticle에서 클라이언트의 요청을 수신하면, 이벤트 버스를 통해 Worker Verticle에 해당 내용을 전달하게 되고, Worker Verticle은 해당 내용을 전달받아 처리하고, 그 결과는 다시 최초 요청을 전달한 Verticle에 전달하게 되는 매우 간단한 구조입니다.


<화면 9. 클러스터 구성>


클러스터 기능을 사용하면 <화면 9>와 비슷한 형태로 각 서버에 로드밸런서가 배포될 것입니다. 주목할 점은 로드밸런서를 구성하는 Verticle의 개수를 자유롭게 변경할 수 있다는 것입니다. 일반적으로 Worker Verticle에 부하가 몰리기 때문에 Worker Verticle의 개수를 4개로 늘려놓은 모습니다.



Vert.x 예제 코드


간단하게 로드밸런서를 구성하는 Verticle의 소스코드를 보며, Vert.x 개발 스타일에 대해 알아보겠습니다. 


아래 코드는 SocketAcceptorVerticle의 일부분입니다. 일부분이라고는 하지만 사실상 이 코드가 거의 전부이기도 합니다. 클라이언트가 새롭게 연결 되었을 때 호출되는 connectionHandler를 시작으로 Callback방식을 통해 각 이벤트를 처리하는 비동기 이벤트 방식 프로그래밍 모델임에 주목합니다. 


클라이언트로부터 데이터를 수신할 때 호출되는 dataHandler가 가장 중요한 부분입니다. 수신 된 데이터를 이벤트 버스를 통해 ZKClientWorkerVerticle로 전달하고, 리턴 값을 받으면 클라이언트로 최종 응답합니다. 예제에서는 고정 3바이트를 기준으로 TCP스트림을 나누는 방식을 사용하고 있는데, 가변길이 TCP스트림을 처리할 때는 TCP스트림을 정확한 단위로 나누기 위해 첫 4바이트는 뒤따라오는 페이로드의 길이를 알기 위해 사용되고, 해당 길이만큼 TCP스트림을 읽어 들이는 방법을 사용합니다.

//-- Net Server 설정
server = vertx.createNetServer();
server.connectHandler(new Handler<netsocket>() {
	@Override
	public void handle(final NetSocket sock) {
		logger.info("sock.writeHandlerID["+sock.writeHandlerID()+"] remote host connected: "+sock.remoteAddress());
				
		//-- close handler
		sock.closeHandler(new VoidHandler() {
			@Override
			protected void handle() {
				logger.info("sock.writeHandlerID["+sock.writeHandlerID()+"] remote host disconnected: "+sock.remoteAddress());
			}
		});
		//-- data handler
		sock.dataHandler(RecordParser.newFixed(3, new Handler<buffer>() {
			@Override
			public void handle(final Buffer data) {
				logger.debug("The total body received was "+data.length()+" bytes.");
				logger.info("Lookup request received: "+data.toString());
						
				eb.send(address, data.toString(), new Handler<message<string>>() {
					@Override
					public void handle(Message<string> reply) {
						sock.write(reply.body());
					}
				});
			}
		}));
		//-- exception handler
		sock.exceptionHandler(new Handler<throwable>() {
			@Override
			public void handle(Throwable throwable) {
				if( throwable instanceof IOException ) {
					/* 현재 연결은 원격 호스트에 의해 강제로 끊겼습니다(아마도?) */
				}
				else {
					logger.error("sock.writeHandlerID["+sock.writeHandlerID()+"] Unexpected exception occur: ", throwable);
				}
			}
		});
	}
});

ZKClientWorkerVerticle은 더 심플한데, Zookeeper 앙상블에 연결을 설정하고, 앞서 SocketAcceptorVerticle에서 이벤트 버스로 전송한 데이터를 파싱해 요청을 해석하고 그 결과 값을 다시 처음 해당 데이터를 전송한 SocketAcceptorVerticle로 응답하는 것이 전부입니다.

@Override
public void start(final Future<Void> startedResult) {
	super.start();
	
	address 	= StringUtils.defaultIfEmpty(config.getString("address"), DEFAULT_ZKWCLT_ADDRESS);
	zkhostPort 	= StringUtils.defaultIfEmpty(config.getString("zkhostPort"), DEFAULT_ZK_HOSTPORT);
	zkSessionTimeout = (Integer) ObjectUtils.defaultIfNull(config.getInteger("zkSessionTimeout"), DEFAULT_ZK_SESSION_TIMEOUT);
		
	try {
		zk = new ZooKeeper(zkhostPort, zkSessionTimeout, this);
	} catch (IOException e) {
		zk = null;
		logger.error(e, e);
	}
		
	eb.registerHandler(address, new Handler<Message<String>>() {
		@Override
		public void handle(final Message<String> data) {
			String result = createReponse(data.body());
			data.reply(result);
		}
	},
	new AsyncResultHandler<Void>() {
		@Override
		public void handle(AsyncResult<Void> result) {
			if( !result.succeeded() ) {
				result.cause().printStackTrace();
				startedResult.setFailure(result.cause());
			}
			else {
				startedResult.setResult(null);
			}
			logger.info("Zookeeper client worker "+(result.succeeded()?"OK":"FAILED"));
		}
	});
}

마지막으로 StarterVerticle인데 모듈을 구성하는 각 Verticle을 배포하는 역할을 하고 있습니다. Zookeeper로 연결되어 동기연산을 처리하는 ZKClientWokerVerticle은 Vert.x인스턴스의 Event Loop Thread에서 실행되면 안 되기 때문에 WokerVerticle로 실행하는 것에 주목합니다. HTTPAcceptorVerticle과 WSocketAcceptorVerticle은 따로 설명은 하지 않았지만, 앞서 SocketAcceptorVerticle의 흐름과 크게 다르지 않습니다.

package com.hellowd.s3.lookup;

import org.vertx.java.core.json.JsonObject;
import org.vertx.java.core.logging.Logger;
import org.vertx.java.platform.Verticle;

public class StarterVerticle extends Verticle {
	
	private Logger 		logger;
	private JsonObject 	config;
	
	@Override
	public void start() {
		logger = container.logger();
		config = container.config();
	   
	    logger.info("app config: "+config.toString());
	    
	    container.deployVerticle("com.hellowd.s3.lookup.acceptor.HTTPAcceptorVerticle", 
	    		config.getObject("httpAcceptor"));
	    container.deployVerticle("com.hellowd.s3.lookup.acceptor.SocketAccreptorVerticle", 
	    		config.getObject("sockAcceptor"));
	    container.deployVerticle("com.hellowd.s3.lookup.acceptor.WSocketAcceptorVerticle", 
	    		config.getObject("wsAcceptor"));
	    container.deployWorkerVerticle("com.hellowd.s3.lookup.worker.ZKClientWorkerVerticle", 
	    		config.getObject("zkClientWorker"), 4);
	}

}

마치며

이것으로 ‘철가방’ 시스템을 구성하는 주요 부분에 대해 알아보았습니다. 제한된 지면을 통해 많은 내용을 전달하려다 보니, 설명이 부족한 부분도 많은 것이라 생각합니다. 부하분산, 장애극복과 같은 주제는 실제 서비스를 운영하며 사고가 터지면 욕도 먹어보고, 새벽에 불려나가는 경험을 하면서 내가 먼저 인간답게 살아보고자 하는 마음에 연구에 매진하게 되는 분야인 것 같다는 생각을 했습니다. 이 내용이 비슷한 고민을 하고 있는 많은 분들께 작게나마 도움이 되기를 바랍니다.



* 참고문헌 및 자료

BlazeDS

1. http://livedocs.adobe.com/blazeds/1/blazeds_devguide

2. http://sourceforge.net/adobe/blazeds/wiki/Home

3. http://www.jamesward.com/2007/04/30/ajax-and-flex-data-loading-benchmarks

BlazeDS-Spring

1. https://github.com/spring-projects/spring-flex 

Zookeeper

1. http://zookeeper.apache.org

2. http://helloworld.naver.com/helloworld/583580

Vert.x

1. http://vertx.io

2. http://helloworld.naver.com/helloworld/163784

3. http://bcho.tistory.com/860

Posted by devop

댓글을 달아 주세요

기고문2012.07.18 11:05
본 내용은 월간 마이크로소프트웨어 2012년 7월호, 한빛미디어 "프로그래머로 사는 법" 부록란에 기고된 내용입니다 :)

--

평범한 개발자에서 스타트업 기업 이사로

좌충우돌 스타트업 생존기


지난 2010년 여름. S/W 마에스트로 과정을 마치고, 앞날에 대한 걱정으로 눈앞이 캄캄하던 시절, 동아리 후배 녀석으로부터 스타트업을 함께 해보자는 제안을 받았습니다.

그렇게 시작되었던 helloworld. 어느 덧 1년에 가까운 시간이 흘렀고 이런저런 경험을 통해 많은 것을 생각하고 느낄 수 있던 시간이었습니다. 스타트업을 고민하는 많은 이들에게 도움이 되길 희망하며 그 간의 경험과 생각을 정리해 봅니다.




필자 : 이연복 lyb1495@helloworlds.co.kr

* 서울과학기술대학교 컴퓨터공학과

* 대학생 연합IT벤처 동아리 SOPT 2기

* SW 마에스트로 1기

* 현) helloword 개발팀장



도전의 불안함과 피로, 그리고 즐거움의 helloworld

helloworld는 지난 2011년 8월 자본금 4천만원으로 설립되어, 현재까지 3개의 서비스를 개발 해왔습니다. 임직원 수는 중간에 다소 변동이 있었으나, 현재 총 8명의 가족(?)같이 끈끈한 연대감을 기반으로 구성 된 정말 따끈따끈한 신생 기업입니다.

어떤 분들이 그러시길 가족 같은 분위기를 내세우는 회사는 정말 가족에게 헌신하듯 적은 월급으로도 회사에 헌신하기를 바라는 회사라는데 어떨까요?


네, 맞습니다. 저희도 정말 쥐꼬리만한 월급 받으며 거의 항상 야근을 달고 살고 있습니다. 리소스는 한정되어 있는데 해야 할 일이 참 많이 있기 때문입니다. 하지만 이제 막 시작하는 기업인데 당연히 할 일이 많아야 어떻게든 먹고 살 수 있습니다. 클라이언트에게서 예정이 없던 일이 막 튀어나오고, 자체 서비스도 개발해야 하고... 1년에 가까운 지난 시간들이 정신 없이 지나간 것 같습니다. 그래도 당분간 굶을 일은 없겠다 싶어 어떻게 보면 참 다행이라는 생각이 드는 현실입니다.


이처럼 척박한 환경에서 겨우 굶지 않을 걱정을 해야 하는 스스로가 어떨 때는 참 안쓰럽고 불안하기도 합니다. 안정적인 직장을 다니고 있는 친구들이 부럽게 느껴질 때도 있습니다.

“그래서 지금 나는 불행한가?“ 라는 질문을 던져봅니다.

그것은 또 아닙니다. 오히려 필자를 포함한 모든 구성원들 대부분은 자발적으로 초과업무를 처리하며 즐거움을 잃지 않고 있습니다.


즐거움을 잃지 않는 이유가 무엇일까 생각해보았습니다. 해답은 스티브 잡스가 남긴 명언 속에서 찾을 수 있었습니다.

우리는 사랑하는 일을 하고 있으며, 잠들기 전에 오늘 멋진 일을 했다고 만족할 수 있는 있을 하고 있고, 진실 된 자신의 삶을 살고 있다고 생각하고 있기 때문입니다.


<화면 1. 스티브 잡스의 명언>


이처럼 도전의 불안함과 피로함 그리고 그에 따른 충족감과 즐거움을 동시에 제공해주는 애증의 개체가 바로 helloworld 입니다.


우리나라에서 법인을 설립한 다는 것의 의미.

helloworld의 법인설립일은 공식적으로 2011년 8월 9일입니다. 그러나 사실 8월 법인 설립에 앞서 팀원 모집, 조직 편성, 자금 확보 등의 과정에 약 1년 정도의 시간이 필요했었습니다. 그리고 이렇게 경험을 해보니, 창업에 필요한 요소 중 무엇 하나 중요하지 않은 것은 없지만 가장 현실적으로 자금이 필요하다는 것을 절실하게 느낄 수 있었던 시간 이였습니다.


우리나라에 법인 설립이 쉽다고 생각하는 분들이 있을지도 모르겠습니다.

그렇습니다. 자금에 충분한 여유가 있다면 법인 설립은 쉽습니다. 반대로 자금이 부족하다면 법인 설립은 시작부터 결코 만만한 일이 아니라는 것 또한 사실입니다. 우리나라에서는 법인 설립의 그 순간부터 모든 일에 돈이 필요하기 때문입니다.


<리스트 1. 법인설립의 대략적인 흐름>

1. 주주, 이사, 감사 등 구성원 결합 / 회사의 형태 결정 / 상호의 결정 / 정환작성 및 인증

2. 주식인수, 자본금의 실제 납입

3. 이사회소집, 대표이사, 감사의 선임 등을 거쳐 설립 등기

4. 법인등기부등본 발급. 그 후 기타 서류를 보완하여 사업자등록 신청

법인설립의 복잡한 행정 절차를 스스로 처리하지 못하면, 법무사를 통해 행정 절차를 처리해야 하는데 법무사를 통하면 대략 70~90만원 정도의 비용이 들어가게 됩니다.

시작부터 돈이 들어가는 셈입니다.

어찌어찌 법인설립을 했다 해도 그 이후부터는 세무행정 처리를 스스로 해내지 못하는 한 역시 세무사를 통해 세무행정 처리를 해야 합니다. 이 과정에서도 평균적으로 매달 몇 십만원 씩 비용이 지출됩니다.

컴퓨터, 책상, 각종 기기/집기 등을 구입하는데 역시 비용이 필요하며, 부모님이 빌딩 한 채를 가지고 계셔서 사무 공간 한 칸을 내주시지 않는 한은 사무 공간 마련을 위해 제 법 큰 비용이 들어가게 됩니다.

자신 스스로가 정말 천재적인 재능과 능력을 소유하고 있어 기획/개발/디자인을 스스로 해내지 않는 한은 추가 인력을 보충하기 위해 역시 매달 인건비가 필요하게 됩니다. 보통 스타트업 초기에는 인건비에 활용할 수 있는 자금이 얼마 없기 때문에, 창업멤버로서 지위와 그에 따른 지분을 부여하고 능력 있는 인재를 스카웃하게 됩니다.


자 그럼 이 모든 비용이 과연 어디서 나올 수 있을까요? 따로 부모님이나 든든한 후견인의 도움이 없다면 창업을 준비하는 여러분 스스로가 해결해야 하는 문제입니다. 그나마 최근 청년창업을 지원하는 정부 지원 프로그램이 많아 이것들을 잘만 이용하면 조금은 쉽게 초기 창업자금을 마련할 수 있기도 합니다.


<리스트 2. 2012년 창원지원 프로그램 목록>

사업명

지원내용

지원대상

지원금액

창업선도대학

예비창업자 및 1년 이내 창업 기업

5000만원 이내

예비기술창업자육성

예비창업자 및 1년 이내 창업 기업

일반 3500만원 이내.

중점 5000만원 이내.

창업아이템 상품화지원

예비창업자 및 1년 이내 창업 기업

5000만원 이내

청년창업사관학교

예비창업자 및 3년 이내 창업 기업

(만 39세 이하)

1억원 이내

창업맞춤형사업화지원

예비창업자 및 1년 이내 창업 기업

4000만~1억 맞춤사업별

일부상이.

특화형

선도벤처연계

예비창업자 및 1년 이내 창업 기업

선도벤처 3000만원 이내

기술창업자 6000만원 이내

연구원특화

연구원 예비창업자(팀)

1억원 이내

글로벌 청년창업활성화

예비창업자 및 창업기업

5000만원 이내

* 출처 tp://www.naeil.com/News/economy/ViewNews.asp?nnum=661987&sid=E&tid=5


helloworld를 설립하는 과정에서도 정부 지원 프로그램을 적절히 이용했었습니다. 경기중소기업지원센터의 G창업스쿨, 창업진흥원 1사 1일 꿈나무, 사회적기업 육성 지원 사업 등이 그것입니다.


하지만 이와 같은 정부 지원 자금의 경우, 주변의 많은 분들이 너무 그것에 의존하지 말라는 말씀을 해주십니다. 정부 지원 자금은 상대적으로 쉽게 확보할 수 있는 반면, 구체적이고 실천적인 계획 없이 정부 지원 자금에 의존해 연명해 나가다가 그것이 끊어지게 될 경우 스스로의 역량으로 자립하기가 어려워지기 때문입니다. 또한, 분기별 감사나 보고 등 사업화 내용을 문서화하는데 상당한 리소스를 투입하고 관리해야 하는데, 적임자를 선정하기 어렵고 그에 따라 초기 프로톼입 서비스를 개발하는데 영향을 끼치기도 하기 때문입니다.

그래서 helloworld는 정부 지원 프로그램 외에도, 외주 프로젝트를 진행하며 추가적인 자금 확보 위한 노력을 지속했었습니다. 외주 프로젝트는 초기 자금을 확보하기에 좋은 소스이며, 팀원들의 기획/개발/디자인 역량을 실무 수준으로 끌어올리는데도 좋은 기획가 됩니다.

그러나 여기서도 역시 한 가지 주의해야 할 점이 있는데, 외주 개발 프로젝트와 자체 서비스 프로젝트 사이의 일정 조절을 잘 해야 한다는 것입니다.


스타트업에서의 외주 개발

대부분의 스타트업 기업들은 당장 생존의 문제를 해결하기 위해 자금이 필요하고, 그에 따라 외주개발을 진행하게 됩니다. 앞서 언급한 것처럼 외주 프로젝트는 자금 확보 외에도 팀원들의 역량 향상에 도움이 되는 장점이 있습니다.

그러나, 스타트업의 자체 서비스 개발과 외주 개발 사이에서의 일정 딜레마는 사실 그리 간단한 문제는 아닙니다.


<화면 2. 외주 개발과 자체 서비스 개발 사이의 딜레마>


스타트업 기업을 시작하며 품은 사명감, 철학, 그리고 팀원들 간의 이해관계가 적절하게 합의되지 않는다면, 팀원이 이탈하는 상황을 맞이할 수 있기 때문입니다. 단전인 예로, 초기 스타트업 기업이 자금 마련을 위해 외주 개발을 맡게 되었을 때, 그에 따라 필연적으로 자체 서비스 개발 일정에 딜레이가 발생하며, 불만을 가지게 되는 팀원이 발생할 수 있습니다. 혹은, 지속적인 자금 문제를 해결을 위해 스타트업이 지닌 대부분의 리소스를 외주 개발에 할당하게 되고, 자체 서비스 개발을 등한시 하게 되며, SI전문 개발사로 탈바꿈하게 되는 상황을 맞이할 수도 있습니다. 최근, 지인을 통해 알게 된 경우는 이미 안정적인 수익원을 지니고 있는 스타트업에서 수익원의 다각화를 위해 외주 개발을 전문적으로 전담하는 팀을 꾸리게 되었으나, 운영의 실패로 회사에 막대한 손해를 끼치게 되었고, 이에 대한 책임으로 창업멤버 중에 한명이 퇴사를 하게 이르렀다는 소식을 듣기도 했습니다.

사실, 제가 몸담고 있는 helloworld 역시 이와 같은 자체서비스 개발과 외주개발 사이의 딜레마에서 완전하게 자유로울 수가 없어서, 외주 개발 건으로 인한 의견충돌로 팀원들과 마찰이 있기도 했었습니다. 이러한 문제는 현재에도 여전히 존재하고 있는 문제로서 우리가 돌파하고 해결해야 하는 대상입니다.

그래서 helloworld에서는 주요 임직원들의 생각과 목표를 서로 최대한 일치시키기 위해, 워크숍과 같은 기회를 통해서 서로의 생각을 진솔하게 이야기 해보는 시간을 지니고 있습니다. 이런 종류의 커뮤니케이션은 어떻게 보면 너무나 당연한 것이기 때문에, 크게 신경 쓰지 않고 흘려보내는 경우가 종종 발생하기도 합니다. 또는, 스타트업 같은 작은 기업에서는 팀원들 간의 커뮤니케이션이 어렵지 않고 자연스럽게 이루어질 수 있다고 생각할 수 도 있습니다.

그러나 분명한 것은 이러한 커뮤니케이션의 기회는 의식하고 노력하지 않으면 절대 그냥 얻어지지 않는다는 것입니다. 스타트업 같이 작은 기업일수록 사람이 곧 재산이며 미래이기 때문에, 서로의 생각과 목표를 일치시켜 나가는 작업은 무엇보다 중요합니다.

앞서 언급한 외주 개발 문제와 같이 조직의 미래를 결정 할 중요한 선택의 갈림길에서 팀원들 간의 갈등을 최소화하고, 동일한 목표를 향해 보다 빨리 나아갈 수 있도록 하는데 큰 도움이 되기 때문입니다.


커뮤니케이션의 중요성은 아무리 강조해도 지나치지 않다는 것을, 본 지의 독자라면 모두 잘 알고 있을 것이라 생각하기 때문에 이 이상 길게 이야기를 이어나가지는 않겠습니다. 대신 helloworld의 조직변화와 이에 따라 커뮤니케이션을 보다 원활하게 구현하기 위해서 인프라를 어떻게 구성했는가에 초점을 맞추어 커뮤니케이션에 대한 이야기를 좀 더 이어나가 보겠습니다.


조직의 변화 그리고 커뮤니케이션 인프라

현재 helloworld는 경영팀, 기획팀, 개발팀, 디자인팀 4개의 팀으로 구성되어 있지만,

지난 2011년 초 시점에는 경영/기획팀, 개발팀 2개의 팀으로만 구성된 조직 이였습니다. 아이디어 하나만 가지고 구체적인 지식 없이 시작한 스타트업 이였던 만큼 구성원 모두가 기획도 하고 동시에 개발도 진행하는 등 그나마 2개라고 나눠났던 팀 사이의 업무경계도 모호하던 시절 이였습니다. 개발 역시 혼자서 거의 모든 것을 처리하던 터라, 개발자간의 업무분담도 고민이 되지 못하던 시절입니다.

이 때는 팀원 간의 커뮤니케이션이 크게 부각되지 않았습니다. 개발자 스스로가 기획까지 하던 터라 무엇을 만들어야 할지 본인 스스로 가장 잘 알고 있었으며, 개발 이슈를 공유할 동료가 없었기 때문입니다.


그렇게 2011년 여름까지 프로토타입이 개발되었으며, 이를 통해 본격적인 비즈니스를 전개하기 위해 인원확충이 이루어졌습니다. 필자가 helloworld에 합류한 시점도 이때입니다. 그리고 2011년 8월 대표 겸 기획자 1명, 개발자 3명으로 helloworld법인이 설립되었습니다.

이 때도 사실 2011년 초와 비교해보았을 때 조직 체계가 크게 변동되지는 않았었습니다. 다만, 개발팀의 인원이 충원되며 개발팀에서 역할분담이 보다 세분화 되었다는 차이점은 있었습니다.

<화면 3. helloworld의 조직 체계 변화>


<화면 3>처럼 helloworld에서 개발팀 역할분담은 서버 등 플랫폼개발, 안드로이드개발, 아이폰 개발으로 세분화되었습니다. 이전에 개발자 혼자서 거의 모든 걸 담당했던 때와 비교와는 비교할 수 없는 정도로 각 개발 분야의 전문화, 효율화를 달성한 것입니다. 그러나 혼자서 개발하는 것에 추가적으로 2명 이상의 개발자가 참여하는 것은 프로젝트에 새로운 복잡도를 부여하는 계기가 됩니다.


그에 따라 프로젝트에 참여하는 개발자간의 커뮤니케이션을 고도화할 필요가 있었습니다.

이슈 트래커, 소스코드 버전관리와 같은 프로젝트 형상관리 솔루션이 필요하게 된 것입니다. 시장은 다양한 형상관리 솔루션을 제공하고 있었고, 우리는 상황을 고려해 사용하기 적합한 솔루션을 선택하기만 하면 되었습니다. 구글코드, nforge와 같은 서비스는 별도의 호스팅 서버나 시스템 구축 없이 바로 사용할 수 있다는 장점이 있었으나, 개발 데이터를 외부 서버에 두기를 원하지 않아 자체적으로 솔루션을 구축해야 했습니다. Trac, Redmine같은 오픈소스 도구가 후보군에 올라왔고, 최종적으로 Redmine을 사용하게 되었습니다. 사실 Trac과 Redmine 등의 도구들 사이에 큰 차이는 없었으나, 가장 최근 Redmine을 직접 구축하고 사용해본 저의 개인적 의견이 강하게 작용한 결과였습니다.


그렇게 <화면 4>에서처럼 윈도우 2008 서버에 Redmine과 SVN서비스를 설치하고, 추가적으로 SMTP서비스를 설치해 각종 이슈의 추가/변경 등에 관한 상항을 관계자들에게 실시간 이메일로 알릴 수 있는 환경을 구축하였습니다. 이렇게 나름 적은 비용을 통해 형상관리 솔루션을 도입하고, 잘 사용하기만 하면 됬는데, 생각처럼 모든 일이 그렇게 생각처럼 쉽게 풀리지는 않았습니다.


효과적인 커뮤니케이션을 위해서는 물론 그것을 잘 서포트해 줄 수 있는 도구도 중요하지만, 가장 중요한 것은 구성원들의 참여의지입니다. 아무리 좋은 도구라 할지라도, 그것을 잘 다루지 못하거나, 잘 모른다 하더라도 그것을 잘 다루기 위한 지식을 배우려는 의지가 없다면 전혀 쓸모가 없게 됩니다.


helloworld에서의 문제도 바로 이것 이였습니다.

Redmine을 운영하고 있었지만, 그것을 제대로 사용하기 위한 지식과, 참여의지가 부족했었습니다. 쓰는 사람만 사용하게 되고, 잘 사용하지 않는 사람은 Redmine에 먼가를 기록하는 것은 귀찮은 작업이라 여기며 그냥 지나치기 일쑤였습니다.

이 문제의 해결방법으로 Redmine에 기록을 남기는 것도 개발 업무의 일부라는 강제성을 부여해 억지로라도 Redmine에 기록하는 것을 습관으로 만들어야 한다고 결론을 이끌어 내기는 했습니다.

<화면 4. 이메일을 통한 실시간 이슈 알림>


Redmine에 등록되어 있는 이슈가 바로 그 사람의 업무 성과를 판단하는 기초이며,

Redmine에 없는 업무는 수행한 업무가 아니다 라는 규칙, 그리고 Redmine을 통해 기록을 하지 않는다면 별도의 문서화 작업을 반드시 수행해야 한다는 규칙이 바로 그것입니다.


하지만 이것으로 모든 문제가 완전히 해결되지는 않았습니다.

강제성이라지만, 사실 아직까지 이러한 문제를 가지고 구성원에게 어떤 불이익을 행사한 적이 없는데, 불이익에 따른 구성원간의 갈등이 깊어서 이탈까지 이어지는 사태를 원하지 않기 때문입니다. 또한 아직까지는 구성원들 사이의 신뢰를 바탕으로, 규칙을 자율적으로 이행할 수 있는 수준의 조직문화를 만들어 가는 단계라 생각하고 있었기 때문입니다.


사실 지금은 수평적 관계를 지향하는 스타트업에서 강제성은 큰 효력을 지니고 있지 못하다고 생각을 바꿔하고 있습니다. 만약 반대로 스타트업과 달리 수직적인 역학관계가 명확한 환경의 기업이었다면, 강제성은 분명 효과가 있었을 것입니다.

어찌되었든 현재 Redmine의 운영은 개발을 진행하는데 꼭 필요하고, 단 1명이라도 공유가 필요한 부분이라면 반드시 기록을 남기고 있으며, 지엽적인 부분에 대해서는 담당자의 자율적인 판단 하에 기록을 남기도록 하고 있습니다.

Redmine을 완전히 정착시키기 위해서는 좀 더 시간과 구성원들의 노력이 필요할 것으로 보고 있지만, 그 필요성을 모두가 인식하고 잘못된 점을 개선해 나가려는 의지와 액션이 있기 때문에 멀지 않은 미래에 목표를 달성할 수 있을 것으로 보고 있습니다.

커뮤니케이션의 확장

자 하지만, 여기서 또 다시 고민해봐야 하는 문제가 하나 있습니다. 바로 개발자들만의 커뮤니케이션뿐만 아니라, 조직 전체적인 커뮤니케이션 처리는 어떻게 해나갈 것인가 하는 질문입니다. 필자는 처음 순진하게도 Redmine을 통해 모든 구성원의 커뮤니케이션과 의사결정을 통합하려는 시도를 했었습니다. Redmine 자체는 충분히 다양한 확장기능을 제공하고 있기 때문에 문제는 없어보였습니다. 그러나, 단 한 가지 모든 종류의 커뮤니케이션을 통합하는데 결정적인 문제는 Redmine자체가 너무 개발자 친화적이라는 것 이였습니다.

기획자와 디자이너의 눈에 비치는 Redmine은 너무도 복잡해 조작할 수 없는 기계와도 같습니다. 십중팔구 Redmine을 기획자와 디자이너에게 처음 보여주고 사용하라면, 너무 어려워서 사용하지 못한다고 말할 것입니다. 사실 저도 Redmine의 인터페이스에 익숙해지기에는 어느 정도의 시간이 필요했었습니다.

사장쯤 되는 위치의 사람이 기획자와 디자이너도 모두 Redmine을 쓰라고 강제성을 부여한다면 억지로라도 사용할지는 모르겠지만, 아무튼 옆의 개발자 동료가 회의시간에 Redmine을 기획자와 디자이너 모두 사용하라고 해서 쉽게 사용하지 않으리라는 것은 명확해 보입니다.


helloworld는 지속적인 인재영입을 통해 현재 대표 1명, 기획자 1명, 디자이너 2명, 개발자 4명으로 인원이 불어나게 되었고 4개의 팀이 존재하고 있었습니다. 따라서, 개발팀에서 사용하는 Redmine외에 전반적인 커뮤니케이션을 관리할 수 있는 수단이 더욱 요구되고 있는 상황 이였습니다. 그리고 생각 끝에 얻은 결론은 private SNS를 구축하는 것 이였습니다.

페이스북, 트위터로 대표되는 오늘 날의 SNS은 IT관련 종사자는 물론, 비 IT계열 종사자까지 폭넓게 사용되고 있습니다. 많은 사람들에게 사랑을 받고 있는 만큼 기존의 SNS와 같은 방식을 통해 조직 내 구성원간의 커뮤니케이션을 권장하는 것은 큰 무리수가 없을 것으로 판단했습니다. 다만, 조직 내 보안유지를 위해 페이스북같은 공용 SNS보다는 사내에서만 따로 사용 가능한 private SNS가 필요했고, 자연스레 Quik(www.quik.co.kr)이라는 서비스를 통해 private SNS를 도입하였습니다.


Quik은 Yammer를 벤치마킹하고 한국형 기업용 SNS를 표방하고 있는 서비스입니다. Quik를 제작한 Always라는 스타트업의 대표와 기획자가 친구이기도 해서, 최소한의 비용으로 Quik를 도입해 사용할 수 있다는 것도 장점이 되었습니다. 그렇게 Quik의 도입 결과 예상대로 모든 팀원 간의 자연스러운 커뮤니케이션이 보다 활성화된 것을 가시적으로 확인할 수 있었습니다. 아이폰과 안드로이드 모바일 앱도 지원하고 있던터라 사무실과 집 등 공간적인 제약을 뛰어넘어 언제 어디서든 서로 자연스럽게 의견을 주고 받을 수 있다는 점이 정말 유용했습니다.


아쉬운 점은 자료검색, 근태관리, 구성원들 사이의 일정 동기화, 의사결정체계 등을 지원하는 프로세스가 미비하다는 것입니다. 그래서 요즘은 구성원들의 근태관리, 지출결의, 의사결정체계 등 기업에 기초적으로 요구되는 프로세스를 구현해 줄 수 있는 그룹웨어를 도입하려고 알아보고 있는 중이기도 합니다. 하지만 그룹웨어의 도입이 private SNS의 사용중단을 의미하지는 않습니다. 그룹웨어와 private SNS는 상호보완적으로 운영되어 나갈 것입니다.

서비스 개발

helloworld는 지금까지 3개의 서비스를 개발하고 2개의 서비스를 운영 하고 있습니다. 이 중 ‘철가방‘ 이라는 서비스는 helloworld의 창업 아이템으로 시작해 몇 번의 우여곡절을 겪고, 현재 시즌2 런칭을 앞두고 있는 상태입니다. 2011년 여름까지 개발자 1명이 프로토타입을 개발했다는 내용을 앞서 언급하기도 했는데, 그 프로토타입이 바로 철가방 이였습니다.

<화면 5. 철가방 메인 화면>


철가방 서비스의 기획계기는 학교 점심이나 저녁시간에 무엇을 시켜 먹을까 고민하다가 시작된 서비스입니다. 현재 철가방과 유사한 대표 어플로는 ‘배달의 민족’, ‘배달통’ 등이 있는데, 철가방 아이디어를 가지고 사업계획서를 쓰는 시점에서 이들 어플이 막 런칭되고 있던 시기였습니다.


그리고 현재 이들 어플 중 가장 잘 나가고 있는 것이 배달의 민족인데 최근 정보에 의하면 약 대규모의 펀딩을 통해 자금을 확보하고 보다 공격적으로 비즈니스를 전개해나가고 있는 것으로 파악하고 있습니다. 배달통도 역시 어느 정도 자금 확보를 통해, 서비스를 지속적으로 업그레이드 해나가고 있는 상황입니다. 배달통과 배달의 민족은 서비스 모델의 거의 유사한데, 배달음식점을 가맹점으로 유치하고, 어플 사용자를 전화로 연결해주는 방식입니다.


철가방 역시 배달음식점을 가맹점으로 유치하는 것은 경쟁 어플들과 같지만, 어플 사용자들이 전자결제를 통해 전화 없이 주문이 가능하다는 것이 가장 큰 차이점입니다. 주문방식의 차이점은 앞으로 비즈니스를 전개해 나가는데, 큰 경쟁력을 지닐 수 있게 합니다. 당장 현재의 상황만 높고 판단해 봐도, 주문경로의 다양화와 주문금액 할인과 같은 이벤트를 유연하게 적용할 수 있는 것입니다.


<리스트 3. 철가방 경쟁 어플 비교 분석>

주문

중개방법

포인트 적립

월 주문 건

주문경로

누적

다운로드

자금회수

배달의 민족

전화걸기

없음

약 60만 콜

앱 주문

100만 건

자동이체

(미수발생)

배달통

전화걸기

없음

약 50만 콜

앱 주문

150만 건

자동이체

(미수발생)

배달114

전화걸기

없음

자료없음

앱 주문

50만 건

자료없음

철가방

주문/결제

할인 및

포인트 적립

약 1만 건

G마켓

11번가

철가방웹/앱

10만 건

현금&모바일 결제


그래서 경쟁 업체들이 펀딩이후에 주력하고 있는 부분도 바로 전자결제를 지원할 수 있도록 서비스를 업그레이드 해나가는 것입니다. 이들 서비스가 전자결제가 가능하도록 서비스 업그레이드를 완료하는 시기를 약 3개월 정도로 예상하고 있는데, 저희 helloworld는 이 3개월의 시간을 어떻게든 잘 활용하여 철가방 서비스를 성공적으로 시장에 안착시키고 일 주문량을 현재의 10배로 끌어 올리는 게 현재의 가장 큰 목표라 할 수 있습니다.


앞으로 배달 어플의 성패는 전자결제의 지원여부와 지역 가맹점의 숫자에 달려있다고 할 수 있는데, 전자결제 자체는 이니시스나 KCP와 같은 외부 모듈을 써야하므로 기술적으로 진입장벽은 전혀 없다고 할 수 있습니다. 결국, 지역 가맹점을 가능한 빨리 그리고 많이 확보 하는 게 이 사업의 핵심이라 판단하고 있습니다.

하지만 2011년 여름 최초 프로토타입 개발 이후, 비즈니스를 본격적으로 전개해 나가고자 했을 때, helloworld가 지니고 있는 것은 앱 제작과 배포, 가맹점 프로그램 개발 능력을 제외하고는 아무것도 내세울게 없는 상태였습니다. 시장에 대한 경험은 아주 없었고, 가맹점을 모집할 수 있는 영업력이나, 자금력도 없었습니다. 고민스러운 상황 이였습니다.


그래서 그때 helloworld는 철가방에 대한 서비스 운영권을 포기하며 가맹점 DB와 영업력을 지닌 기업과 업무제휴를 맺고, helloworld가 그 당시 가장 잘 할 수 있는 앱 개발에만 몰두하기로 결정을 내렸었습니다. 업무제휴는 2011년 10월에 최종 계약을 맺게 되었고, 약 2개월의 개발 기간을 거쳐 현재의 철가방 1.0 버전의 앱을 배포하였습니다. 그 당시 자세한 계약내용은 지면을 통해 밝힐 수는 없지만 운영권을 포기하는 대신 앱 개발 비용과 더불어 일정부분 수익분배도 약속 받았고 저희 helloworld로서는 그리 나쁘지 많은 않은 계약 이였었습니다.


그렇게 철가방 앱 런칭이 후 가맹점 영업, 마켓팅 등을 제휴사에서 진행하는 것을 관망하며

배울 것은 배우고, 버릴 것은 버려가며 비즈니스에 대한 감각을 키워나갔으며, 외주 개발을 통해 추가적인 자금을 확보하는 등 내실을 다지는 시간을 가졌습니다.

내실을 다지는 기간 동안 특히 여러 포럼이나 세미나를 쫓아다니며 다른 스타트업의 대표들, 벤처 투자자, 기업 관계자들과 최대한 많은 인연을 만들고자 노력했는데, 그 결과 helloworld의 사업모델에 관심을 가지고 투자를 이끌어준 인연을 만들 수 있었던 기간이기도 합니다.

이처럼 인맥의 힘이란 우리나라에서 아직까지는 정말 중요하게 작용하고 있는데, 기술자로만 구성된 스타트업들이 겪는 어려움 중에 하나가, 다양한 인맥을 쌓고 그것을 활용하는 능력이 모자라기 때문입니다. 어렵게 돌아가야 하는 길을 인맥을 통하면 쉽고 빠르게 갈 수 있는 경우가 생각보다 많습니다.

필자는 helloworld내에서 개발팀을 책임지고 있어서 이 기간 동안 밖에서 많은 시간을 보내지는 못했지만, 저희 hellowolrd의 대표는 이 기간 동안 정말 많은 사람들과 인연을 쌓는데 노력했고, 그 결과가 투자까지 이어진 것이라 생각합니다. 특히, 앞으로 스타트업을 생각하고 계신 분이라면, 최대한 많은 사람들과 인연을 만들어 가는 것을 결코 소홀히 하지 마시길 바랍니다.


비전

2011년은 helloworld가 새로운 변화를 맞이하며, 도약을 꿈꾸는 해입니다. 사실, 필자를 포함한 멤버들 나이도 생각하고 해서, 어느 정도 가시적인 성과를 이끌어내 지속가능성에 대한 의문에 명확한 답을 내려야 하는 시기이기도 합니다. 그리고 우리는 그 해답을 철가방 서비스에서 찾고 있습니다. 철가방 서비스의 운영권은 제휴사에 모두 지니고 있는 것인데, 어째서 우리의 미래를 철가방에서 찾고 있을까요?


답은 간단합니다.

철가방 서비스 운영을 맡고 있던 제휴사가 사실상 경영이 어려워졌기 때문입니다. 저희 helloworld 쪽에 지급해야할 개발비용도 모두 상환하지 못한 상태입니다. 그래서 자연스레 철가방 서비스에 대한 운영권도 helloworld가 소유하게 되며, 실질적으로 개발부터 영업, 운영에 이르기까지 철가방에 관련된 모든 비즈니스를 helloworld에서 소유하고 주도할 수 있게 되었습니다.


제휴사의 미래가 불투명하게 된 것은 철가방 서비스 모델이 아직까지 충분하지 검증되지 못한 것 아니냐고 묻고 싶은 분이 계실지 모르겠습니다.


앞서 배달음식 중계 서비스의 핵심은 전자결제와 지역 가맹점고, 이 두 개중 더 중요한 것이 지역 가맹점 확보라 언급했었습니다. 철가방의 업무 제휴사가 어려워진 이유는 한정된 리소스를 보다 효율적으로 사용하지 못해서라고 분석하고 있는데, 지역 가맹점 확보를 전국에서 동시 다발적으로 진행하려다 보니 스스로 자금 압박에 무너진 꼴이 된 것입니다. 즉, 선택과 집중의 실패입니다.

helloworld는 이전에도 그랬듯이 우리가 가장 잘할 수 있는 일은 직접 처리하고, 부족한 부분은 그것을 가장 잘 할 수 있는 사람이나 조직에 맡기고, 발생하는 이익을 나눠가지려 생각합니다. 지역 가맹점들을 효율적으로 설득하고 움직일 수 있는 사람들을 통해 영업을 전개해 나가려는 것입니다. 그리고 실제 지금 이 순간에도 그런 사람들이 지역 가맹점 확보를 위해 움직이고 있습니다. 물론 앞선 실패를 반복하지 않기 위해, 서울지역의 대학가 위주부터 시작해 가맹점 밀집 지역을 확장해 나가는 전략을 사용할 것입니다.


배달음식 중계 서비스 시장은 이미 유럽에서도 성공사례가 나타나고 있습니다.

그러고 보니 2011년 독일에서 사람이 찾아와, 20억 정도의 투자검토를 한 후 돌아가기도 했었는데, 지난 2011년 11월 독일의 delivery hero가 약 8백만 유로(약 120억원)의 투자를 받았다는 사실을 확인했습니다.(화면 6) 국내의 경우 배달의 민족 같은 서비스에 거액의 자금이 투입되고 있으며, 오픈마켓 업체에서도 많은 관심을 기울이는 분야입니다.

배달음식 시장은 아직까지 IT 인프라 확산에 따른 변화가 거의 없는 분야인 만큼, 분명 기회가 있습니다. helloworld는 바로 여기서 2011년 한 해 동안 의미 있는 결과를 만들어보고자 합니다.

<화면 6. 독일 스타트업 딜리버리 히어로>

출처 : http://venturevillage.eu/newsdeliveryhero-lieferheld-financing-round


Smart liefe with helloworld

최근 패스트트랙아시아에서 좋은 의사를 찾아주는 서비스 굿닥을 발표했습니다.

사실 helloworld에서도 굿닥과 비슷한 종류의 서비스를 기획하고 있습니다. 철가방처럼 앱을 통해 병원에 진료예약을 할 수 있는 서비스입니다. 또한, helloworld는 국내 최고의 여행사에서 10년 이상 몸담은 국내 여행 전문가와 좋은 인연을 가지고 있고, 이 사람을 통해 여행 예약관련 서비스를 개발하고자 합니다.

이처럼 현재 helloworld의 철가방 시스템을 궁극적으로 음식, 병원, 여행 등과 같이 사람들의 생활과 밀접한 관계를 갖는 분야를 IT에 접목시켜 쉽게 확인하고 예약할 수 있는 플랫폼으로 만들어갈 것입니다. 2011년 이후 계속해서 helloworld가 살아남아 성장하고 있다면, 머지않은 미래에 충분히 가능한 일입니다.

Posted by devop

댓글을 달아 주세요