🌱 인프런 아키텍처의 과거와 현재, 그리고 미래 - 이동욱님
인프런의 아키텍처가 어떻게 변해왔는지, 그리고 왜 그렇게 변해왔는지에 대한 이야기. 목표를 일정 안에 달성하기 위해 감수해야하는 제약 조건들이 무엇인지, 그런 제약 조건 속 가장 적절한 아키텍처가 무엇인지, 인프런은 어떤 결정을 했는지 하나하나 설명해주셨다.
🌱 시즌1
인프런의 규모가 작고 가냘펐던 기간. 개발자는 1~2명. 모든걸 소수의 인원이 직접 해결할 수 밖에 없던 기간.
가능한 직접 개발하는 행동을 최소화하고, 외부 자원(SaSS)을 최대한 활용하는 방식으로 버텼다.
기존 PHP + jQuery
구성에서 워드프레스 + MySQL
로, 호스팅 서비스 서버는 단 1대로.
그러나 회원수 10만이 되면서 많은 문제가 발생한다.
- 미칠듯이 느려지는 서비스
- 의도를 알 수 없는 데이터
- 워드프레스로 인한 기능 확장의 한계
- 직접 관리해야하는 서버, 배포, DB
결국 기능은 그대로 둔채, 서비스를 직접 다시 만들자는 결정을 내린다. 아직 서비스의 규모가 작기 때문에 (많은 기능이 없었기 때문에) 이런 결정을 내릴 수 있었다.
결국 워드프레스를 선택한 것이 악수가 되어 돌아왔지만, 작은 회사에서 나름대로의 생존전략을 잘 연구했다는 생각이 든다.
🌱 시즌2
시즌1에서 프론트와 백엔드 등 각 분야의 개발자들이 조금씩 합류했다. 이렇게 됐을 때 리스크는 무엇일까? 각 분야의 개발자 1명이라도 빠지면 해당 영역의 개발이 완전히 멈추어버린다. 이걸 막기 위한 인프런의 선택은?
- 기술 스택을 통일하자.
- FE, BE, DevOps 를 한 언어(JS)로
- 낮은 러닝 커브를 가진 기술 선택
- SSR & FxDom, FxSQL
- 관리비용 최소한으로 줄이기
- CircleCI, AWS(Fargate, RDS)
정적 HTML을 서버에서 내려주고, 프론트에서 DOM을 선택하는 방식(서버 사이드 랜더링)을 택했다. 결국 구현 코드가 최소화되고, 단일화된 라이브러리와 익숙한 패턴으로 3.5명의 개발자가 5개월만에 전체 시스템을 워드프레스에서 Full JS로 전환에 성공했다.
그러나 신규 개발자를 채용하면서 문제가 점점 발생하기 시작했다. 구현 코드를 최소화하려다보니 바닐라 JS를 사용했고, 바닐라 JS는 Type을 표현할 방법이 없었다. 모든 데이터가 객체(Json)으로만 움직이다 보니 console.log를 찍어보지 않는 이상 코드 단계에서 동작 결과를 유추할 수 없었다. 또한 IDE의 지원을 받지 못한채로 개발을 진행해야만 했다. 급한 일정에 맞춰 테스트가 생략되었으므로 검증되지 않은 기존 기능에 대한 심리적 불안감도 함께 감당해야했다. 엎친데 덮친 격으로, 바닐라 JS는 몇 년 전부터 버전업을 멈춘 상태.
이 단계에서 이동욱님이 합류하신다.
이쯤에서부터 인프런 대표님이 궁금해졌다. 대범한 기술스택 선정과 실행. 결과는 아쉬웠으나 가만히 가라앉길 기다리는 배보다, 선체를 뜯어내어서라도 달리는 배가 나은거 같다.
🌱 시즌3
BE 3명, FE 3명, DevOps 1명, CTO(이동욱님) 1명. 인프런은 새로운 전략을 채택한다.
- 심리적 안정감을 끌어올리자. 코드 수정에 대한 자신감, 코드 레벨에서 예측 가능한 결과물을 만들도록
- Class & Type, 테스트 코드 & 정적 분석, eslint,& prettier
- 외부 개발자들이 많이 사용하는 라이브러리, 구조, 패턴을 사용해서 진입 장벽을 낮추자
- TypeScript, React & Next.js, 계층형 아키텍처 DI 테라폼, GO 등
- 독립성, FE BE가 독립적으로 개발 가능하도록 하자.
- FE & BE 계층 분리, API 명세 기반 개발, 분리된 저장소
결국 대중적인 기술스택을 선택해야, 개발자 생태계에서 필요한 인력을 쉽게쉽게 구할 수 있다. 대중적인 기술스택을 선택하는 것도 모두 전략이다. 반대로 생각하면 그런 기술스택을 선택해야 쉽게쉽게 구직이 가능하다. 야호. 잘 선택했다.
그렇다면 이 전략들을 어떻게 반영할 것인가? 크게 2가지 방법이 있다.
- 빅뱅
- 서비스 개선 멈추고 전체를 다시 만들어서 한번에 교체
- 예상 기간 1년
- 점진적 개편
- 서비스 개선과 시스템 개편 병행, 기능 그룹별 이관
- 예상 기간 2년
당연히 인프런은 후자를 택했다. 스타트업에서 6개월이라는 시간은 대기업에서의 2년에 준하는 시간이다. 6개월간 서비스를 멈춘다는 건, 경쟁업체에게 모든 걸 양보한다는 뜻이 된다. 결국 달리는 자동차의 바퀴를 교체할 수 밖에 없었다. 그리고 이어지는 동욱님의 한마디. “매년 2~300% 성장하는 서비스를 중단 없이 개편하는 경험을 개발자들에게 주고 싶었었어요.”
사업관점에서 이미 후자를 택할 수 밖에 없는 상태. 그래서 개편 경험을 개발자들에게 주고 싶었다는 동욱님의 말씀은 그저 꾸며주는 말이었을지도 모르겠다. 그러나 왜인지 모르게 ‘부럽다’는 세글자가 머릿 속을 맴돌았다.
백엔드와 프론트를 점진적으로 개편하기 시작한다.
기존 인프런 백엔드 서버는 API 게이트웨이
로서의 역할을 하고, 회원/인증 서버
와 비즈니스 로직 서버
를 분리해서 개편.
🌱 서비스 장애 (강제 시즌4)
점진적으로 개편이 진행되는 상황에서 문제가 발생하기 시작했다. 한 기능의 장애가 일어나면 전체 기능의 장애로 확장되는 상황. 가령 어드민 화면에서 데이터를 엑셀 다운로드하면 인프런 서비스 전체가 다운되어 버린다던지… 이럴 때 독립적으로 운영하는 서비스를 모두 분리(MSA 도입)하면 애플리케이션 서버의 메모리, CPU를 많이 점유해서 발생하는 장애는 격리시킬 수 있다.
그러나 데이터베이스에 대한 장애 격리는 여전히 불가능하다. 데이터베이스 또한 분리를 해야한다. 데이터베이스가 분산되기 시작하면 복잡도가 기하급수적으로 높아진다. 트랜잭션 관리법도 달라지고, 쿼리를 쪼개서 API 통신으로 변경해야한다. 때문에 기존 쿼리를 대대적으로 개편해야한다.
현재 상황을 타개하기 위한 모범답안은 RDS, Redis, DDB 같은 데이터베이스를 모두 도입해서 적재적소에 사용하고 기술적 도약을 이루어내는 것. 그러나 현재 인프런은 시즌3 작업이 여전히 진행중이다. 도저히 짬이 안난다. 시즌3 작업 말고도 할게 많다. Data lake, 검색 기능, 전체 회원 조회 등등… 모범 답안이 뭔지는 알지만, 일단 기술의 가지수를 줄이는 것이 중요하다. 우선 2~3년을 버틸 수 있는 적정 기술을 선택하고, 짬이 나면 다시 개편을 진행하기로 결정한다.
그래서 선택한 DB가 MongoDB Atlas. DynamoDB와 같은 NoSQL + Elasticsearch와 같은 Lucene 검색 엔진과 한글 형태소 분석을 지원한다. Data lake, 검색 엔진, 실시간 데이터 처리 등을 하나의 기술 스택으로 처리할 수 있어서 선택한다.
- 여전히 (구)인프런은
API 게이트웨이
로만 - 신규 인프런 백엔드가 이벤트를 발행하고, SNS(Simple Notofication Service)를 통한 멀티 구독
- SQS(Simple Queue Service)를 통한 최종적 일관성 보장
- Spring Boot를 통한 배압조절 (Back pressure)
- Zero Payload를 통한 이벤트 순서 보장
- ‘검색 엔진’이라는 추가 의존성, But 기존 서비스와 장애가 격리된 채로 완성되었다.
만일 SNS로 이벤트 발행이 실패하면 어떻게 할까? 이벤트 저장소를 중간에 두면 해결이 된다. 그러나 아키텍처 복잡도가 너무 높아진다는 단점이 있다. 이번 시즌에는 에러로그가 발생하면 수동으로 재발행하는 식으로 해결했다. 자세한 내용은 인프랜 기술 블로그에 더 공개할 예정이다. (왜 카프카를 사용하지 않았는지 등)
🌱 마무리 & QnA
- 아직도 (구)인프런이 90%를 차지하고 있다.
- 아키텍처가 아주 조금씩 조금씩 옮겨지고 있다. 여전히 시즌3~4는 진행중이다.
- 한번에 모든걸 바꾸기 어렵고, 점진적으로…
Q. 중간에 메세지를 저장하는걸 준비중이라했는데 어떤 DB를 사용하실건가요?
A. 트랜잭션이 되어야해서, 트랜잭션을 지원하는 DB 중에 선택할 예정입니다.
Q. 자바 스크립트 테스트 작성이 많이 어려울텐데, 어떻게 안정성을 지키면서 자바로 옮겼나요?
A. 함수형 프로그래밍을 하고 있기 때문에, 단일 함수들로 이루어져있어서 테스트 코드를 짜는게 어느정도 가능하긴했어요.
단일 함수 + API 엔드포인트에 대한 것만 테스트를 진행하고, 나머지는 블랙박스라고 감안하고 작업했습니다.
Q. 장애격리를 위해서 서비스를 분리해야하는 이유? 데브옵스 적인 영역으로 가능할거 같은데 굳이 서비스를 분리한 이유가 있을까요?
A. 하나의 큰 저장소 자체가 부담이었습니다. 독립적으로 떼어내서 단위테스트나 추가나 프로젝트 전환이 편해요.
모노레포로 모든걸 물고 있으면 JS -> JAVA 전환이 너무 힘듭니다. 그래서 데브옵스 적인 걸로 해결도 가능하지만, 서비스를 분리했어요.
Q. 이어지는 문제로, DB 쪽 커넥션이 많아진다던지 문제는? A. 서비스별로 디비 계정을 발급해서, 디비 계정별로 에러가 발생하는 계정이 무엇인지로 판별해서 어디가 문제인지 판별했고, 커넥션이 많이 필요한 계정에만 풀을 넉넉히 발급하고, 나머지는 커넥션을 줄여서 최적화 했습니다.
Q. 개편을 어떻게 설득하고, 성과지표를 어떻게 측정했나요?
A. 개편안에 대해서 운영파트에서 먼저 제안을 주셨어요. 인프런은 마케팅적으로 많은 이벤트를 했는데, 그 때마다 최대실적을 달성한적이 없습니다.
달성하려하면 서버가 죽어버리니까요. 운영이나 사업쪽이 답답한 나머지 먼저 이야기했고,
저는 서버 비용이나 데드횟수 같은걸 모두 체크했습니다.
“저희가 이만큼 많이 장애나고 있어요.” 이런 내용을 모두 발표했습니다.
이 때 장애가 안났더라면 얼마나 많은 회원이 늘었을지, 얼마나 많은 돈을 벌었을지 예상지표를 만들어서 발표 후 설득했습니다.
🌱 후기
80일간의 세계일주 책의 후반부를 보면, 자신들이 타고 있는 배의 선체를 뜯어내어 연료삼아 결국 목적지에 도착한다. 인프런이 딱 그런 길을 걸어오고 있다는 생각이 들었다. 당장의 생존이 중요한 스타트업이 걸어야하는, 현재 우리 회사도 걷는 중인…
개인적으로 사업의 성장보다 기술적인 완성도에 더 관심이 많다보니, 일이 재미없고 답답하고 불편할 때도 있다. 좋게 생각하면 현실과 이상의 거리를 좁히는 시간이라고 생각한다. 그러나 기술적인 이상을 지나치게 낮춰서 사업에서 필요한 기능만 쳐내는 개발자가 되고 싶진 않다. 적당히 현실적으로 생각하면서, 적절히 기술적 이상을 충족시킬 수 있는, 끊임없이 욕심 내는. 그런 개발자가 되어야지…
인프콘에서 뵌 동욱님은 [우아콘2020] 수십억건에서 QUERYDSL 사용하기와 정말 다른 느낌이었다. 2020년 우아콘 영상에서는 기술적으로 얼마나 깊고 디테일하게 파고들어서 다루는지가 포커스였다면, 올해 인프콘에서는 현실적으로 주어진 리소스와 시간을 어떻게 최적화해서 활용하는지에 포커스가 쏠려있었다. 우아콘 동욱님이 백엔드 이동욱님이셨다면, 인프콘 동욱님은 CTO 이동욱님이셨다.
개발바닥 동욱님, 주현님, 맘편한세상 현태님을 보면 CTO라는 이름의 무게가 조금씩 느껴진다. 처음엔 두리뭉술하게 ‘개발자 끝판왕’, ‘개발자들의 리더’ 정도로 생각했는데, 요즘은 가지고 있는 리소스(개발자, 시간)를 최소한으로 활용해서 최대한의 결과를 내야하는… 끊임없이 자신의 선택을 후회해야하는… 숨막히는 이름으로 느껴진다. 얼마전 경수님이 놀러오셨을 때 정희님께서 현태님 주름이 깊어졌다고 농담을 던지셨다. 현태님과 마주 앉을 때마다 현태님 주름을 살펴보게 될 것 같다.
댓글남기기