프로그래밍 언어에 대한 글을 쓴다고 했지만… 학업과 회사일을 병행하면서 프로그래밍 언어에 대한 글까지 쓰는것은 사실상 불가능에 가까웠기 떄문에,


그리고 현업에서 일을 하는  도움이 되는 글을 쓰는 것도 좋으리라고 판단했기 때문에 지금까지 우리 회사가 어떤 기술적인 선택을  왔는지 설명하는 글을  보려고 합니다 아래로는 반말로 작성합니다(사실 미리   글들을 대충 정리하고 그대로 옮겨  거라 그렇습니다).


TL;DR

1. 스타트업에서 중요한 것은 적절한 수준, 빠른 기능 배포, 그리고 시기에 맞는 변화를 위한 유연성에 대한 사전대응

2. RDBMS라는 선택지는 회사의 수요와 기술의 장단을 따져 잘 도입했다.

3. Hasura의 도입은 추후의 비즈니스 성장을 좌지우지 한 가장 중요했던 결정

4. Flutter의 도입은 거슬리지는 않지만 여전히 아쉬운 부분이 있다.

5. Firebase remote config와 AWS의 도움으로 빠르게 기술 부채를 상환했다.

6. We are hiring


서론우리 회사는 무엇을 해야 하는가.


스타트업은 필연적으로 다양한 문제를 해결해야 한다그것은 남들이 이미 해결한 것일수도 있고우리만이 해결할  있는 문제일수도 있다후자의 경우 보통  스타트업의 핵심 사업 아이템이 된다전자의 경우는 기술적인문제가 된다.


그렇다애석하지만 기술 중심 스타트업이 아닌서비스 중심의 스타트업인 이상 기술 문제가 스타트업의 중심이되긴 힘들다 경우 기술 문재는 대개 중심 문제를 해결하기 위한 도구에 지나지 않게 되고따라서 기술 문제는대개 빠르게 해결하고 넘겨야 되는 중간 걸림돌 이상의 의미를 갖기가 힘들다바꿔 말하면  경우사업 아이템이기술적으로는 구현하기 어렵지 않기 떄문에 다른 경쟁자가 시장에 진입하기 이전에 최대한 해결 수준의 격차를 넓히는 것이 중요하다 말은  빠른 배포가 중요하다는 뜻이다.


 중요하게 기억해야  것이 있다스타트업의 경우 제대로 성장하고수익이나 투자금을 통해 추가로 구성원을고용하는 것이 가능해지기 전까지는 CTO(그러니까 ) 회사의 유일한 기술 담당자가 되고서버부터 클라이언트에 이르기까지  많은 문제를 최적의 성능보다는 용납가능한 품질로 최대한 빠르게최대한 적은 구현 비용으로 납품해야 한다는 것이다  가장 중요한 것이 회사의 핵심 문제를 올바로 파악하는 것이다그리고 그들에게서 핵심 기술 문제를 추출해내고 이들을 가장 낮은 비용으로 해결할 방안을 강구해야 한다.


서비스 중심의 스타트업일 경우 클라이언트에서부터 기획하는 것이 많은 경우에 유용하다이유는 클라이언트는유저들이 직접 접하는 단말(터미널) 되고이들과 상호작용하는 방법 자체가  서비스 자체를 규정하기 떄문이다물론 백엔드는 중요하고 서비스 규모가 커질수록  중요도가 매우 커지지만초기 단계에서는 대규모의 트래픽을 담당해야  일이 별로 없다망하거나 문제가 발생하는 것도 일단  궤도에 올라가야 고민할  있는 문제인것이다.


(구체적인 기획은 삭제)


여기서 우리 서비스는 다음과 같은 기술적 문제를 해결해야 함을   있다.

1. 복잡한 데이터 구조를 핸들링   있는 모델의 구현법

2. 서비스의 변경이 매우 잦을 경우 이를 핸들링할  있는 서버 API

3. 가성비 높은 백오피스 구현

4. 사용자에게 최대의 만족감을 주면서 다양한 플랫폼을 대응할  있는 모바일  개발론

5. 외부에서 사용자 유입을 위한 웹뷰 구현(유니버셜 링크   링크에 대응하는 웹뷰 구현을 위한)


서버의 스펙부터 결정: GraphQL


스타트업에서 가장 중요한 것은 보통 클라이언트의 문제를 해결하는 것에서부터 시작한다고 했고 경우 클라이언트와 서버  관계를 어떻게 지어주는가를 고민하는 것이 매우 중요하다이는 빠른 개발을 위해서도 중요하지만서비스 전체의 모델은  서비스를 구현하는 데이터의 구조와도 같다문서화는  문제를 해결하는 가장 왕도적이고 훌륭한 방법이지만개발 기한을 맞추면서 문서를 동시에 작성하기는 힘들고개발이 끝난 서비스 구성요소를 추후에 문서화하는 것은 사실상 불가능에 가깝다 경우 차선으로 택할  있는 것이 바로 서비스의 구현 자체가 문서화가 되도록 하는 것이다 경우 클라이언트 개발자와 서버 개발자 모두 클라이언트가 서버에 요구하는 데이터의 모델을 어떻게 규정하는가를 보는 것이 추후에 서비스를 파악하는 출발점이 된다.


현재 시장의 주류가 되는 것은 REST API이다(적어도 한국 시장에서는 그렇다). HTTP 프로토콜의 기본 구성요소로 기능의 상당 부분을 정의할  있으니 표준 자체가 견고하기도 하고범용성이  방법인 만큼 구현을 위한 방법론도 매우 많다(다양한 언어와 다양한 프레임워크를 사용할  있다). 하지만 매우  단점이 존재하는데그것은 바로 서버의 기능성을 모두  구현해야 한다는 점이다.


서버를 서비스에서 직접 구현하는 것은 당연한  아니냐고   있다맞는 말이다그러나 우리 서비스의 경우굉장히 많은 모델 구성요소를 필요로 했고이들에 대응하는 기능성을 일일이  개발하면서 기한 내로 출시하는것은 사실상 불가능에 가까웠다또한 변경이 잦은데 서버와 클라이언트를 동시에 개발하는 것은(그것도  명이작업 효율과 주의집중의 context switch비용까지 고려했을  여러모로 현명하지 않았다.


때문에 서비스의 데이터 구성요소와 이에 대한 접근 자체를 외부에 노출하는 GraphQL 여러모로    보였다데이터만  정의한다면 서버 API개발 자체는 사실상 끝나는 것에 가깝다개별 구성요소에 대해서만 동작을정의하면나머지는 GraphQL 서버 엔진 구현체들이 알아서 처리해준다하지만 여기에는  함정이 있다컴퓨터의 자동 로직은 많은 경우에 똑똑하지 않고, GraphQL구현체 자체도 시장에 나온지 오래되지 않아 성숙하지 못했다이에 따라 복잡도가 커질수록 서버 개발이 일반적인 개발 방법론보다도 훨씬 비싸진다.  따라서  문제를 제대로 해결하려면 다양한 메모리 층위와 알고리즘 최적화를 통해 문제를 해결하는 방법이 있고자본을 부어서 해결하는  다른 방법이 있다회사가 대규모라면 전자의 방법을 택했겠지만 스타트업에게 시간은  무엇보다 비싸다따라서 우리의 선택은 후자가 되었다.


그러자 AWS Amplify에서 제공하는 Data 나쁘지 않게 보였다연산에 필요한 자원을 프로비저닝할 필요가 없고사실상 무한에 가까운 자원을 활용한다는 클라우드의 장점을 최대한 활용하는 클라우드 컴퓨팅의 장점을 십분활용할  있으며이들의 동작성은 AWS 보장한다는 데에서 더욱 그렇다그러나 해당 서비스를 조사하면서 한계를 매우 많이 발견했는데   번째는 개발하기 편한 만큼 제약사항도 매우 많아 우리가 원하는 수준의 복잡도를 구현하기 위해서는 중간에 다양한 AWS Lambda 구현해야 했으며이는 서버 지연시간으로 보나 구현복잡도를 보나 그다지 타당해 보이지 않았다.


AWS Lambda 내가 대학교 2~3학년때 쯤에 갑자기 시장에 등장해서 많은 관심을 받았다그러나 소비자들은곧 이를 외면하기 시작했는데가장  이유는 개발의 난해성이었다.


AWS Lambda 자원 프로비저닝(미리 할당) 필요가 없다는 것을 강점으로 내세워 자원관리에 지친 여러 사업자들을 유혹했다그러나 이는 바꿔 말하면 배포 환경이 어떻게 될지 개발자는 모른다는 뜻이 된다따라서 배포 이후에 개발 환경에서 발생하지 않은 이슈들이 매우 많게 되었고이를 해결하기 위한 디버거도하다 못해 콘솔 기반의 디버깅을 하는 것도 불가능에 가깝다(물론 cloudwatch log 통해 상당부분 해결할  있지만). 따라서 프로비저닝 이외에 모든  불편한 괴상한 서비스가 되었고 개발자들의 외면을 받았다현재도 Lambda 보통AWS 다양한 관리형 서비스를 중간에 매개하는 글루 코드 정도의 역할을 하고 있다.


Lambda  다른 단점이 있는데바로 실행 빈도에 따라서 반응 속도가 다르다는 것이다. AWS lambda 설명문을 처음에 읽으면마치 서버 로직은 AWS 존재하고 언제나 최적의 속도로 실행될  처럼 느껴진다하지만 실상은 매우 다른데실행 빈도가 낮은 함수는 AWS 콜드 상태 배포 중단 상태로 둔다 실행되지 않는자원을 항상  두는 것은 AWS입장에서 극악의 효율을 보이기 때문이다(실행 시간에 비례해 lambda 과금하기 때문에 그렇다). 따라서 자주 쓰이지 않는 로직의 경우 반응 속도가 느리고적당한 빈도로 실행되는 경우  함수가 cold stage 들어가는 것을 막기 위해 주기적으로 해당 함수를 call 해야 하는데 이는 자본이나 개발 구성요소간 연결성을 파악하는  있어서도 매우  낭비이다따라서 AWS amplify: data 최종적으로 선택하지 못했다.


이후로 prisma ORM GraphQL 구현을 섞은 다양한 구현체들을 검토했고대체로 비슷한 모델을 제시했지만 전부  기한 내로 구현하는 것은 불가능해 보였다(우리 서비스를 기획하고 구현하는  주어진 시간은  6개월 정도였고기획 단계에서 이미 2달이 소비되었다). 그러던 와중 정말 다행스럽게도, Hasura라는 기술을 발견하게 되었다.


Hasura: 스타트업은 언제든지 유연하게 변경할 여지를 남겨야 한다.


Hasura 도입하던 시점에서의 기술적 기반은 다음과 같다. PostgreSQL RDBMS + Haskell API구현체현재는 서비스가 더욱 발달해 MSSQL, MySQL, BigQuery 다양한 Data source 연결할  있지만  시점에서도 가장 중대한 기술적 기반은  두가지다.


내가 대학교를 다닐 떄는 RDBMS 마치 구시대의 산물처럼 사람들이 떠들어대던 시기였다그러나 나는 이런 시각에 동의하지 않는다물론 훌륭한 NoSQL 많이 등장한 것도 사실이다(개인적인 견해인데, NoSQL 메타버스나 UX, 햅틱처럼 사람들이 제대로 정의내리기도 전에 먼저 남용되어버린 버즈워드  하나라고 생각한다). 그러나 만능의 기술적 선택은 존재하지 않는다만약 그런게 있었다면 시장은 이전 세대를 완전히 버리고 빠르게 다음 세대로 이주했을 것이다어떤 기술에건 학습 난이도의 측면이건성능의 측면이건비용의 측면이건 단점은 존재한다. NoSQL에도 다양한 단점들이 존재한다   번째가 무결성 검증의 난해성 번쨰가 조직의 실수에대한 대응책 전무라는 점이다.


NoSQL에는 다양한 종류가 존재하지만  가장 대중적인 형태가 Key-value 저장소 형태다른 하나가 문서기반의 구현체  가지다전자의 가장 유명한 사례로 카산드라후자의 형태로 가장 유명한 것이 MongoDB이다하둡 등은 오로지 분석을 위한 서비스로 보는 것이 가장 적절하기 때문에 서비스 구현을 위해서 채택하기에는 적합하지 않다.


  그나마 시장에서 많이 받아들여지고 있는 것이 MongoDB정도인데문서를 바로 저장하기 때문에 프론트엔드를 개발하는(그러니까 데이터 관점에서개발자가  한번의 실수를 하더라도 이를 되돌리기 매우 힘들다. RDBMS 말하자면 서비스 입장에서 데이터에 타입을 부여하는 것이고타입은 프로그래밍 언어 관점에서  없이 많은 예측 불가한 문제 중에 사전에 예측 가능한 것으로 바꿔낸   되는 사례이다이를 포기하면서MongoDB 갔을  얻는 이점은 새로운 컬렉션 추가(RDBMS에서는 테이블에 대응되는없이 서비스 변경하기의 용이함샤딩 정도인데(이는 엔진 자체의 장점에 가까웠다). 전자의 경우 저장은 쉽지만 후에 검색을 위해 인덱스를 다시 부여할  매우  다운타임을 요구해 불가능에 가깝고후자의 경우 후술하겠지만 대안 기술에 대체제가 존재하기 때문에 강력한 특장점이라고 보기는 힘들었다.


반면 RDBMS 데이터베이스에 일반적으로 기대하는 일관성의 면에서도서비스의 유연한 확장과 견고한 구조다양한 기능성 전체의 면에서 바라봤을때 우리 서비스에 가장 안성맞춤이라고 여겨 선택하게 되었다 MongoDB에서 취하지 못해 너무나 아쉬웠던 기능이 바로 샤딩이었는데놀랍게도 확장을 사용하면PostgreSQL 샤딩이 가능했다(가장 대표적으로 citus 있다). 물론 단일 DB에서 테이블의 덩치가 커질수록PostgreSQL 성능이 떨어지는 단점이 있긴 했지만(인덱싱 전략의 문제 때문이다), 샤딩이라는 해결책으로 이를 해결할  있어 보였기에 우리는 최종적으로 DBMS PostgreSQL 선택하기로 했다때맞춰 Hasura 발견한 것은 천운이었다.


Hasura Haskell이라는 함수형 프로그래밍 언어를 기반으로 구현되었다개발자가 데이터 타입을 선언하고(테이블과 스키마를 정의하고), 이를 기반으로 관계를 설정하고인증 토큰에 존재하는 값을 Session variable 사용해 사용자의 권한을 제한할  있다가장 중요한 것은 과정에서 코드를  1줄도 작성하지 않아도 된다는 것이다사실상 일반적인 서버 API 개발할  필요한 일들을 거의  핸들링해 주는 셈이다물론 이는 AWS data에서도 사용 가능한 것이다하지만 위에서 말했듯, Hasura PostgreSQL 사용하고 있었고이는 내가 원할떄는 언제든 필요한 기능을 덧붙여   있다는 뜻이다(SQL 범용성은 매우 높다). 거기다 RDBMS데이터 소스는 관리하기 힘들다면 언제든 RDS/Aurora 같은 완전관리형 서비스로 책임을 이관할  있었다.


 거기다 실시간으로 여러 소비자들에게 정보를 제공하기 위한 websocket기반의 subscription, 서버사이드 캐싱그리고 호환 client apollo 강력한 기능성을 고려하면사실상 서버구현비용을 0으로 내릴  있는 매우좋은 선택지로 보였다.


서비스를 런칭한  이제  2 정도가 되었다 동안 나는   순간도 서버사이드 기능구현에 어려움을 겪지않았다또한 Index 걸지 않았을 때를 제외하면 성능 이슈도 겪지 않았다아마 내가  스타트업에서 가장 잘내린향후 2년을 결정한 가장 훌륭한 선택이었던  같다또한 이를 결정하며 우리 스타트업의 기술적 기조도 결정할  있었다그것은 바로 ‘언제건 변경 가능한 유연성과 성능 사이의 절충점이다.


REST API 구현서비스는 내부 서비스만으로 구현되지 않는다.


REST API 구현하기 싫다면서 갑자기   이야기를 꺼내는 것인지 궁금한 사람들이 많을 것이다이를 이해하기 위해서는 간단하게나마 튜링 머신에 대해서 이해할 필요가 있다


튜링의 모델에 따르면컴퓨터는 수행할 작업과 이에 필요한 데이터가 기록된 무한의 테이프그리고 이를 읽고 처리하는 헤드  가지로 구성된다전자의 경우 컴퓨터의 핵심 기능  하나인 기억후자의 경우 처리를 의미한다그리고 이는 프로그래밍 언어에 대응했을  전자는 변수후자는 함수에 대응된다서버 또한 클라이언트의 요청을 처리하는 일종의 거대한 컴퓨터로 추상화해 생각할  있으며  전자의 기능성이 서버의 Database, 후자가 클라이언트의 요청을 처리하는 서버 API 대응할  있다


GraphQL특히 우리 회사의 기술적 선택을 고려하면 데이터 자체를 외부로 공개하는 것에 가깝다 서버 자체를 하나의 거대한 저장소로 취급하고이에 대한 동작 역시 클라이언트에서 규정하기 때문에 사실상 서버는 거대한 메모리로 모델링된 것으로   있다. REST API 이와 달리 서버에서 대부분의 동작을 정의하고 클라이언트에서는 정해진 동작을 다만 따르게 되기 때문에 오히려 서버는 거대한 함수 모음집으로 보는 것이 타당하다전자의 경우 클라이언트는 소비자에게 가까운 메모리 층위의 데이터와 동작을 동시에 처리/저장하고후자의 경우클라이언트는 단지 데이터만을 저장하고 서버에 구체적인 동작의 모음집들을 수행하도록 의뢰하게 된다.


따라서 단지 서비스 내부의 데이터를 변경하는 것으로 구현이 끝난다면  떄는 GraphQL만을 사용해도 충분하다그러나 점차 필수가 되는 SSO 대한 대응은결제 기능 구현은? SSO 구현시 애플 플랫폼에 따라붙을 Sign in with Apple ID?


외부 서비스의 경우 구체적인 데이터 모델을   없다단지 이들도  다른 REST API 정의할 뿐이다 동작을 중심으로 구동되는 서비스의 경우어쩔  없이 REST API 일부나마 필요한 것이다.


따라서 GraphQL REST API 사이에 명확한 구분기준을 만들었고그것은 ‘동작 중심으로 구현되는가, ‘데이터 중심으로 정의되는가였다이는 일반적인 경우에 ‘외부서비스를 사용하는가’ 여부에 따라 결정되었다.


다음으로 점검해야  사항은 서버 개발 언어와 프레임워크를 결정하는 것이었다당시 나는Go/Ocaml/Kotlin/Swift/Rust/C++(C  좋아한다로우레벨 건드릴  어쩔  없이 쓰는 쪽에  가깝다)/Python/JavaScript-TypeScript/Java등을 다뤄 봤었다일단 내가 만지기 싫은 C++ 제외했다(농담이고배포 시간과 언어 자체의 개발 난이도를 고려해 배제했다). 추후에 개발자를 구하기 힘들  같은Go/Ocaml/Rust 배제했다서버 개발에  쓰이지 않는 Swift 배제했다. Java 지나치게 레거시화되었고훌륭한 Kotlin이라는 대체재가 존재했기에 배제했다남은 것은 Kotlin, JavaScript, TypeScript, Python정도였다.


이쯤 되니 상당히 고민이 된다 언어 모두 시장에서 많이 쓰이고사랑받는(적어도 지지층은 확실히 )언어다   무엇을 골라도 개발자를 추가로 충원하기에도 좋으며성능 또한 모두 하한선은 검증된 상태다여기서 우리는 다시 요구사항을 명확히  필요가 있었다

  1. 빠른 배포
  2. 안정적인 개발
  3. 필요한 최신 기능의 빠른 도입

여기까지 정하고 나자 Kotlin Python 배제할  있었다코틀린은 물론 너무나 훌륭한 언어고 개인 프로젝트에서는 여전히 사랑해 마지않는 언어 순위에  들어가지만빌드를 필수적으로 필요로 하고 다른 언어들에 비해서는 한국에서 서버 개발에 쓰이는 빈도가 그다지 높지 않다고 여겼다. Python 데이터 사이언스에 지나치게 특화되어 있는데 반해 최근 서버 개발을 위한 기능들은 그다지 많이 발달하지 못했다.


따라서 최종적으로는 TypeScript JavaScript 남았는데 중에서도 후자를 선택했다 이유는 대부분의서버 로직은 Hasura만으로 핸들링이 가능했고안전성을 위한 코드를 배제하고 더욱 빠르게 개발할  있었으며필요하다면 TypeScript 우선 이주한  Typed  다른 언어로 이주하기에 매우 적합해 보였기 때문이다현재 서비스에는 JavaScript에서 Koa.js 사용해 서버를 구현했고여기에 인증  알림 발송을 위해 Firebase admin sdk, 결제를 위해 Iamport 등을 혼용해서 사용중이다카카오나 애플 아이디 등으로 로그인한 후에는JWT 발급하는 일도 REST API에서 한다.


후회하진 않지만 예측하지 못한 문제는 있었다바로 최근 버전에서 발생하기 시작한 Memory leak issue이다. Node.js환경을 업그레이드   문제가 발생했는데다만 타입에 관련한 코드를 간결하게 표현하기위해서는  필요했기 때문에 어쩔  없이 사용중이다해당 문제는 ECS fargate에서 메모리 leak 발생할 때대체 인스턴스(task) 시작하고 현재 요청은 마지막으로 처리한  해당 instance 꺼지게 하는 것으로 해결했다.


 대시보드: React admin과의 만남, vue-element-admin으로의 이전


 대시보드매우 힘든 일이다사용자 클라이언트 개발은 돈을 벌어 오고서버 개발은 돈을 아낀다하지만 관리자층을 위한  대시보드는 직접적으로 수익을 창출하지도 않고반면에 가장 방대한 기능성을 요구하기 때문에무척 개발하기 까다로운 일이다 문제는 어떻게 해결해야 할까?


마찬가지로 Hasura 같이 개발 없이 서비스를 구현할  있는 방법을 찾았다놀랍게도 있었다. React admin이라는 오픈소스 프로젝트는 React.js, Redux, Redux-saga, React-router  이미 유명한 React 기반의 기술들을 기반으로, Django admin 같이 데이터 모델을 명기하면(여기서는 Data provider라고 불렀다데이터를 직접적으로 핸들링할  있는 멋진 프로젝트였다거기다 기본 디자인 컴포넌트로 Material design 채택하고 있었는데개인적으로 머터리얼 디자인 가이드라인은 사용자 관점에서 그다지 훌륭하다고 생각하지 않지만 디자이너 없이 최소 한도 수준의 디자인을 만들수 있다는 점에서는 괜찮은 선택이었다.


문서를 열심히 따라하다 보니 초기 구현체를 만드는데는  3 정도가 소요된  같다(물론 코드는 개판이었지만그래도 괜찮았다그래도 우리는 코드 구현부를 최대한 줄였으니까). 이후에도 디자인이 예쁘지 않다는 불만은자주 나왔지만 어차피 회사에 디자이너가 있는 것도 아니었고(그래서 심지어는  초기 디자인도 내가  했다), 쓰기 불편하다는 요구사항도 직접 결제를 하지 않는 소비자들을 대상으로 하는 제품이니만큼 거의 무시했다(카카오페이 대시보드도 구리긴 마찬가지다). 그러나 점점 서비스에 들어와야  벤더가 많아지며 대시보드의 불편함이사실상 서비스의 성장을 가로막게 되며우리는 서비스를 구현할 새로운 기술적 대안을 찾아야 했다.


앵귤러와 리액트를 싫어하니 클라이언트의 -모델을 담당하는 라이브러리의 선택지는 사실상 vue.js밖에 남지않았다조금  자세히 설명하면컴포넌트 단위로 모델-스타일-컨트롤러를 분리해서 보기에도반응형으로 데이터의 변화와 드로잉을 분리하지 않고 생각하기에도 Vue 모델이  나아보였다(일단 react.js 너무 verbose하다최종 사용자를 위한 제품이 아님을 다시 상기하자).


그래도 서비스를 구현하는 난이도를  낮추고 싶었고유용한  컴포넌트와 어드민 대시보드를 구현하는데 필요한 기본 구조는 갖춘 기술을 원했고 결과 vue, element-ui, vue-router 결합한 vue-element-admin이라는 라이브러리를 찾았다.


기본 골격은 사실상 react admin 유사하지만기존에도 훌륭한 기능성 덕분에  활용해  element-ui 사용했다는 점이 무척 만족스러웠다(사실 지금에야 와서 말하는 거지만단순 서버 쿼리로 해결되는 기능성을 구현하는 데는 vue기반의 코드를 작성하는 것과 react admin 개발 난이도에  차이가 없었다).


 클라이언트: Flutter, 가장  도박


앞서 말했듯서비스 중심의 스타트업은 클라이언트 개발이 다른 무엇보다도 중요하다(최소한 나는 그렇게 생각한다).  때문에 우리는 클라이언트 개발에 다양한 선택지들을 시험하고   우리 회사의 기술적 수요에 가장 적합한 것을 선택해야 했다이를 위해 플랫폼부터 고민하기 시작했다.

모바일 환경을 위해서는 우선 설치형 클라이언트와  그리고  둘의 중간자적 포지션에 해당하는 프로그레시브    가지를 생각할  있다  가장 먼저 포기한 것이 프로그레시브  앱이었다물론 에셋과 로직의경계를 불분명하게 만드는 프로그레시브  앱의 개념은 분명 매력적이다그러나 모바일 환경은 구글  아니라애플 플랫폼도 동시에 고려해야 하기 마련이다구글 플랫폼만을 중심으로 돌아가는 프로그레시브  앱은 사용자의 직관성 면에서도 여러 모로 좋지 않다고 생각했기 때문에 포기하게 되었다최근  년동안 프로그레시브  앱으로 개발된 플랫폼에 일반 유저들이 별로 유입되지 않았다는 사실은  방증이라고 생각한다.


 앱은 사실상  사이트를 만들자는 것인데알림 전송과 같은 서비스야 카카오톡과 같은 플랫폼으로 대체할 수있다손 치더라도  사이즈 디스플레이가 일상이  시대에 우리가 원하는 수준의 만족감을 만들어내긴 힘들었다(다양한 네이티브 피처의 사용이 난해하다는 것은 덤이다). 결국 우리는 네이티브 클라이언트 개발을 해야 한다는쪽으로 가닥이 잡혔다.


남은 것은 어떤 형태로 개발을 하는가이다우선 빠른 개발을 위해서는  기반 기술로 개발하는 웹뷰/하이브리드앱 정도를 생각할  있고여기서 최우선순위로 고려한 것은 Ionic + capacitor 조합이었다 다음으로 고려한것은 Flutter/React native 같은 3rd party lang to native 개발 플랫폼이었다애초에 Swift + cocoa/Kotlin + Android API 대표되는 완전한 네이티브 개발 환경은 고려조차 하지 않았다우리는 그럴 시간이 없었으니까.


웹뷰 앱과 네이티브-언어 채널형 크로스플랫폼 개발툴의 가장  비교 우위는 배포 편의성/성능일 것이다물론React native + Codepush 조합은  둘의 장점을 모두 취하기에 가장 이상적인 솔루션일  있었지만기존에이미 질릴대로 질려버린 React native 디버깅 경험이 발목을 잡았다( 한번도 브레이크포인트가 내가 원하는 곳에 걸려  기억이 없다). 솔직히 순수 웹뷰 앱은 toss마냥 극강의  프론트엔드 인력과 시간을 갈아넣지 않는 이상 달성하기 힘들 것이라고 생각했기 때문에 하이브리드 앱이 좋은 선택지였겠지만생각보다 기술적 자유도가 낮아 원하는 수준의 피처를 개발하기는 힘들다는 것이 발목을 잡았다.


결국 개별 플랫폼  무엇을 고르냐는 문제로  일주일이 흘러갔다 이상은 기다릴 수가 없었다그래서 우리팀은(다시 말해 나는개발 환경을 고르는 전략부터 다시 고민했다.


  1. 가장 장점이 강력한 플랫폼을  것인지
  2. 가장 단점이  거슬리는 플랫폼을  것인지


솔직히 언제나 가장  풀렸을 경우보다는 언제나 가장 잘못될 경우를 우선적으로 생각하는 사람이기에 2 전략으로 고르기로 했다그러자 자연스레 답은 Flutter 귀결되었다 다음으로 고민해야  것은 이것이었다.


선택한 개발 플랫폼의 단점/다른 플랫폼에 비한 비교열위를 극복할 방법


사실 이는 React native 대비한 단점을 생각만 해봐도 단순한 문제였다우리는 빠른 배포가 필요했다특히나기능 변경이 잦은 스타트업에서 이는 가장 중요한 고려 요소였다오랜 시간 고민한 끝에 Codepush 같이 에셋과 응용 로직을 동일 선상에 두고 원격으로 업데이트 가능한 플랫폼은 Flutter 도입할  없으니차선책으로 앱의 기능성을 어느 정도 원격에서 수정할  있는지 검토해 보기로 했다.


장고 끝에 Firebase Remote config 사용해앱에서 사용할 사용자의 GraphQL쿼리를 사용자 디바이스에저장하고서버에서도 실행 가능한 쿼리를 2중으로 제약하여(hasura 기능이다), 서버 접근에 대한 기능성을 수정하고전체 서비스 데이터의 일관성을 해칠  있기 때문에 일률적 업데이트를 수행해야 하는 경우에는Remote config 통해 사용자 클라이언트의 업데이트를 강제하기로 했다.


추가로 개발자를 구할  있는가도 문제이기는 했다 부분은 Flutter 최근 성장세가 가파르다는 것과한국에서도 점차 사용층이 늘기 시작했다는 그리고 기본적인 구조가 React 크게 다르지 않다는 점을 믿고 가기로했다.


최종적으로  선택은 어땠을까일부의 성공그리고 일부의 실패로 평가할  있을  같다. Dart  생각보다더 괜찮은 언어였다전반적인 코드 느낌이 자바스크립트와 자바에서 내가 가장 싫어하는 부분들만 섞은 형태라맘에  들었는데, Dart2까지 업그레이드  이후로 상당히 효울적으로 개발을   있었다또한 Flutter 성능이 매우 개선되어 드로잉으로는 거의 네이티브에 준하는 개발을   있었다(물론 최근에 대두되는 120hz이상디스플레이까지 고려하면  애매하다). 단점으로는 여전히 개발자를 구하기 힘들다는 그리고 데이터 어널리틱스를 기반으로  A/B 테스트를 배포하고 수행하는 것은 비효율적이라는 점이다.


서버 마이그레이션예상보다 빠른 비즈니스 성장


서비스를 최초로 출시한 것이 2020 7 경이었다코로나로 여러 산업이 흔들리는 와중에 출시 직후 이벤트를했는데상당히 반응이 좋아서 출시 직후 시간당 접속자 수가  10000명을 달성하게 되었다좋은 신호였지만Flutter 렌더링 사이클과 GraphQL클라이언트에 대한 이해 부족으로 개별 사용자의 클라이언트 전체가 초당약 600000회의 요청을 보내게 되었다당연히 서비스는 아주 빠르게 뻗었고당시 테스트로 배포한 상황이라EC2인스턴스를 재부팅해야만 했다.


물론 이후에 앱을 빠르게 재배포하여 문제는  이틀만에 해결되었다그러나 사용자들을 기다리게 하는 것은 곧잠재적 수익  성장 동력에 대한 심각한 위협 요소였고우리는 출시 이후  2주만에 ECS Fargate/RDS 기반으로 서버 근간을 이전하기로 했다.


ECS Fargate/RDS조합은 Promising 보였다물론 Fargate 상당히 비싸다. EC2 기준으로 단위 자원당비용을 비교하면 2 정도가 된다하지만 Fargate 언제든 아마존이 재배포할  있는 Spot자원을 활용할 경우70퍼센트 가격을 할인해 주므로결과적으로는 EC2 프로비저닝해  때보다  40퍼센트 저렴한 가격 대비성능을 누릴  있었고결정적으로 자원을 미리 프로비저닝  필요가 없다는 점이 무척 매력적이었다다음으로RDS인스턴스를 고르는  또한 그다지 어렵지 않았다. Hasura dump API 사용해 덤프 데이터를 다운로드 받고해당 쿼리를 RDS인스턴스에서 다시 실행하면 그만이었다.


중요한 것은데이터의 일관성을 해치지 않고 이들을 이전할  있는 방법이었다여러  고민해봤지만 이전/테스트/배포는 모두 시간이 걸리는 일이고결국 중간에 짧게나마 다운타임이 발생하는 것은 어쩔  없는 일이었다어널리틱스로 분석한 결과우리 서비스는 새벽 3~4 사이의 접속률이 확정적으로 시간당 0명에 가까웠고우리는이 시간에 Remote config 활용강제로 사용자들이 서비스를 사용하지 못하도록 막은  미리 사전에 연습한배포 프로세스를 실행했다결과는 성공적이어서 예정된 30 보다 이른 시간인 10분만에 이전은 끝났고이후 20분에 거친 정상 동작 테스트 끝에 서비스는  런칭했다하지만 RDS 그렇다 쳐도 EC2ECS사이에 유의미한 사용자의 만족도 차이가 있는지 궁금했는데, Remote config 기반으로  A/B테스트를 통해 측정한 결과두 집단 사이에서 서버 요청  응답 시간에는 유의미한 경향성의 차이가 보이지 않았다이렇게 배포 시간은 짧았지만실제 전체 집단에 적용하기까지는  2주의 시간이 걸렸다 동안 처음엔 10퍼센트 다음엔 30 퍼센트 다음엔 70 퍼센트 순으로 사용자들을 테스트했고이전 기간 동안 불편을 호소한 고객은 없었다.


 다음으로 해결해야  문제는 데이터베이스 인스턴스의 업데이트 다운타임이었다이를 해결할 방법을 찾았지만결론적으로 데이터베이스 시스템을 수정할  다운타임은 불가피했다차선책으로 우리는 AWS RDS Aurora 검토했는데기존 데이터베이스의 읽기 복제본을 만든 다음높은 성능의 해당 인스턴스로 Failback하면 100밀리세컨드 이내의 시간 단위로 동기화가 되기 때문에 사실상 적용 가능한 유일한 솔루션으로 보였다.


여기에 관리형 서비스를 사용하며 점점 상승하는 관리 난이도 때문에 DB세션을 납품 기한 내로 정확히 컨트롤하는 것이 힘들었기 때문에관리형 RDS session pool 관리 서비스인 RDS proxy 사용해 데이터베이스에 지나치게 많은 연결이 생성되는 것을 방지했다.


백엔드와 관련된 기능  백오피스가 있는데간단한 데이터 어널리틱스( BI) 경우 Redash라는 오픈소스 프로젝트를 도입했고전문적인 데이터 어널리틱스의 경우는 Amplitude Mixpanel 활용했으며이들에서 발생한 Raw data 추후에 주기적으로 BigQuery 같은 대규모 데이터 분석 도구에 재배치하기로 했다( 시점에서는 여전히 유의미한 데이터가 쌓이지 않기에 포기했다…).


앞으로 해야  


앞으로 해야  일은 물론 산더미처럼 많다 관리자를 뽑기 위해서  코드를 정리한 것이 당장 일주일 전에야끝났다(한달 안에 끝내야 했다). 여전히  대시보드 정리 작업은 손도  대고 있다그나마 vue flutter모두GraphQL클라이언트의 기본 구조가 유사하고, State management 전략 또한 라이브러리들을 사용하면 유사하게 관리할  있는 전략이 있기 떄문에 우선순위는 높이기 힘들지만 시간  때마다 조금씩 정리해 두고다른 개발자를 고용하면 쉽게 해결할  있을  같다남은 것은 AWS cloudsearch 활용한   자연스러운 자연어검색기능 구현 정도와 데이터 어널리틱스에 대한 본격적 대비그리고 모듈화한  컴포넌트들의 redesign정도인것 같다추가 기능 구현 요청이  당연히 들어오겠지만적어도  시점에서  동안 쌓아온 기술 부채는 거의 청산한 느낌이다그것도 신기능을 꾸준히 개발하면서 이룬 것이니충분히 자부심을 가져도 좋을  같다.


그래서 새 직원은 어디서 구하지...