가이드

출시 후 SaaS를 망가뜨리지 않고 확장하는 법

출시는 쉬운 부분이었습니다. 위험한 구간은 그다음 한 해입니다. 성장이 여기까지 데려와 준 단순한 제품을 조용히 갈라놓습니다. 이것은 느린 붕괴 없이 확장하기 위한 차분하고 실용적인 안내입니다.

Have a nice dayHave a nice day읽는 데 11분
출시 후 SaaS를 망가뜨리지 않고 확장하는 법

누구나 출시를 축하합니다. 그러나 그 뒤에 오는 부분을 경고해 주는 사람은 거의 없습니다. 첫 백 명의 고객을 안겨 준 제품이 다음 천 명의 무게에 휘청이기 시작하는, 기묘하고 손에 땀을 쥐게 하는 한 해 말입니다. 극적인 일은 아무것도 일어나지 않습니다. 그저 모든 것이 느려지고, 불안정해지고, 바꾸기 어려워질 뿐입니다. 어느 화요일, 예전에는 하루면 되던 기능이 이제 일주일이 걸린다는 것을 깨닫지만, 그 이유를 누구도 똑 부러지게 말하지 못합니다. 대부분의 SaaS 제품이 조용히 승패가 갈리는 곳은 출시가 아니라 바로 그곳입니다.

우리는 바로 이 지점에서 많은 창업자와 함께 앉아 왔습니다. 그들은 실패하고 있는 게 아닙니다. 그것이 헷갈리는 지점입니다. 매출은 오르고, 팀은 커지고, 데모는 잘 됩니다. 그러나 그 아래에서 제품은 신음하고 있습니다. 지원 문의가 사용자보다 빠르게 늘어납니다. 예전에는 지루하던 배포가 이제는 숨을 죽이고 하는 일이 되었습니다. 1년 전에는 영리하게 느껴지던 코드베이스가, 이제는 변경 하나하나가 다른 무언가를 건드릴까 위태로운 지뢰밭처럼 느껴집니다. 그들은 잘못한 게 없습니다. 그저 자신이 만든 것을 성장으로 앞질렀을 뿐이며, 그것이 원래 일어나야 할 일이라고 아무도 일러 주지 않았을 뿐입니다.

그래서 이것은 균열이 나타나기 전에 더 많은 창업자가 가졌으면 했던 안내입니다. 하이퍼스케일이나 쿠버네티스, 또는 어느 유니콘이 오천만 사용자에서 한 일에 관한 이야기가 아닙니다. 지루하지만 결정적인 중간 지점에 관한 이야기입니다. 모든 것을 재작성하거나, 고객을 겁주거나, 팀을 소진시키지 않으면서 소수에게 작동하던 것을 다수에게 작동하도록 옮겨 가는 일입니다. 목표는 완벽한 아키텍처가 아닙니다. 조용히 망가지기 시작하는 제품이 아니라, 계속 성장하는 제품입니다.

SaaS가 성장할 때 실제로 무너지는 것

창업자를 가장 놀라게 하는 부분이 이것입니다. 확장 문제는 대비하던 극적인 장애로 찾아오는 일이 거의 없습니다. 서버에 불이 붙지 않습니다. 대신 제품은 일종의 미열을 앓습니다. 즉시 로딩되던 페이지가 3초씩 걸리기 시작합니다. 초기 고객에게는 멀쩡히 돌던 보고서가 막 계약한 큰 고객에게는 타임아웃이 납니다. 같은 버그가 계속 재발합니다. 코드의 두 부분이 누구도 문서화하지 않은 방식으로 은밀히 서로 의존하고 있기 때문입니다.

정말로 무너지는 것은 서버인 경우가 드뭅니다. 그것은 여러분의 가정입니다. 초기에는 첫 사용자들의 형태에 맞춰 만들었습니다. 몇 개의 계정, 작은 데이터, 단순한 워크플로, 모두가 대체로 비슷했습니다. 성장은 같은 것을 더 늘리기만 하지 않습니다. 다양성을 더합니다. 데이터가 열 배인 고객. 상상도 못 한 방식으로 기능을 쓰는 팀. 모두가 한꺼번에 로그인하는 월요일 오전 9시의 급증. 그 하나하나가, 어기는 것이 상상조차 안 되던 1년 전에 코드에 박아 넣은 가정을 조용히 어깁니다.

제품은 사용자가 더 많아져서 무너지는 게 아닙니다. 그 사용자들이 첫 고객들이 그랬던 것보다 서로 더 다르기 때문에 무너집니다.
균열의 순간에 창업자들에게 하는 말

그것이 먼저 드러나는 경향이 있는 네 곳은 예측 가능합니다. 데이터베이스는 거의 항상 탄광의 카나리아입니다. 작은 테이블에서 즉시였던 쿼리가 데이터가 늘면서 기어갑니다. 느린 엔드포인트. 한두 페이지가 요청마다 일을 너무 많이 하는데, 트래픽이 쌓이기 전까지는 괜찮습니다. 취약한 배포. 코드가 너무 얽혀 있어 가늠할 수 없는 탓에 무엇이든 내보내는 일 자체가 두려워졌습니다. 그리고 지원 부하. 이것은 인프라가 전혀 아니지만, 제품이 사람들이 실제로 쓰는 방식에 더는 맞지 않는다는 가장 정직한 초기 신호입니다.

몇 명의 보행자에게는 멀쩡하던 작은 나무다리가 늘어나는 군중 아래 눈에 띄게 팽팽해지고, 한두 개의 널빤지가 휘기 시작하는, 차분하고 절제된 색조의 깔끔한 편집 일러스트
확장은 붕괴로 실패하는 일이 드뭅니다. 휘어짐에서 시작합니다. 새 하중이 더해질 때마다 조금씩 더 휘는 한 장의 널빤지에서.

아직 없는 문제를 푸는 함정

고치는 이야기를 하기 전에, 어떤 최적화보다 더 많은 제품을 구해 온 경고를 하나. 성장하는 SaaS에 가장 큰 위협은 확장을 무시하는 것이 아닙니다. 너무 일찍 그것을 쫓는 것입니다. 창업자가 첫 둔화를 느끼는 순간, 본능은 유명 엔지니어링 블로그에서 읽은 아키텍처로 손을 뻗습니다. 마이크로서비스. 메시지 큐. 멀티 리전 구성. 백만 행도 차지 않은 데이터베이스를 샤딩하는 일.

이렇게 해서, 아직 도달하지도 않은 규모를 위한 인프라를 짓는 데 여섯 달과 큰돈을 쓰는 사이, 정작 제품은 멈춰 섭니다. 더 나쁜 것은, 이제 앞으로의 모든 변경이 더 어려워졌다는 점입니다. 분산 시스템은 여러분이 가지고 있던 단순한 것보다 구축도 디버깅도 극적으로 더 복잡하기 때문입니다. 아직 없던 문제를 오늘 확실히 가진 문제와 맞바꾼 것입니다. 아무것도 출시되지 않는다는 문제와요.

여기서의 규율은 애초에 좋은 제품을 만드는 규율과 같습니다. 눈앞의 문제를 푸는 것. 상상하면 우쭐해지는 문제가 아니라요. 빠르게 바꿀 수 있는, 지루하고 잘 이해된 모놀리스가, 손대기 두려운 유행하는 분산 시스템보다 더 잘 확장됩니다. 복잡성은 한 번 사는 것이 아니라 매일 치르는 비용입니다.

무엇이든 바꾸기 전에 측정하라

이 단계에서 만나는 거의 모든 창업자는 문제가 어디 있는지 안다고 확신합니다. 절반은 틀립니다. 부주의해서가 아니라, 직관이 형편없는 프로파일러이기 때문입니다. 느리게 느껴지는 코드 부분은 대개 멀쩡하고, 진짜 범인은 아무도 신경 쓰지 않은 페이지에서 마흔 번씩 도는 어떤 조용한 쿼리입니다. 측정하지 않은 것은 고칠 수 없으며, 추측이야말로 팀이 엉뚱한 것을 최적화하느라 몇 주를 쓰는 방식입니다.

시작하는 데 화려한 관측 스택은 필요 없습니다. 늘 눈앞에 둘 지루한 숫자 세 개가 필요합니다. 어떤 엔드포인트가 가장 느린지, 그리고 실제 트래픽에서 얼마나 느린지. 어떤 데이터베이스 쿼리가 총 시간을 가장 많이 쓰는지. 가장 느린 단일 쿼리가 아니라, 수천 번의 호출에 걸쳐 시간이 쌓이는 쿼리 말입니다. 그리고 오류가 실제로 어디서 일어나는지, 재현할 만큼의 맥락과 함께. 이 셋이면 안개는 대개 하루 안에 걷힙니다.

  1. 1
    기본 모니터링을 켠다
    엔드포인트별 응답 시간, 오류율, 그리고 데이터베이스의 슬로 쿼리 로깅. 호스팅형 도구는 이를 한나절이면 해냅니다. 보이지 않는 숫자는 개선할 수 없습니다.
  2. 2
    진짜 상위 3개를 찾는다
    직감이 아니라 소비된 총 시간으로 정렬합니다. 세 범인이 거의 항상 고통의 대부분을 차지합니다. 적어 두십시오. 그것이 진짜 로드맵입니다.
  3. 3
    하나를 고치고, 다시 측정한다
    한 가지만 바꾸고, 그다음 숫자를 다시 확인합니다. 다음으로 넘어가기 전에 효과가 있었는지 확인합니다. 한 번에 둘을 바꾸면 어느 것이 중요했는지 결코 알 수 없습니다.
  4. 4
    충분히 좋아지면 멈춘다
    시작하기 전에 '충분히 빠름'을 정의하십시오. 가령 현재 부하에서 모든 페이지가 1초 미만, 처럼요. 그 이상은 최적화가 승리가 아니라 시간 낭비입니다.

이 마지막 단계는 보기보다 더 중요합니다. 성능 작업은 진짜로 중독성이 있습니다. 깎아 낼 밀리초는 늘 하나 더 있습니다. 그러나 고객은 200ms와 120ms의 차이를 느끼지 못하고, 그것을 쫓느라 쓰는 시간은 정작 사업을 키울 기능에 쓰지 못한 시간입니다. 측정하고, 상위 3개를 고치고, 승리를 선언하고, 앞으로 나아가십시오.

데이터베이스는 거의 항상 첫 번째 벽

성장하는 SaaS가 첫 진짜 천장에 부딪히는 곳에 돈을 걸어야 한다면, 우리는 매번 데이터베이스에 걸겠습니다. 초기의 작은 결정이 가장 세게 누적되는, 시스템에서 유일한 부분입니다. 인덱스 없는 쿼리는 천 행에서는 눈 깜짝할 새 돌지만 백만 행에서는 멈춰 섭니다. 코드는 바뀌지 않았습니다. 바뀐 것은 데이터이며, 데이터는 오직 늘어나기만 합니다.

좋은 소식은, 데이터베이스가 가장 싸고 가장 효과 큰 수정이 사는 곳이기도 하다는 점입니다. 고전은 빠진 인덱스입니다. 한 줄이 몇 초짜리 쿼리를 즉시로 바꿉니다. 필요한 몇 행을 찾으려고 모든 행을 훑는 일을 데이터베이스가 그만두기 때문입니다. 그 바로 뒤에 N+1 쿼리 문제가 있습니다. 한 가지를 묻는 대신, 같은 작은 질문을 루프에서 수백 번 데이터베이스에 조용히 던지는 페이지입니다. 둘 다 흔하고, 둘 다 들여다보기 전까지 보이지 않으며, 둘 다 일단 찾으면 대개 하루짜리 수정입니다.

여기에는 기댈 만한 순서가 있고, 끝으로 건너뛰기보다 순서대로 따르는 편이 이득입니다. 먼저 쿼리를 고치십시오. 인덱스, N+1, 느린 보고서. 그다음 끊임없이 읽히지만 좀처럼 바뀌지 않는 데이터에 캐싱을 더하십시오. 그 후에야 읽기 복제본, 더 큰 인스턴스, 데이터 분리를 논할 의미가 생깁니다. 대부분의 SaaS 제품은 뒤쪽 단계가 결코 필요 없습니다. 그저 앞쪽 단계를 제대로 해야 했을 뿐입니다.

사서가 방대한 색인된 책장에서 라벨 붙은 책 한 권을 즉시 빼내는 모습과, 바닥에 놓인 라벨 없는 책을 한 권씩 다급히 확인하는 인물을 대비한, 인덱스가 있는 데이터베이스 쿼리와 없는 쿼리를 나타내는 플랫한 편집 일러스트
인덱스는 그저 책장에 붙은 라벨일 뿐입니다. 그것이 없으면 데이터베이스는 여러분이 청한 한 권을 찾으려 바닥의 모든 책을 확인합니다.

제품을 확장한다는 것은 바꾸는 방식을 확장하는 것

창업자의 허를 찌르는 전환이 이것입니다. 어느 지점을 넘어서면, 확장은 제품이 더 많은 사용자를 감당하는 일이 아니라 이 더 많은 변경을 감당하는 일이 됩니다. 여러분과 개발자 한 명이던 시절에는 모두가 시스템 전체를 머릿속에 담았습니다. 무엇이 영향받을지 알았기에 무엇이든 바꿀 수 있었습니다. 다섯이나 열 명이 되면 그 심상 모델은 산산이 부서지고, 모두가 모든 것을 안다고 가정한 코드는 부담이 됩니다.

배포가 두려워지는 진짜 이유가 이것입니다. 코드가 하룻밤 새 나빠진 게 아닙니다. 변경의 폭발 반경을 이제 누구도 온전히 예측할 수 없게 된 것입니다. 해법은 영웅적 분투나 출시 동결이 아닙니다. 더 큰 팀이 서로를 밟지 않고 움직이게 해 주는, 화려하지 않은 받침대에 투자하는 것입니다. 명백한 고장을 잡아내는 자동화 테스트 묶음, 의식이 아니라 일상이 된 배포, 그리고 허둥대는 대신 나쁜 릴리스를 몇 초 만에 끄는 방법 말입니다.

  • 고장 나면 치명적일 한 줌의 흐름을 덮는 테스트 묶음. 로그인, 결제, 핵심 동작. 전부가 아니라 결정적인 소수만.
  • 의식이 아니라 버튼으로 도는 배포. 작고 잦은 출시가 마음 졸이는 일이 아니라 안전해지도록.
  • 빠르게 롤백하는 수단. 나쁜 릴리스가 밤샘 사고가 아니라 5분짜리 사건이 되도록.
  • 기능 플래그. 코드를 먼저 몇몇 고객에게 내보내고, 말썽이면 즉시 끌 수 있도록.
  • 한 사람의 휴가가 제품의 한 영역 전체를 얼어붙게 하지 않을 만큼의 문서.

이 가운데 무엇도 데모에 나타나지 않습니다. 무엇도 직접 기능을 더하지 않습니다. 그리고 이것이야말로, 계속 가속하는 제품과 새로 채용할 때마다 더 느리게 갈리는 제품을 가르는 바로 그 작업입니다. 잘 확장하는 팀은 제품을 안전하게 바꿀 수 있는 능력 자체를 하나의 기능으로 대합니다. 규모에서는 그것이 바로 기능이기 때문입니다.

균열의 순간에서 나온 짧은 이야기

구체적으로 보이기 위해, 우리가 한 작업에서 엮어 낸 합성 사례를 하나. 세부는 흐렸고, 형태는 실제에 충실합니다. 현장 서비스 팀을 관리하는 작은 SaaS가 잘 출시되어 수백 개의 유료 회사까지 성장했습니다. 창업자들은 기쁨과 탈진을 똑같이 안고 있었습니다. 그때 역대 최대 고객이 계약했습니다. 이전 열 고객을 합친 것보다 더 많은 사용자와 더 많은 과거 데이터를 가진 회사였습니다.

한 주 만에, 모두가 살다시피 하던 대시보드가 그 고객에게 기어가듯 느려졌고, 이상하게도 다른 모두에게도 느려졌습니다. 지원 문의가 치솟았습니다. 창업자들은 훨씬 큰 서버가 필요하다고 단정하고, 고통스럽고 값비싼 재설계를 각오하고 있었습니다. 우리가 불려 온 것이 바로 그 순간이었고, 그 본능은 이해할 만했지만 틀렸습니다.

우리는 아키텍처에 손대지 않았습니다. 슬로 쿼리 로깅을 켜고 한나절 지켜봤습니다. 범인은 거의 민망할 만큼 작았습니다. 메인 대시보드가 각 사용자의 작업 목록을 고전적인 N+1 패턴으로, 작업마다 쿼리 하나씩 쏘며 불러오고 있었습니다. 작은 고객에게는 무해한 쿼리 수십 개를 뜻했습니다. 새 거대 고객에게는 페이지 로드마다 수천 개를 뜻했고, 공유 인프라에서 그것이 시스템 전체를 모두에게 끌어내렸습니다.

창업자들이 얻은 교훈은 기술적인 것이 아니었습니다. 그들이 상상하던 무시무시한 확장 문제, 재구축과 투자 유치가 필요하다던 그 문제가, 측정해 보니 무서운 증상 뒤에 숨은 이틀짜리 수정이었다는 것입니다. 그들은 엉뚱한 문제를 푸는 데 몇 달을 쓰기 직전이었습니다. 상상한 위기와 측정된 위기 사이의 그 간극이야말로, 확장에 쓰는 돈이 가장 많이 낭비되는 곳입니다.

정말로 한 부분을 다시 만들어야 할 때

조급한 확장에 대한 이 모든 신중함은 결코 리팩터링하지 말고, 결코 다시 만들지 말라로 읽힐 수 있습니다. 그게 아닙니다. 때로 제품의 한 부분은 정말로 수명을 다했고, 그것을 또 기우는 것이 비싼 선택이 됩니다. 요령은 진짜 구조적 한계와, 측정된 수정으로 처리될 보통의 성장통을 구별하는 것입니다.

정직한 신호는 이렇습니다. 컴포넌트를 다시 만드는 것은, 그것을 바꾸는 비용이 그것을 교체하는 비용보다 일관되게 높아졌을 때입니다. 못생겨서가 아닙니다. 안정적이고 좀처럼 손대지 않는 못생긴 코드는 괜찮습니다. 여러분이 찾는 것은, 모든 변경이 느리고 위험하며, 같은 버그가 계속 돌아오고, 새 개발자가 안전하게 작업할 수 없고, 더 싼 수정을 이미 시도해 벽에 부딪힌 시스템의 한 부분입니다. 그중 여럿이 한꺼번에 참일 때, 그 한 부분만을 겨냥한 재작성이 옳은 판단입니다.

신호아마 그냥 수정아마 재구축
증상느린 페이지나 쿼리 하나한 영역의 모든 변경이 느리고 위험함
버그이따금, 고칠 수 있음같은 버그가 계속 돌아옴
싼 수정아직 시도 안 함이미 소진, 여전히 막힘
범위한 기능에 한정됨모듈 전체로 번짐
옳은 한 수측정하고 기우기그 한 부분만 의도적으로 재구축
측정된 수정과 진짜 재구축을 구별하기.

그리고 다시 만들 때는, 한 부분을 다시 만드십시오. 제품을 통째로가 아니라요. 바닥부터의 전면 재작성은 확장의 사이렌 노래입니다. 깔끔해 보이지만 결국 한 해를 가라앉히고, 그사이 경쟁사는 출시합니다. 명확한 경계 너머에서 썩은 한 컴포넌트를 교체하고, 나머지 제품은 계속 돌아가며 벌게 하십시오. 영웅적으로가 아니라, 외과적으로.

안에 있는 가족이 일상을 이어 가는 동안, 장인이 다른 부분은 튼튼한 집에서 닳은 들보 하나만 조심스레 갈아 끼우는, 전면 재구축이 아니라 표적화된 리팩터링을 전하는 차분한 편집 일러스트
잘하는 확장은 닳은 들보를 한 번에 하나씩 갈아 끼우는 모습입니다. 모두가 여전히 사는 집을 허무는 것이 아니라.

벽에 부딪혔는데 수정인지 재구축인지 모르겠나요?

그것은 틀리면 비싸고 맞히면 싼 판단입니다. 여러분의 제품이 실제로 어디서 팽팽해지는지 측정하고, 이틀짜리 수정인지 더 깊은 무언가인지 솔직히 말씀드립니다. 누구든 새 코드 한 줄을 쓰기 전에요.

우리가 소프트웨어 확장에 접근하는 방식 보기

자주 묻는 질문

제 SaaS가 확장의 벽에 곧 부딪힐지 어떻게 알 수 있나요?
충돌이 아니라 느린 증상을 살피십시오. 페이지가 꾸준히 느려지고, 같은 버그가 재발하고, 배포가 이제 위험하게 느껴지고, 지원 문의가 사용자 수보다 빠르게 늘어나는 것들입니다. 이것들은 보통 어떤 극적인 장애보다 한참 먼저 나타납니다. 일찍 기본 모니터링을 켜 두면 벽에 부딪히는 대신 다가오는 것을 보게 됩니다.
확장을 위해 마이크로서비스로 옮겨야 하나요?
거의 확실히 아직은 아니며, 어쩌면 영영 아닙니다. 마이크로서비스는 성장하는 SaaS 제품 대부분이 실제로는 갖고 있지 않은 조직·규모 문제를 해결하면서, 일상의 복잡성을 잔뜩 더합니다. 빠르게 바꿀 수 있는 깨끗하고 잘 이해된 모놀리스가, 손대기 두려운 분산 시스템보다 더 잘 확장됩니다. 그 아키텍처로 손을 뻗는 것은 구체적이고 측정된 문제가 그것을 요구할 때뿐이어야 합니다.
코드를 최적화하는 것과 그냥 더 큰 서버를 사는 것 중 무엇이 더 쌀까요?
거의 언제나 먼저 최적화입니다. 더 큰 서버는 약간의 여유를 사 주는 매달의 반복 비용이지만, 빠진 인덱스나 N+1 쿼리를 고치는 것은 대개 한 번의 노력으로 그 후에는 비용이 들지 않으며 흔히 훨씬 큰 이득을 줍니다. 하드웨어는 코드와 쿼리가 이미 깨끗해진 뒤에만 확장하십시오.
전면 재작성이 정말로 옳은 결정일 때는 언제인가요?
드물고, 제품 전체가 아니라 한 번에 한 부분씩입니다. 컴포넌트를 다시 만드는 것은 그것을 바꾸는 일이 교체보다 일관되게 더 느리고 위험해졌고, 같은 버그가 계속 돌아오며, 더 싼 수정을 이미 소진했을 때입니다. 그때도 그 한 컴포넌트를 명확한 경계 너머에서 교체하고 나머지는 계속 돌립니다. 바닥부터의 전면 재작성은 대개 일 년짜리 함정입니다.
사용자가 생기기 전에 확장에 얼마나 투자해야 하나요?
의도적으로, 아주 적게입니다. 빠르게 바꿀 수 있는 깨끗하고 단순한 것을 만들고, 문제가 다가오는 것을 볼 수 있도록 기본 모니터링을 더하고, 그 밖에는 도달하지도 않은 규모를 위한 인프라 구축을 참으십시오. 확장을 위한 최선의 준비는 복잡한 아키텍처가 아닙니다. 단순한 제품과, 진짜 병목이 나타났을 때 그것을 안전하게 바꿀 수 있는 팀입니다.
Have a nice day
Have a nice day
편집팀

Have a nice day는 중소기업의 디지털 전환을 돕는 소프트웨어 스튜디오입니다. 슬라이드에서만이 아니라 일상 업무에서 실제로 작동하는 자동화, AI, 맞춤형 소프트웨어를 만듭니다.

관련 서비스