Vercel의 백엔드는 그냥 다른 백엔드와 같은 줄 알았어요...

발행: (2026년 1월 10일 오전 11:29 GMT+9)
7 분 소요
원문: Dev.to

Source: Dev.to

Vercel과 전통적인 백엔드에 대한 교훈

프로젝트를 통해 Next.js를 깊이 있게 배우는 과정에서, 저는 Next.js 백엔드 서버를 일반적인 Go 백엔드(의존성 주입)처럼 설계하려고 시도했습니다. 하지만 이는 불가능하다는 것을 알게 되었습니다. Vercel의 /api 라우트를 “미니 Express 서버”처럼 취급하는 것은 근본적인 아키텍처 오류입니다. 두 경우 모두 Node.js를 실행하지만, 그 기반 인프라(서버리스 vs. 장기 실행) 때문에 완전히 다른 규칙이 적용됩니다.

비슷한 방식으로 동작하는 다른 서비스로는 Netlify, Cloudflare Pages / Workers, 그리고 AWS Amplify가 있습니다.

1. Process Lifecycle: Ephemeral vs. Persistent

Traditional Backend (Long‑Running)

  • Concept: 프로세스가 시작되면 수동으로 중지하거나 충돌할 때까지 계속 살아 있습니다.
  • Global root: 영구적; 메모리는 한 번 할당되며 수천 개의 요청에 걸쳐 계속 사용할 수 있습니다.
  • Implication: 서버가 내부 RAM에 있는 정보를 며칠, 몇 주 동안 “기억”한다고 믿을 수 있습니다.

Vercel (Serverless)

  • Concept: 프로세스는 일시적입니다. 요청이 들어오면 생성되고 응답이 전송되는 순간(또는 그 순간에) 종료(또는 동결)됩니다.
  • Global root: 자주 파괴됩니다.
  • Implication: 모든 내부 상태가 사라집니다. 모든 요청을 서버가 콜드 스타트를 수행한 것처럼 처리하십시오.

2. State Management: The Global Variable Trap

Traditional Backend (Long‑Running)

  • Memory: 전역 변수는 공유됩니다. User A가 전역 카운터를 업데이트하면, 같은 프로세스를 이용하는 User B는 업데이트된 값을 보게 됩니다.
  • Garbage collection: 프로세스가 살아 있는 한, GC는 전역 변수를 reachable(도달 가능)하다고 판단해 무시하므로, 메모리 내 캐시나 간단한 카운터를 사용할 수 있습니다.

Vercel (Serverless)

  • Memory: 전역 변수는 격리됩니다. 10명의 사용자가 API를 호출하면 Vercel은 10개의 별도 실행 환경(인스턴스)을 띄울 수 있습니다.
  • Trap: User A가 인스턴스 #1에서 카운터를 업데이트합니다. User B가 인스턴스 #2에 접근하면, 해당 인스턴스는 자체 전역 루트를 가지고 있어 카운터 값이 여전히 0입니다.
  • Note: 워밍된 인스턴스는 연속적인 요청 사이에 전역 변수를 유지할 수 있지만, 유휴 상태가 되면 콜드 상태로 전환되어 모든 전역 변수가 사라집니다.
  • Solution: 영속성을 위해 메모리 내 데이터를 의존하지 마세요. Redis나 PostgreSQL과 같은 외부 스토어를 사용하십시오.

3. Concurrency: Thread Pooling vs. Horizontal Scaling

Traditional Backend (Long‑Running)

  • Mechanism: 하나의 서버가 단일 스레드와 이벤트 루프(Node.js)를 사용해 다수의 동시 요청을 처리하고, 연결 풀을 관리합니다.
  • Scaling: 트래픽이 많아지면 서버가 더 열심히 작업하게 되며, 과부하가 걸리면 요청이 대기열에 쌓입니다.

Vercel (Serverless)

  • Mechanism: Vercel은 multiplication 방식으로 확장합니다—트래픽 급증 시 함수의 복사본을 다수 생성합니다.
  • Challenge: 1,000개의 함수가 동시에 시작되면 각각 새로운 데이터베이스 연결을 열어 연결 고갈 위험이 발생하고 DB가 다운될 수 있습니다.
  • Solution: 연결 풀러를 사용합니다(예: Prisma Accelerate 또는 Supabase pooling).

4. 실행 타이밍: 동기 vs. 백그라운드 작업

전통적인 백엔드 (장기 실행)

  • res.send(); fireAndForgetEmail(); 와 같이 응답을 보낸 뒤에도 백그라운드에서 코드를 계속 실행할 수 있습니다.
  • 프로세스가 살아 있기 때문에 백그라운드 작업이 끝날 때까지 진행됩니다.

Vercel (서버리스)

  • 흐름: res.send() 가 호출되는 순간 런타임 환경이 일시 중지되거나 종료됩니다.
  • 재앙: 응답 이후에 시작된 백그라운드 작업은 실행 도중 중단될 수 있어, 작업이 불완전하거나 누락될 수 있습니다 (예: 절반만 전송된 이메일).
  • 해결책: 응답하기 전에 모든 작업을 완료하거나, Upstash Workflow 와 같은 큐 서비스에 장기 실행 작업을 위임합니다.

5. Persistent Connections: WebSockets vs. HTTP

Traditional Backend (Long‑Running)

  • Connectivity: 상태 저장 연결을 지원합니다. 클라이언트는 실시간 채팅이나 라이브 업데이트를 위해 TCP 소켓을 열어둘 수 있습니다(WebSockets); 서버 프로세스는 상대편을 유지할 수 있도록 계속 실행됩니다.

Vercel (Serverless)

  • Connectivity: 무상태 HTTP 요청만 지원합니다. 함수는 응답 후 종료되므로 연결을 유지할 수 없습니다.
  • Result: socket.io와 같은 것을 사용하면 각 요청 후 “서버”가 사라지기 때문에 실패합니다.
  • Solution: Pusher 또는 Ably와 같은 써드파티 실시간‑as‑a‑service 솔루션을 사용하세요.

이 문서는 AI 도움을 받아 개인 메모로 작성되었습니다.

Back to Blog

관련 글

더 보기 »

안녕, 뉴비 여기요.

안녕! 나는 다시 S.T.E.M. 분야로 돌아가고 있어. 에너지 시스템, 과학, 기술, 공학, 그리고 수학을 배우는 것을 즐겨. 내가 진행하고 있는 프로젝트 중 하나는...