Articlestack.convex.dev·2026년 7월 8일·0

Building a 70-Module Convex Backend: Web, Mobile & API in One Deployment

Quick Summary

저자는 Client Commander를 하나의 Convex 백엔드로 확장하면서 웹 대시보드, 모바일 앱, 백그라운드 동기화, REST API를 같은 배포 안에서 운영할 수 있었고, 권한·타입·워크플로·검색이 플랫폼 기본 구조에 자연스럽게 붙었다고 설명한다.

Building a 70-Module Convex Backend: Web, Mobile & API in One Deployment 관련 대표 이미지

🖼️ 인포그래픽

Building a 70-Module Convex Backend: Web, Mobile & API in One Deployment 내용을 설명하는 본문 이미지

🖼️ 4컷 인포그래픽

Building a 70-Module Convex Backend: Web, Mobile & API in One Deployment 내용을 설명하는 본문 이미지

💡 한 줄 요약

저자는 Client Commander를 하나의 Convex 백엔드로 확장하면서 웹 대시보드, 모바일 앱, 백그라운드 동기화, REST API를 같은 배포 안에서 운영할 수 있었고, 권한·타입·워크플로·검색이 플랫폼 기본 구조에 자연스럽게 붙었다고 설명한다.

📌 핵심 요약

  • Client Commander는 처음에는 Next.js 앱 하나와 약 10개 테이블로 시작했지만, 1년 뒤에는 웹 대시보드, 모바일 앱, 백그라운드 동기화 서비스, 40개 이상의 REST 엔드포인트를 가진 API까지 같은 Convex 배포에서 처리하는 50개 이상 테이블·70개 이상 모듈 규모로 커졌다.
  • Convex 쿼리가 일회성 요청이 아니라 구독처럼 동작하기 때문에, 사용자의 역할이나 연락처 데이터가 바뀌면 관련 쿼리가 자동으로 다시 평가되어 권한 변경이 대시보드에 실시간으로 반영된다.
  • Expo 모바일 앱과 Next.js 웹 앱은 같은 생성 API와 타입을 가져다 쓰며, 스키마나 쿼리가 바뀌면 두 플랫폼의 호출 지점이 TypeScript 단계에서 함께 깨져 모바일과 웹의 동기화 문제를 배포 전에 발견할 수 있었다.
  • 외부 연동을 위해 REST API와 웹훅을 추가했지만 별도 API 서버를 만들지 않았고, HTTP 액션이 인증·레이트 리밋·서명 검증 뒤 기존 UI와 같은 쿼리·뮤테이션을 호출하는 얇은 래퍼 역할을 했다.
  • 며칠 또는 몇 주에 걸친 워크플로는 단계별 체크포인트 덕분에 배포나 서버 재시작 후에도 이어졌고, 검색은 스키마의 searchIndex와 일반 인덱스로 처리되어 별도 검색 인프라 없이 빠른 조회를 구현했다.

🧩 주요 포인트

  1. Client Commander는 처음에는 Next.js 앱 하나와 약 10개 테이블로 시작했지만, 1년 뒤에는 웹 대시보드, 모바일 앱, 백그라운드 동기화 서비스, 40개 이상의 REST 엔드포인트를 가진 API까지 같은 Convex 배포에서 처리하는 50개 이상 테이블·70개 이상 모듈 규모로 커졌다.
  2. Convex 쿼리가 일회성 요청이 아니라 구독처럼 동작하기 때문에, 사용자의 역할이나 연락처 데이터가 바뀌면 관련 쿼리가 자동으로 다시 평가되어 권한 변경이 대시보드에 실시간으로 반영된다.
  3. Expo 모바일 앱과 Next.js 웹 앱은 같은 생성 API와 타입을 가져다 쓰며, 스키마나 쿼리가 바뀌면 두 플랫폼의 호출 지점이 TypeScript 단계에서 함께 깨져 모바일과 웹의 동기화 문제를 배포 전에 발견할 수 있었다.
  4. 외부 연동을 위해 REST API와 웹훅을 추가했지만 별도 API 서버를 만들지 않았고, HTTP 액션이 인증·레이트 리밋·서명 검증 뒤 기존 UI와 같은 쿼리·뮤테이션을 호출하는 얇은 래퍼 역할을 했다.
  5. 며칠 또는 몇 주에 걸친 워크플로는 단계별 체크포인트 덕분에 배포나 서버 재시작 후에도 이어졌고, 검색은 스키마의 searchIndex와 일반 인덱스로 처리되어 별도 검색 인프라 없이 빠른 조회를 구현했다.

🧠 상세 정리

1. 작게 시작한 백엔드가 하나의 배포 안에서 커진 과정

저자는 Client Commander를 처음 만들 때 Next.js 앱 하나와 약 10개의 테이블만 갖고 있었다고 설명한다. 그러나 1년 뒤 같은 Convex 배포는 웹 대시보드, 모바일 앱, 백그라운드 동기화 서비스, 전체 REST API까지 담당하게 되었다. 규모도 50개 이상의 테이블과 70개 이상의 모듈로 커졌지만, 저자는 이런 확장을 처음부터 계획한 것이 아니라 계속 기능을 추가했을 뿐이라고 말한다. 놀라운 점은 구조가 무너지지 않았다는 데 있다. 글의 핵심은 복잡한 아키텍처를 사전에 설계했다는 자랑이 아니라, 하나의 백엔드가 여러 클라이언트와 접근 방식을 감당하는 과정에서 Convex의 기본 모델이 어떤 역할을 했는지를 보여주는 데 있다.

2. Client Commander의 도메인과 빠르게 늘어나는 요구사항

Client Commander는 여러 회사가 가입해 팀원을 추가하고 연락처와 거래를 관리하는 멀티테넌트 CRM이다. 글에서 말하는 Agents는 시스템을 사용하는 직원, Contacts는 추적 대상인 사람이나 고객, Companies는 SaaS 고객이 되는 테넌트를 뜻한다. 저자는 CRM이라는 도메인 자체가 혁신적인 것은 아니라고 선을 긋지만, 실제 요구사항은 빠르게 복잡해진다고 설명한다. 권한 관리, 실시간 동기화, 모바일 지원, API 접근, 백그라운드 작업이 모두 필요해지기 때문이다. 즉 이 글은 화려한 도메인보다 흔한 SaaS 제품이 성장하면서 맞닥뜨리는 기술적 누적 부담을 중심으로 전개된다.

3. 권한 변경이 실시간으로 반영되는 구조

저자는 사용자의 역할을 바꾸면 대시보드가 새로고침이나 로그아웃 없이 즉시 다른 데이터를 보여준다고 설명한다. 이를 위해 별도의 복잡한 실시간 권한 시스템을 만든 것이 아니라, 쿼리 상단에서 일반적인 권한 검사를 했을 뿐이라고 말한다. Convex의 쿼리는 일회성 요청이 아니라 구독처럼 계속 살아 있으며, 기반 데이터가 바뀔 때마다 다시 평가된다. 따라서 연락처가 바뀌어도 쿼리가 다시 실행되고, 사용자의 역할 레코드가 바뀌어도 권한 검사가 다시 수행된다. 플랫폼이 쿼리가 실제로 읽은 데이터 의존성을 추적하기 때문에, 역할을 바꿨을 때 그 역할에 의존하는 쿼리만 다시 실행되는 점도 강조된다.

4. 모바일 앱을 추가해도 백엔드를 새로 만들지 않은 이유

모바일 앱을 출시할 때 저자는 새로운 백엔드 코드를 추가하지 않았다고 말한다. 더 중요한 효과는 시간을 아낀 것보다 모바일이 웹과 어긋나는 문제를 사용자 버그 리포트가 아니라 개발 중 컴파일 단계에서 발견하게 된 점이었다. Expo 앱은 Next.js 앱과 같은 Convex 생성 API를 import하고, 같은 쿼리와 같은 뮤테이션, 같은 타입을 사용한다. 쿼리 버그를 고치면 양쪽이 함께 고쳐지고, 스키마가 바뀌면 두 앱이 해당 변화를 처리할 때까지 타입 오류로 드러난다. 모노레포에서 백엔드가 공유 패키지로 존재하고 스키마가 단일 진실 공급원이 되기 때문에, 플랫폼 간 타입 중복이나 수동 동기화 절차가 줄어든다.

5. 실시간 UI와 REST API를 같은 코드 경로로 연결

저자는 대시보드에는 실시간 기능이 좋지만 외부 통합은 WebSocket이 아니라 REST와 HTTP 엔드포인트를 원한다고 설명한다. 그래서 40개 이상의 REST 엔드포인트를 추가했지만, 별도의 서비스나 API 서버를 만들지는 않았다. HTTP 레이어는 API 키를 검증하고 레이트 리밋을 적용한 뒤, UI에서 쓰는 것과 같은 내부 쿼리나 뮤테이션을 호출하는 얇은 래퍼로 동작한다. 예시에서는 /v1/contacts GET 요청이 인증을 통과한 뒤 ctx.runQuery로 contacts 목록을 가져와 JSON 응답을 반환한다. 웹훅도 같은 방식으로 서명을 검증한 뒤 UI가 호출하는 것과 같은 뮤테이션을 실행하므로, 두 구현을 따로 유지하지 않아도 된다는 점이 장점으로 제시된다.

6. 배포와 재시작을 견디는 장기 워크플로

Client Commander에는 다음 단계를 실행하기 전 3일을 기다리거나, 어떤 경우에는 일주일을 기다리는 워크플로가 있다. 저자는 배포를 해도 이런 작업을 잃지 않고, 반쯤 끝난 워크플로 때문에 아침에 문제를 발견하는 일도 없었다고 설명한다. 단순한 지연 실행은 scheduler.runAfter로 가능하지만, 기다렸다가 X를 하고 다시 기다렸다가 Y를 하는 체인에서는 어느 단계가 끝났는지, 실패하면 어떻게 재시도할지, 상태를 어떻게 관리할지가 문제가 된다. Convex의 workflow 컴포넌트는 각 단계를 기록하고 체크포인트를 남긴다. 중간에 서버가 재시작되거나 새 배포가 일어나도 이미 실행된 단계는 건너뛰고 멈춘 지점부터 이어가기 때문에, 별도 작업 큐나 stuck job 폴링 없이 온보딩 시퀀스와 체험판 만료 같은 자동화를 운영할 수 있었다.

7. 검색과 인덱스를 인프라 프로젝트가 아닌 스키마 변경으로 처리

연락처에 대한 전체 텍스트 검색이 필요해졌을 때 저자는 처음에는 Elasticsearch 같은 별도 검색 인프라를 떠올렸지만, 실제 구현은 스키마에 searchIndex를 추가하는 방식이었다고 말한다. contacts 테이블에 fullName을 검색 필드로 지정하고 companyId를 필터 필드로 둔 뒤 배포하면, 회사별 필터와 관련도 기반 검색이 동작한다. 같은 패턴은 전화번호 조회, 거래 단계별 조회, 다음 작업 마감일 정렬에도 반복되었다. 필요한 조회가 생기면 인덱스를 추가하거나, nextTaskDueAt처럼 조회를 빠르게 만들기 위해 값을 비정규화해 저장했다. 데이터와 검색 인덱스가 트랜잭션과 함께 갱신되기 때문에 별도 동기화나 검색 인덱스 지연 문제를 다루지 않아도 된다는 점이 핵심이다.

8. 플랫폼이 줄여준 부담과 남은 트레이드오프

저자는 Convex를 쓰면서 WebSocket 인프라, 캐시 무효화, 푸시 로직, 별도 API 서버, 연결 풀 관리, 검색 서비스 운영 같은 일을 직접 관리하지 않았다고 정리한다. 권한은 쿼리 의존성 추적과 재평가로 실시간 반영되었고, 타입은 생성 API를 통해 웹과 모바일의 계약 역할을 했으며, HTTP 라우트는 기존 백엔드 함수 위에 얇게 올라갔다. 워크플로는 체크포인트로 장기 작업을 이어갔고, 검색은 스키마와 인덱스 변경으로 추가되었다. 다만 저자는 모든 것이 공짜라고 말하지는 않는다. fullName이나 nextTaskDueAt처럼 값을 미리 계산해 저장하는 비정규화를 더 많이 하게 되어 쓰기 로직은 조금 복잡해졌지만, 그 대가로 쿼리는 빠르게 유지되고 별도 인프라 프로젝트를 만들 필요가 없었다는 결론을 낸다.

🧾 핵심 주장 / 시사점

  • 이 글의 중심은 기능 목록보다 ‘하나의 코드 경로’에 있다. 웹, 모바일, REST, 웹훅이 같은 쿼리·뮤테이션과 생성 타입을 공유하자, 구현 중복과 플랫폼 간 드리프트가 줄어드는 구조가 만들어졌다.
  • Convex의 실시간 모델은 별도 구독 채널을 설계하는 방식이 아니라 쿼리가 읽은 데이터 의존성을 기반으로 다시 평가되는 방식이다. 그래서 권한 같은 애플리케이션 로직도 실시간 반응성의 일부가 된다.
  • 저자가 인정한 주요 트레이드오프는 비정규화다. 별도 검색·동기화 인프라를 줄이는 대신 fullName이나 nextTaskDueAt 같은 조회용 값을 저장해 쓰기 로직을 더 신경 써야 하지만, 그 결과 읽기 성능과 운영 단순성을 얻었다.

✅ 액션 아이템

  • 웹 대시보드·모바일 앱·백그라운드 동기화·REST API를 같은 Convex 배포에서 운영할 때 50개 이상 테이블·70개 이상 모듈 구성을 기준으로 확장 계획을 정한다.
  • Convex 구독형 쿼리 특성을 반영해 역할·연락처 변경 시 권한 업데이트가 대시보드에 실시간 반영되는지 배포 전 시나리오로 검증한다.
  • Expo와 Next.js가 공유하는 생성 API·타입 기반으로 스키마나 쿼리 변경 시 모바일·웹 호출 지점이 동시에 타입 오류를 일으키는지 사전 점검한다.

❓ 열린 질문

  • 단일 배포 전략에서 웹, 모바일, API, 백그라운드 동기화가 늘었을 때 기능 확장에 따른 성능 경계는 어디인가?
  • 권한·연락처 변경이 발생했을 때 Convex 구독형 쿼리의 재평가 지연이 허용 가능한 실시간 반영 한계는 무엇인가?
  • REST HTTP 액션이 인증·레이트 리밋·서명 검증 뒤 UI의 기존 쿼리·뮤테이션을 호출할 때 실패 처리를 어떤 방식으로 우선순위화해야 하는가?

관련 문서

공통 태그와 주제 흐름을 기준으로 같이 보면 좋은 문서를 이어서 제안합니다.