콜드 스타트의 신화: 이벤트를 위한 JVM 최적화
SchemaBridge Team · 2026-01-20 · Serverless, Performance, JVM, Java
Java가 더 이상 서버리스에 느리지 않은 이유. 스레드 풀, JIT 워밍업, 그리고 장기 실행 컨텍스트.
현대 클라우드의 지연 시간 세금
"서버리스(Serverless)"는 우리를 구원해 줄 것이라 약속했습니다. 무한한 확장성과 관리 부담 제로를 약속했죠. 하지만 사용자 대면 지연 시간(User-Facing Latency) 측면에서, 범용 서버리스 함수(FaaS)에는 지저분한 비밀이 하나 있습니다. 바로 콜드 스타트(Cold Start)입니다.
한동안 실행되지 않은 Lambda에 요청이 도달하면, 프로바이더는 컨테이너를 새로 띄우고, 런타임을 부팅하고, 코드를 로드해야 합니다. 복잡한 애플리케이션의 경우 이 과정은 몇 초가 걸릴 수 있습니다. 현대 웹 애플리케이션의 세계에서 2초는 영겁의 시간입니다. "쾌적한(Snappy)" 경험과 "고장 난(Broken)" 경험의 차이가 바로 여기서 갈립니다.
수년간 업계의 통념은 이러했습니다: "Java는 서버리스에 쓰기엔 너무 무겁다. Go나 Node.js를 써라."
SchemaBridge는 이러한 통념에 도전합니다. 우리는 JVM(Java Virtual Machine)의 견고함에 의존하며, Java를 포기하는 대신 그것이 실행되는 방식을 최적화함으로써 콜드 스타트 문제를 제거하도록 시스템을 설계했습니다.
JVM: 단거리 주자가 아니라 짐 나르는 짐승
JVM은 단명하는 함수가 아니라 장기 실행 서버 프로세스를 위해 설계되었습니다. 초기 구동 시점에는 클래스를 로드하고, 바이트코드를 인터프리트하고, 핫 패스를 최적화(JIT 컴파일)하는 "워밍업" 과정을 거칩니다.
만약 Java 애플리케이션을 100ms 만에 소멸하는 "함수"처럼 다룬다면, 여러분은 런타임의 물리 법칙에 맞서 싸우고 있는 셈입니다. JIT 최적화의 이점은 전혀 누리지 못한 채, 매 요청마다 시작 비용만 지불하게 됩니다.
활용 방식: 장기 실행 애플리케이션 컨텍스트
SchemaBridge는 워크플로 단계를 격리된, 일시적인 함수로 실행하지 않습니다. 대신 우리는 장기 실행 워커 모델(Long-Running Worker Model)을 사용합니다.
1. 스레드 풀링: 우리는 영속적인 애플리케이션 컨텍스트 내에서 워밍업된 스레드 풀을 유지합니다. 이벤트가 도착하면, 이미 존재하는 워밍업된 스레드가 이를 처리합니다. 새로 스폰할 OS 프로세스도, 부팅할 JVM도 없습니다. 실행은 즉각적입니다.
2. JIT 최적화: 우리 워커는 며칠에서 몇 주씩 실행되기 때문에, JVM의 C2 컴파일러가 여러분의 로직을 네이티브 머신 코드 수준의 속도로 최적화할 시간을 충분히 갖습니다. 코드는 실행될수록 실제로 더 빨라집니다.
3. 커넥션 풀링: 데이터베이스 연결을 관리하는 것은 비용이 큽니다. FaaS 모델에서는 연결을 끊임없이 열고 닫아야 합니다. 우리의 장기 실행 워커는 DynamoDB 및 외부 서비스와의 정상적인 연결 풀을 유지함으로써, 호출당 수십 밀리초의 지연을 줄여줍니다.
미래: GraalVM과 네이티브 이미지
현재는 워밍업된 표준 JVM에 의존하고 있지만, 우리는 GraalVM 네이티브 이미지(Native Image)를 적극적으로 실험하고 있습니다.
네이티브 이미지를 사용하면 Java 애플리케이션을 사전 컴파일(AOT, Ahead-Of-Time)하여 독립 실행형 바이너리로 만들 수 있습니다. 이를 통해 JVM 워밍업 단계를 완전히 제거할 수 있습니다.
- 시작 시간: ~1초에서 ~50ms로 단축.
- 메모리 사용량: 5배 감소.
이 기술은 Java 생태계의 생산성과 Go나 Rust의 시작 속도 사이의 간극을 메워줍니다. GraalVM이 성숙해짐에 따라 이는 우리 인프라의 핵심 요소가 될 것이며, "콜드 스타트"의 불이익 없이 부하 급증에 대응해 새로운 워커를 동적으로 띄울 수 있게 해줄 것입니다.
사례 연구: 고빈도 광고 입찰
우리는 입찰 요청을 50ms 이내에 처리해야 하는 애드테크(AdTech) 기업과 협업했습니다.
문제
그들은 Java 기반의 표준 AWS Lambda를 사용하고 있었습니다. 주기적인 콜드 스타트로 인해 p99 지연 시간이 2초에 달했습니다. 이는 입찰 기회의 5%를 놓치고 있다는 의미였습니다.
SchemaBridge의 해법
그들은 입찰 로직을 SchemaBridge의 워밍업된 워커 풀로 이전했습니다.
1. 워밍업된 스레드: 로직은 사전에 워밍업된 스레드에서 실행되었습니다.
2. 결과: p99 지연 시간이 18ms로 떨어졌습니다.
3. 안정성: 컨테이너 프로비저닝을 기다릴 필요가 없어지면서 지연 시간의 변동폭(지터)이 사실상 사라졌습니다.
비교: 일시적 FaaS vs. SchemaBridge 워커
| Feature | 표준 FaaS (Lambda) | SchemaBridge 워밍업 워커 |
| :--- | :--- | :--- |
| 콜드 스타트 | 200ms - 2초 | < 1ms (큐 폴링 시간) |
| 런타임 | 요청 시 부팅 | 영속적 프로세스(Durable Process) |
| 최적화 | 없음 (코드가 일찍 소멸) | 완전한 JIT 컴파일 |
| 연결 | 빈번하게 재수립 | 풀링 및 재사용 |
| 비용 모델 | 요청당 과금 (단가 높음) | 워커 시간당 과금 (단가 낮음) |
Java 성능을 위한 전문가 체크리스트
1. 리소스 재사용: 핸들러 함수 내부에서 데이터베이스 클라이언트를 생성하지 마세요. 한 번만(static/global) 생성하고 재사용하세요.
2. 힙 튜닝: 공격적인 가비지 컬렉션을 피하기 위해 JVM 힙 크기가 컨테이너에 맞게 올바르게 설정되어 있는지 확인하세요.
3. 리플렉션 피하기: 리플렉션을 많이 사용하는 라이브러리(일부 오래된 JSON 파서 등)는 워밍업이 느립니다. 가능하면 컴파일 타임 코드 생성을 선호하세요.
4. GC 정지 시간 모니터링: 가비지 컬렉션이 워크플로에 지연 스파이크를 유발하지 않는지 도구를 사용해 모니터링하세요.
결론: 런타임이 중요하다
"서버리스는 느리다"는 시대는 끝났습니다. 런타임의 근본적인 물리 법칙을 이해하고, 그 법칙을 존중하는 아키텍처를 선택함으로써, 우리는 함수의 개발 생산성과 베어메탈 수준의 순수한 성능을 동시에 달성할 수 있습니다.
13부에서는 "제네릭 커넥터(Generic Connector)" 패턴에 대해 다뤘습니다. 14부에서는 "컴플라이언스와 감사(Compliance & Auditing)"를 살펴보고, 규제 기관을 위해 모든 실행을 어떻게 추적하는지 알아보겠습니다.