비트코인 모의 매매 서비스인 "Dobbit"를 만들면서 서버 관련 배포방식에대해 고민이 많았다. 물론 내가 Upbit Open API에 대해 이해를 잘못해서 '일단 돌아가시잖아~' 로 개발을 진행했는데, 사용자가 많아졌을 때 무조건 문제가 발생할 수 밖에 없는 방식 이기 때문에 문제를 개선해야겠다는 생각이 들었다.

기존 개발 방식의 문제점
Dobbit는 비트코인 관련 정보를 실시간 으로 받아와야한다. 그렇기 때문에 'WebSocket' 을 활용해야한다는 생각이 들었지만, 여러가지 방법을 찾아보니까 WebSocket을 사용할 때는 서버를 직접 AWS EC2와 같은 방법으로 배포해야한다고 한다. 물론 서버를 개발한다면 Express 로 진행할 예정이기 때문에 Vercel을 활용해서 배포할 수 있지만, Vercel은 Serverless 방식이기 때문에 여러가지 제약이 많다고 한다.
서버에 대한 잘못된 생각
처음에는 내가 직접 Websocket 서버를 개발하고, client랑 연결해야한다고 생각했다. 아직까지 Express 로 서버를 개발하는데에는 많이 부족하다고 생각해서 거부감을 가지고 있다. 그래서 일단 서버를 따로 만들지 말고, 다른 방식을 찾아보았다.
아직 DB에 대한 이해도가 매우 부족하기 때문에 DB는 PostgreSQL 을 활용하는 Supabase 를 활용하기로 했다. 지금 인프런에서 수강하고 있는 강의에서 Supabase를 활용방법에 대해 배우고 있었기 때문에 이 강의를 활용해서 충분히 이용할 수 있다고 생각했다. 추가적으로 Supabase MCP 서버롤 활용하고, Copilot을 활용한다면 DB 관련 부분은 충분이 커버할 수 있다는 생각이 들었다. 그러던 중 Supabase 의 Realtime 기능에 대해서 알게되었다. 아직 자세한 방식은 잘 모르지만, 마치 WebSocket 과 비슷한 방식으로 실시간 통신이 가능하다고 한다. 그래서 로그인 관련 부분과, DB 관련 부분, 코인 관련 실시간 정보를 Supabase를 활용해서 진행해보았다.
이렇게 기능 구현을 할때까지 나는 WebSocket 서버를 따로 만들지 않고, Supabase와 Realtime 기능을 활용해서 완벽하게 대체했다고 생각했다. 그리고 Next.js를 vercel 로 배포해서 서비스 배포까지 완료했다. 하지만 이해가 안되는 코드 몇개가 있었다. 아직 Realtime 기능에 대한 이해가 부족하다 보니 Data를 Next.js의 서버에서 Supabase로부터 가져오고, Route Handler 를 사용해서 클라이언트로 호출하는 방식을 사용했다. 이때 React Query의 polling 기능을 활용해서 1초마다 데이터를 가져올 수 있게 기능구현을 진행해보았다.
아직까지도 이 방식이 잘 구현한건지, 잘못 구현한건지 잘 모르겠다. 하지만 확실한건 사용자가 많아져서 API 요청을 많이 보내게 된다면, 분명히 문제가 발생할 것이라고 생각했다. 그러던 중 Upbit Open API 문서를 다시 읽어보았다.

이미 만들어져 있는 WebSocket 서버를 이용할 수 있다는 것이었다!!!!!! 이미 어느정도 기능 구현이 되어있기 때문에 전부 수정하기는 힘들 것 같기때문에 일단 supabase에서 벗어나 직접 DB를 구축하는 방법으로 변경해볼 예정이다.
Vercel 배포의 한계
일단 전체적으로 리팩토링을 진행하기에 앞서, Vercel의 배포방식에 대해 알아볼려고 한다. 보통 서버를 구축할때 Express 로 개발을 진행하기 때문에 무료로 배포할 수 있는 Vercel를 활용한다면 뭐든지 만들수 있다고 생각했다. 심지어 지금은 Copilot과 Gemini 도 활용할 수 있기 때문에 쉽게 서버를 만들고 배포해서 이용할 수 있다고 생각했다. 하지만 Vercel은 Serverless 방식이기 때문에 여러가지 문제점이 발생할 수 있다는 점을 인지하게 되었다.
- Cold Start 로 인한 응답 지연
- 상태 유지의 어려움
- 메모리 및 시간 제한
이 세가지 문제점에 대해 조금 더 알아보도록 하자.
Serverless?
먼저, 서버리스란 서버가 없다는 것이 아니라 개발자가 서버를 관리할 필요 없이 애플리케이션을 빌드하고 실행할 수 있게 해준다. 24시간 내내 돌아가는 것이 아니라 수면상태에 있다가 요청이 들어오면 잠들어 있는 함수를 깨우고 실행시켜 요청한 작업을 수행 하게 된다.
Function as as a Service (FaaS)
Vercel은 내부적으로 AWS Lambda와 비슷한 FaaS 모델을 사용한다.
- Event-Driven (이벤트 기반): 평소에 서버는 꺼져있다가, 클라이언트의 HTTP 요청 (Event) 이 들어오는 순간에만 실행된다.
- Ephemeral (일회성): 동결 처리가 끝나면 일정 시간 후 컨테이너는 동결(Freeze) 되거나 파기(Destroy) 된다. 따라서 함수가 실행되는 동안에만 상태를 유지할 수 있다.
- Stateless (무상태): 각 함수 호출은 독립적이며, 이전 호출의 상태나 세션 정보를 저장하지 않는다. 이는 함수가 독립적이고, 예측가능한 동작을 유지하도록 돕는다.
Cold Start
Cold Start 란 서버 리소스가 할당되지 않은 상태(Idle)에서 요청이 들어왔을 때, 새로운 실행 환경(Container)을 준비하고 코드를 실행 가능한 상태로 만드는 데 걸리는 초기 지연 시간을 의미한다.
Express 서버가 Cold Start에 취약한 이유
Vercel의 Serverless Function은 본래 "하나의 함수가 하나의 기능"을 하도록 설계되어있다. 하지만 Express를 Vercel에 배포한다는 것은 거대한 앱 전체를 하나의 함수 안에 구겨 넣는 것과 같다.
이 구조가 Cold Start 시간을 늘리는 기술적 원인은 다음과 같다.
1. 무거운 초기화 (Heavy Initialization)
Express는 실행 시 라우팅 테이블 등록, 미들웨어 설정, DB 연결 설정 등 전역적인 초기화 작업이 필요하다. Cold Start가 발생할 때마다 이 모든 과정이 처음부터 다시 수행되어야 하므로, 단순한 함수보다 부팅 시간이 훨씬 길다.
2. 큰 번들 사이즈 (Bundle Size)
Cold Start의 '코드 다운로드' 단계는 파일 크기에 비례하여 시간이 걸린다. Express 프레임워크 자체와 관련된 수많은 의존성 패키지(node_modules)들이 함께 번들링되므로, 함수 용량이 커져 로딩 속도가 저하된다.
3. DB 연결 오버헤드 (Connection Overhead)
전통적인 서버는 DB 연결을 한 번 맺어두고 계속 재사용(Connection Pool)한다. 하지만 Serverless 환경에서는 Cold Start가 일어날 때마다 DB 핸드셰이크를 새로 맺어야 한다. 이는 상당한 네트워크 지연(Latency)을 유발한다.
Cold Start 해결 및 최적화 방안
1. 번들 최적화 (Tree Shaking)
사용하지 않는 코드를 제거하고, 거대한 라이브러리 사용을 지양하여 번들 크기를 줄인다. (예: lodash 전체 import 대신 필요한 함수만 import)
2. DB 연결 관리 최적화
함수 외부(Global Scope)에 DB 연결 객체를 캐싱하여, Warm Start 시에는 재연결 없이 기존 연결을 재사용하도록 코드를 작성해야 한다.
3. Edge Functions 활용
Node.js 런타임보다 훨씬 가벼운 V8 기반의 Edge Runtime을 사용하면 Cold Start를 거의 0에 가깝게 줄일 수 있다. (단, Express는 Edge Runtime에서 완벽하게 호환되지 않을 수 있음)
4. Serverless에 맞는 아키텍처 변경
가능하다면 Express라는 거대 프레임워크 대신, Next.js의 Route Handlers를 사용하여 각 엔드포인트를 개별 함수로 분리하는 것이 Vercel 플랫폼 최적화 관점에서 가장 유리하다.
WebSocket 를 Vercel 로 배포하면 안되는 이유
[공식문서]
WebSocket은 지속적으로 연결을 유지 (구독) 해야한다. 하지만 Vercel의 Serverless 방식은 요청이 없으면 함수가 동결(Freeze) 되거나 파기(Destroy) 된다. 따라서 WebSocket 연결이 유지될 수 없기 때문에 WebSocket 서버를 Vercel로 배포하는 것은 적합하지 않다고 한다.
정리
간단한 CRUD 를 사용하는 서버라면 Vercel로 배포하는게 좋을것 같다. 하지만 지속적으로 연결을 유지해야하는 서버가 필요하다면 AWS EC2나 GCP를 이용하거나 Railway 와 같은 서비스를 활용해야할 것 같다. 또한 Next.js의 Route Handler를 잘 활용하는 방법도 좋은 방법일 것 같다.
참고자료
서버리스 아키텍처(Serverless Architecture)와 콜드 스타트(Cold Start)
Vercel과 EC2 배포 시 무슨 차이가 있을까? (서버리스와 Cold Start)
How can I improve function cold start performance on Vercel?