서버리스 커넥션 풀링이란?

업데이트: 2026년 11월

서버리스 커넥션 풀링은 소수의 실제 데이터베이스 연결을 수명이 짧은 다수의 함수 인스턴스가 공유하게 하는 기법입니다. 이 기법이 존재하는 이유는 두 확장 모델이 서로 맞지 않기 때문입니다. 서버리스 컴퓨팅은 부하에 인스턴스를 늘리는 방식으로 대응하는 반면, 데이터베이스는 모든 연결을 메모리를 점유하는 비싼 자원으로 취급하며 엄격한 상한을 둡니다. 둘을 단순하게 결합하면 적당한 트래픽 급증만으로도 too many connections 장애가 됩니다.

핵심 요약

질문답변
충돌함수는 복제로 확장되고, 연결은 상한이 있으며 비쌈
증상급증 시 FATAL: sorry, too many clients already
해결책풀러가 많은 클라이언트를 소수의 실제 연결 위에서 다중화
조절 다이얼트랜잭션 vs. 세션 풀링: 공유 효율 vs. 호환성
BaaS의 답클라이언트는 무상태 HTTPS로 통신하고, 풀은 플랫폼이 소유

데이터베이스에서 관찰한 장애

-- 서버리스 트래픽 급증 중 데이터베이스가 보는 모습:
SELECT state, count(*)
FROM pg_stat_activity
GROUP BY state;
--  active               |   14
--  idle                 |  483     ← 웜 함수 인스턴스가 붙잡아 둔 연결
SHOW max_connections;    --  100    ← 이미 뚫려 버린 상한

-- 각 연결은 실제 메모리를 점유하는 실제 프로세스입니다.
-- 그래서 상한이 존재하며, 상한을 올리는 것은 해결책이 아닙니다.

문제의 형태는 이렇습니다. 함수 인스턴스마다 자체 연결을 열고, 호출 사이의 유휴 시간에도 그 연결을 유지하며, 인스턴스 수는 개발자가 아니라 트래픽이 결정합니다. 재사용은 웜 인스턴스 안에서만 일어나고 인스턴스 사이에서는 결코 일어나지 않으므로, 동시성 300은 연결 하나짜리 풀 300개를 뜻합니다. max_connections를 올리면 실제 메모리 비용을 치르고 여유를 확보할 뿐, 더 큰 급증이 오면 다시 한계에 부딪힙니다.

관리형 백엔드에서는 애플리케이션 코드가 이 문제에서 완전히 벗어납니다. SDK 호출은 무상태 HTTPS 요청이며, 어떤 클라이언트도 데이터베이스 연결을 보유하지 않습니다.

// JavaScript / Node.js — Back4app JS SDK
// Inside a serverless function: no driver, no pool, no connection to leak.
// Each SDK call is a stateless HTTPS request; pooling happens platform-side.
Parse.initialize('APP_ID', 'JS_KEY');
Parse.serverURL = 'https://parseapi.back4app.com';

export async function handler(event) {
  const query = new Parse.Query('Order');
  query.equalTo('status', 'pending');
  query.limit(20);
  const orders = await query.find();  // request returns; nothing stays open
  return orders.map((o) => o.id);
}
// 1,000 concurrent invocations = 1,000 HTTP requests,
// not 1,000 database connections.

풀러가 팬아웃을 흡수하는 방식

서버리스 함수와 데이터베이스 사이의 커넥션 풀러수명이 짧은 함수 인스턴스 수백 개가 풀러에 비용이 싼 클라이언트 연결을 열고, 풀러는 이를 소수의 고정된 실제 데이터베이스 연결 위에서 다중화해 데이터베이스를 연결 상한 아래로 유지합니다.

실제 연결 20개

실패하는 대신
잠시 대기열에서 기다림

함수 ×500
(급증)

풀러
(예: PgBouncer)

함수 ×500
(다음 급증)

데이터베이스
max_connections: 100

수명이 짧은 함수 인스턴스 수백 개가 풀러에 비용이 싼 클라이언트 연결을 열고, 풀러는 이를 소수의 고정된 실제 데이터베이스 연결 위에서 다중화해 데이터베이스를 연결 상한 아래로 유지합니다.

PgBouncer 같은 풀러는 클라이언트 연결을 수천 개 단위로 받아들이고(풀러 쪽에서는 각각의 비용이 쌉니다), 작업이 도착할 때만 소수의 풀에서 실제 서버 연결을 빌려줍니다. 데이터베이스는 안정적인 연결 20개를 보고, 함수 500개는 각자 연결을 하나씩 가졌다고 믿습니다. 수요가 풀을 넘으면 클라이언트는 오류를 받는 대신 대기열에서 몇 밀리초를 기다립니다. 치명적인 실패가 약간의 지연 시간으로 바뀌는 것입니다.

풀 크기 산정은 직관이 가장 크게 틀리는 지점입니다. 데이터베이스의 유효 병렬성은 코어와 디스크에 묶여 있습니다. 처리량은 보통 풀 크기가 수십 개 이내의 낮은 수준, 즉 코어 수의 두 배 근처일 때 정점에 이르고, 연결이 더 늘면 경합이 커져 오히려 떨어집니다. 풀러의 계산이 성립하는 이유는 서버리스 요청이 짧기 때문입니다. 8 ms짜리 트랜잭션을 처리하는 연결 하나는 초당 100건을 훌쩍 넘게 처리할 수 있으므로, 실제 연결 20개로 수천 건의 함수 호출을 넉넉히 흡수합니다. 풀은 편의를 위한 캐시가 아니라, 대기 비용이 싼 곳에 의도적으로 둔 병목입니다.

결정적인 조절 다이얼은 임대가 얼마나 오래 지속되는가입니다.

트랜잭션 풀링 vs. 세션 풀링

항목세션 풀링트랜잭션 풀링
임대 기간클라이언트 세션 전체트랜잭션 하나, 이후 회수
공유 배수낮음: 유휴 클라이언트가 연결을 붙잡아 둠높음: 짧은 서버리스 작업에 이상적
Prepared statement완전히 지원프로토콜 수준 지원을 명시적으로 설정한 경우에만
세션 상태(SET, 임시 테이블, LISTEN/NOTIFY)동작함깨짐: 트랜잭션마다 다른 연결에 닿을 수 있음
적합한 용도수명이 긴 앱, 관리 도구, 마이그레이션웹과 서버리스의 요청/응답 트래픽

트랜잭션 모드는 서버리스 워크로드를 가능하게 만드는 요소입니다. 8 ms 트랜잭션 하나를 실행하는 함수가 몇 분에 걸친 웜 수명 내내 연결을 소유해서는 안 됩니다. 하지만 공짜는 아닙니다. SQL 문 사이에 “내 연결”이 유지된다고 가정하는 모든 것이 미묘한 버그가 됩니다. 일반적인 권장 사항은 요청 트래픽에는 트랜잭션 풀링을, 마이그레이션과 상태가 있는 모든 작업에는 직접 연결이나 세션 풀링 연결을 쓰는 것입니다.

일반적인 사용 사례

  • SQL에 접근하는 서버리스 함수와 엣지 함수. 대표적인 사례로, 급변하는 인스턴스 수가 고정된 연결 예산과 맞부딪힙니다.
  • 작은 서비스 여럿, 데이터베이스 하나. 서비스 20개 × 기본 풀 크기 10 = 아무도 계획하지 않은 연결 200개. 공유 풀러를 두면 연결 수를 다시 통제할 수 있습니다.
  • 멀티테넌트 플랫폼. 테넌트별 워크로드가 연결 수요를 몇 배로 늘리며, 풀링을 쓰면 공유 데이터베이스의 연결 예산을 계속 지킬 수 있습니다.
  • 작은 데이터베이스에 몰리는 트래픽 버스트. 출시일의 급증이 데이터베이스에서 오류를 내는 대신 풀러에서 몇 밀리초 동안 대기합니다.
  • 장애 중 프라이머리 보호. 고정 크기 풀은 격벽(bulkhead)입니다. 폭주하는 클라이언트는 데이터베이스의 메모리가 아니라 풀러의 대기열을 소진합니다.

자체 풀러를 운영해야 할까? 결정 매트릭스

풀러를 운영해야 하는 경우건너뛰어도 되는 경우
함수나 여러 서비스가 SQL에 직접 연결함관리형 BaaS가 API 계층에서 클라이언트 트래픽을 종료함
부하 급증 시 연결 오류가 나타남클라이언트 측 풀을 갖춘 소수의 장기 실행 서버에서 오는 꾸준한 트래픽
데이터베이스와 그 예산을 직접 운영함플랫폼이 이미 API 계층과 데이터베이스 사이에서 풀링함
여러 테넌트나 팀이 데이터베이스 하나를 공유함앱 하나, 풀 하나로 한도 안에 충분히 들어오는 워크로드
핵심 구성 요소를 하나 더 운영할 여력이 있음풀러를 담당하는 온콜이 없음

솔직히 말해 둘은 대등하지 않습니다. 풀러는 연결 고갈을 해결하지만, 관리형 백엔드는 연결 고갈이 생길 여지를 아예 없앱니다. 클라이언트가 자동 생성된 API와 HTTPS로 통신하면 팬아웃은 데이터베이스 계층에 아예 닿지 않습니다. 풀링은 여전히 일어나지만, 직접 운영하는 구성 요소가 아니라 다른 누군가가 충분히 검증한 인프라로서 일어납니다.

한계와 트레이드오프

  • 풀러는 하나의 구성 요소입니다. 배포, 모니터링, 장애 조치, 업그레이드가 필요합니다. 자원 문제를 운영 문제와 맞바꾼 것이며, 유리한 거래이긴 하지만 비용이 없는 것은 아닙니다.
  • 트랜잭션 모드는 의미를 바꿉니다. prepared statement, 세션 변수, advisory lock이 문서대로 동작하지 않게 됩니다. 실패는 간헐적이고 부하에 따라 달라지는, 가장 나쁜 종류입니다.
  • 홉이 하나 더 늘면 지연 시간이 듭니다. 같은 네트워크 안에서는 보통 1밀리초 미만이지만, 모든 쿼리의 경로 위에 있습니다.
  • 풀러가 병목이 될 수 있습니다. 단일 스레드 풀러는 격렬한 급증 아래에서 대기열이 깊어집니다. 상한은 이동했을 뿐 사라지지 않았습니다.
  • 풀링은 느린 쿼리를 고치지 않습니다. 풀링은 연결을 배급할 뿐이며, 몇 초 동안 연결을 붙잡는 쿼리는 풀을 고갈시킵니다. 빠른 쿼리와 그 뒤의 인덱스가 여전히 진짜 용량 조절 수단입니다.

Back4app에서의 커넥션 풀링

Back4app은 관리형 데이터베이스, 자동 생성되는 REST 및 GraphQL API, 인증, 파일 스토리지, Cloud Code 서버리스 함수를 결합한 오픈소스 Backend as a Service(BaaS) 플랫폼입니다. 이 아키텍처는 풀링 문제에 구조적으로 답합니다. 클라이언트 앱과 Cloud Code 함수는 무상태 HTTPS 요청을 보내고, 개발자의 코드가 아니라 플랫폼의 서버 계층이 관리형 데이터베이스에 대해 수명이 길고 적정 크기인 커넥션 풀을 유지합니다. 컴퓨팅 쪽의 인스턴스 팬아웃이 데이터베이스 쪽의 연결 팬아웃으로 이어지는 일은 없으며, 이 글에서 다룬 기법들이 얻으려는 것이 바로 이 속성입니다.

자주 묻는 질문

서버리스는 왜 데이터베이스 연결을 고갈시킵니까?

확장 모델이 충돌하기 때문입니다. 서버리스 컴퓨팅은 요청에 따라 인스턴스를 늘리지만, 데이터베이스의 연결 예산은 고정되어 있습니다(기본값은 보통 100 안팎). 함수 인스턴스마다 자체 연결을 열고 유휴 시간에도 유지하므로, 인스턴스 500개를 띄우는 트래픽 급증은 한꺼번에 연결 500개를 요구합니다. 데이터베이스는 초과분을 거부하고, 관련 없는 쿼리까지 실패하기 시작합니다.

PgBouncer 같은 커넥션 풀러는 실제로 무엇을 합니까?

코드와 데이터베이스 사이에 위치해 비용이 싼 클라이언트 연결을 수천 개 받아들이고, 이를 소수의 실제 서버 커넥션 풀 위에서 다중화합니다. 클라이언트는 연결을 가지고 있다고 생각하지만 실제로는 트랜잭션이나 세션 동안 하나를 빌렸다가 반납합니다. 함수가 몇 개나 실행되든 데이터베이스는 안정적인 고정 연결 집합만 보게 됩니다.

트랜잭션 풀링과 세션 풀링의 차이는 무엇입니까?

임대 기간입니다. 세션 풀링은 클라이언트의 세션 전체 동안 실제 연결을 할당하므로 모든 기능에 안전하지만, 유휴 클라이언트 하나도 연결 하나를 붙잡아 둡니다. 트랜잭션 풀링은 트랜잭션이 실행되는 동안에만 연결을 빌려주고 회수하므로 공유 효율이 크게 높아지지만 세션 상태가 깨집니다. prepared statement, 세션 변수, LISTEN/NOTIFY는 주의가 필요하며 그렇지 않으면 실패합니다.

함수가 확장될 때 연결 수는 왜 급증합니까?

서버리스 플랫폼은 인스턴스를 복제해 동시성을 처리하고, 각 복제본은 자체 데이터베이스 클라이언트를 초기화합니다. 연결 재사용은 하나의 웜 인스턴스 안에서만 일어나며 인스턴스 사이에서는 결코 일어나지 않습니다. 따라서 동시성 300은 크기 1짜리 풀 300개를 뜻합니다. 공유는 전혀 없이 풀링의 오버헤드만 떠안는 최악의 형태입니다. 이 팬아웃은 구조적인 것이지 코드의 버그가 아닙니다.

데이터베이스 커넥션 풀은 얼마나 커야 합니까?

직관보다 훨씬 작아야 합니다. 처리량은 보통 풀 크기가 수백이 아니라 데이터베이스의 실제 병렬성 근처일 때 정점에 이릅니다. 흔한 경험칙은 코어 수의 두 배이며, 디스크 대기에 따라 조정합니다. 그 이상의 연결은 처리량은 늘리지 않고 경합과 메모리 비용만 더합니다. 풀러의 역할은 바로 작은 풀을 호출자에게 무한한 것처럼 느끼게 하는 것입니다.

BaaS를 사용해도 풀러가 필요합니까?

여러분의 앱이 보내는 트래픽에는 필요 없으며, 바로 그 점이 핵심입니다. 관리형 백엔드는 클라이언트 요청을 API 계층에서 무상태 HTTPS 호출로 종료하고, 자체 서버 계층이 데이터베이스에 대해 수명이 길고 적절한 크기의 풀을 유지합니다. 함수와 앱은 데이터베이스 연결을 전혀 보유하지 않으므로 고갈 시나리오는 완화되는 것이 아니라 아예 사라집니다. 풀러는 외부 도구를 데이터베이스에 직접 연결할 때만 다시 필요해집니다.

커넥션 풀러의 단점은 무엇입니까?

네트워크 홉 하나만큼의 지연 시간이 더해지고, 직접 운영하고 모니터링해야 하는 구성 요소가 되며, 트랜잭션 모드는 의미를 조용히 바꿉니다. 세션 범위 기능이 문서대로 동작하지 않게 됩니다. 단일 풀러 프로세스는 급증하는 부하에서 그 자체가 병목이 될 수 있고, 크기를 잘못 잡으면 대기열의 위치만 옮겨집니다. 필수 인프라이지만, 결국 직접 운영해야 할 인프라가 하나 더 늘어나는 셈입니다.

관련 용어

비교 대상

참고 자료

백엔드를 구축할 준비가 되셨나요?

몇 분 안에 Back4app에서 프로젝트를 시작하세요. 데이터베이스, 인증, API, Cloud Code가 포함되어 있으며 신용카드는 필요 없습니다.

작성 및 검수: Back4app Engineering, Back4app Engineering · 게시일 2026-11-03