Capa-Java: 하이브리드 클라우드 Java 구축 3년 — 왜 나는 여전히 Sidecar보다 SDK를 선호하는가
요약
하이브리드 클라우드 환경에서 Sidecar 방식 대신 SDK 기반의 Capa-Java를 3년간 운영하며 얻은 경험을 공유합니다. 멀티 클라우드의 API 파편화 문제를 해결하기 위해 운영 부하를 줄이는 SDK 접근 방식의 장점을 다룹니다.
핵심 포인트
- 멀티 클라우드 환경의 API 불일치 문제(구성, 디스커버리, 메시지 큐 등) 해결 필요
- Sidecar 방식은 운영 복잡도와 모니터링 부하를 증가시킬 수 있음
- SDK 기반 접근 방식은 애플리케이션 프로세스 내부에서 인프라 기능을 처리하여 운영 효율성 증대
- Capa-Java를 통한 하이브리드 클라우드 구축의 실전적 교훈
Capa-Java: 하이브리드 클라우드 Java 구축 3년 — 왜 나는 여전히 Sidecar보다 SDK를 선호하는가
솔직히 말씀드리겠습니다. 저는 Sidecar(사이드카) 열풍에 완전히 매료되었었습니다. Service Mesh(서비스 메시)가 어디에나 있었을 때, 저는 모든 컨퍼런스 강연을 섭렵하고 모든 CNCF 블로그 포스트를 읽으며, Sidecar가 모든 하이브리드 클라우드 문제를 해결할 유일한 진리(One True Way™)라고 진심으로 믿었습니다. 만약 3년 전에 제가 SDK 방식을 옹호하는 글을 쓰게 될 것이라고 말씀하셨다면, 저는 당신을 비웃었을 것입니다.
하지만 지금 우리는 여기에 있습니다. Capa-Java를 프로덕션 환경에서 3년 동안 운영하면서, 제 생각은 바뀌었습니다. 이것은 단순히 "Sidecar는 나쁘고, SDK는 좋다"라는 식의 불평이 아닙니다. Sidecar가 나쁘다는 것이 아닙니다. 적절한 맥락에서는 훌륭합니다. 하지만 제가 배운 것은 하이브리드 클라우드 문제를 해결하는 방법이 하나 이상 있으며, SDK 기반 접근 방식이 여전히 사람들이 더 이상 이야기하지 않는 많은 장점을 가지고 있다는 사실입니다.
이것은 실제 이야기입니다. Capa-Java를 3년 동안 매일 사용하면서 겪은, 지저분하고 솔직하며 마케팅 브로슈어에는 담기지 않는 이야기입니다. 좋은 점, 나쁜 점, 실제로 누구를 위한 것인지, 그리고 아마도 다른 대안을 찾아봐야 할 사람은 누구인지에 대해 다룹니다.
내가 여기에 도달하게 된 과정: 아무도 경고해주지 않았던 하이브리드 클라우드의 혼란
이 모든 것이 시작된 지점으로 거슬러 올라가 보겠습니다. 3년 전, 저희 팀은 꽤 표준적인 비즈니스 요구사항을 받았습니다: "우리는 (컴플라이언스를 위해) 프라이빗 데이터 센터와 (버스트 용량을 위해) AWS에서 동일한 앱을 실행해야 합니다. 코드베이스는 동일해야 하며, 재작성은 없어야 합니다."
충분히 간단해 보이죠? 하지만 그게 바로 비극의 시작이었습니다.
실제로 파고들기 시작했을 때, 우리는 예상할 수 있는 모든 문제에 부딪혔습니다. 모든 클라우드는 모든 것에 대해 서로 다른 API를 가지고 있었습니다:
- 구성 관리 (Configuration management)? 각 클라우드마다 다른 API.
- 서비스 디스커버리 (Service discovery)? 각 클라우드마다 다른 API.
- 메시지 큐 (Message queues)? 각 클라우드마다 다른 API.
- 분산 트레이싱 (Distributed tracing)? 예상하셨겠지만 — 각 클라우드마다 다른 API.
멀티 클라우드 지원을 원한다면, 선택지는 기본적으로 다음과 같았습니다:
- 직접 엄청난 양의 커스텀 어댑터 (custom adapter) 코드를 작성한다 (우리는 이전에 한 번 시도해 보았는데, 유지보수의 악몽이 되었습니다)
- Sidecar/Service Mesh (서비스 메쉬)에 올인한다 (모두가 추천하는 유행하는 "현대적인" 방식)
우리는 인기 있는 Sidecar 옵션들을 면밀히 검토했습니다. 이론적으로는 아키텍처가 타당해 보입니다. 모든 횡단 관심사 (cross-cutting concerns)를 별도의 프로세스로 분리하고, 애플리케이션은 그저 localhost와 통신하기만 하면 됩니다. 깔끔한 분리, 맞죠?
하지만 실제로 우리 팀에 적용하기 위해 계산해 보니 무언가 잘못되었다는 느낌이 들었습니다. 우리는 4명뿐입니다. 모든 애플리케이션 인스턴스마다 그 옆에서 실행되는 Sidecar 컨테이너가 필요할 것입니다. 이는 더 많은 배포, 더 많은 모니터링, 그리고 무언가 고장 났을 때 디버깅해야 할 더 많은 요소들을 의미합니다. 우리가 감당할 수 없는 운영 부하 (operational load)가 늘어나는 것이죠.
그때 Capa-Java를 우연히 발견했습니다. 이것은 정반대의 접근 방식을 취합니다. 모든 인프라 기능을 Sidecar에 넣는 대신, 앱 프로세스 내부에서 실행되는 SDK에 넣는 것입니다. 핵심 아이디어는 동일합니다. 비즈니스 로직에서 인프라 관심사를 분리하는 것 — 단지 배치 위치가 다를 뿐입니다.
처음에는 회의적이었습니다. "이건 그냥 옛날 방식이잖아"라고 생각했습니다. "모두가 Sidecar가 현대적인 방식이라는 걸 알고 있는데 말이야." 하지만 솔직히 말하면? 우리는 막다른 길에 처해 있었고, 잃을 것도 별로 없었기에 한 번 시도해 보았습니다. 3년이 지난 지금도, 저는 매일 이것을 사용하고 있습니다.
Capa-Java가 실제로 하는 일 (약속하건대, 전문 용어 없이 설명하겠습니다)
공식 문서가 다소 추상적일 수 있으니, 간단하게 설명해 보겠습니다.
핵심적으로, Capa-Java는 필요한 모든 공통 인프라 요소에 대해 하나의 일관된 API를 제공합니다:
- 설정 관리 (Configuration management)
- 서비스 디스커버리 (Service discovery)
- 서비스 간 RPC 호출
- Pub/sub 메시징
- 상태 관리 (State management)
- 그 외 무엇이든
그런 다음, 플러그인 시스템 (Java의 SPI를 아신다면 이해가 빠를 것입니다)을 사용하여 이 일관된 API를 실제로 실행 중인 클라우드에 매핑합니다. 비즈니스 코드는 한 번만 작성하세요. 다른 환경에 배포할 때는 플러그인만 교체하면 됩니다. 실제 비즈니스 로직에는 변경 사항이 전혀 없습니다.
그것이 약속입니다. 과연 실현될까요? 네, 대체로 그렇습니다. 하지만 몇 가지 주의 사항이 있으며, 이에 대해서는 나중에 다루겠습니다. 만약 효과가 없었다면 저는 여전히 이곳에 있지 않았을 것입니다.
Sidecar와의 가장 큰 차이점은 모든 것이 _애플리케이션 프로세스 내부(in your application process)_에서 실행된다는 점입니다. 설정값이 필요할 때 Capa SDK를 직접 호출합니다. localhost로의 네트워크 홉(network hop)이나 추가적인 왕복(round trip)이 필요 없습니다. 이는 성능과 시스템 운영 방식 모두를 완전히 바꿔 놓습니다.
진짜 승부처는 성능이 아니라 운영의 단순함입니다
프로세스 내 실행(in-process) 방식과 Sidecar 방식을 비교할 때 사람들은 항상 성능을 먼저 언급하므로, 그 부분부터 짚고 넘어가겠습니다. 네, 네트워크 홉을 제거하면 지연 시간(latency)이 줄어듭니다. 저희의 프로덕션 벤치마크 결과, 테스트했던 Sidecar 설정과 비교했을 때 P99 지연 시간이 20-30% 감소하는 것을 확인했습니다. 이는 결코 작은 수치가 아닙니다. 특히 핀테크나 지연 시간에 민감한 분야에 있다면 더욱 그렇습니다.
하지만 솔직히 말씀드리면, 제가 이 방식을 고수한 이유는 성능 때문이 아닙니다. 진정한 게임 체인저는 운영의 단순함(operational simplicity)이었습니다.
업계는 이 사실을 잊고 있습니다. 스택에 추가되는 모든 구성 요소는 _인지적 비용(cognitive cost)_을 발생시킨다는 점입니다. 이는 단순히 CPU나 메모리에 관한 문제가 아닙니다. 그런 자원은 저렴합니다. 문제는 정신적 여유, 즉 당신이 기억하고 수행해야 할 일들, 그리고 프로덕션 환경에서 고장 날 수 있는 요소들에 관한 것입니다.
Sidecar를 사용하면 다음과 같은 작업들을 해야 합니다:
- 모든 애플리케이션 인스턴스 옆에 Sidecar를 함께 배포해야 함
- 애플리케이션과 별도로 Sidecar를 모니터링해야 함
- Sidecar를 별도로 업데이트해야 함 (또한 이를 애플리케이션 배포와 조율해야 함)
- 애플리케이션과 Sidecar 모두에 걸쳐 발생하는 문제를 디버깅해야 함
- 애플리케이션을 확장할 때 Sidecar도 함께 확장해야 함
다시 말씀드리지만, 이것이 엄청나게 어려운 일은 아닙니다. 하지만 분명히 _할 일이 더 많아지는 것_입니다. 전담 플랫폼 엔지니어링 팀이 있는 대기업이라면 괜찮습니다. 그들은 그런 일을 처리하기 위해 급여를 받으니까요. 하지만 모든 구성원이 이미 3~4가지 역할을 동시에 수행하고 있는 소규모 팀이라면 어떨까요? 그 추가적인 운영 부하는 매일 당신의 발목을 잡는 실질적인 부담이 됩니다.
Capa-Java를 사용한다면? 추가적인 구동 요소(moving parts)가 없습니다. 인프라 기능이 그냥 애플리케이션의 일부가 됩니다. 애플리케이션을 배포할 때, 이미 Capa도 함께 배포된 것입니다. 애플리케이션을 확장(scale)할 때, Capa도 함께 확장됩니다. Capa를 업데이트해야 할 때는 빌드 파일의 버전을 올리고 평소처럼 배포하기만 하면 됩니다.
이러한 단순함은 서류상으로는 당연하게 들리겠지만, 실제로 이를 경험하게 된다면? 혁명적입니다. 저는 인프라에 대해 걱정하는 시간을 훨씬 줄이고, 사용자에게 실제로 중요한 기능을 구축하는 데 훨씬 더 많은 시간을 쓰고 있습니다. 그것이 진정한 승리입니다.
구체적으로 살펴보기: 코드 예제 시간
추상적인 아키텍처 이야기는 이쯤 해두고 — Capa-Java를 실제로 사용하는 것이 어떤 모습인지 보여드리겠습니다. 솔직히 여러분이 생각하는 것보다 훨씬 더 간단합니다.
다른 서비스를 호출하고 싶으신가요? 방법은 다음과 같습니다:
import cloud.capa.api.rpc.CapaRpcClient;
import cloud.capa.api.core.TypeRef;
import reactor.core.publisher.Mono;
...
그게 전부입니다. 환경별 조건문(conditionals)도 필요 없습니다. 서로 다른 클라우드마다 다른 클라이언트도 필요 없습니다. 그저 당신이 필요로 하는 일을 수행하는 코드만 있으면 됩니다.
설정(configuration)을 가져오는 것은 어떨까요? 마찬가지로 단순합니다:
import cloud.capa.api.config.ConfigurationClient;
import cloud.capa.api.config.Configuration;
import reactor.core.publisher.Mono;
...
여기서의 묘미는 플러그인이 모든 클라우드 특화적인 작업들을 처리한다는 점입니다. 여러분의 비즈니스 로직(business code)은 어떤 클라우드에서 실행되는지 전혀 신경 쓸 필요가 없습니다. 그것이 약속된 기능이며, 실제로 실무에서 작동하는 방식입니다.
추악한 진실: 아무도 언급하지 않는 트레이드오프 (Trade-Offs)
좋습니다, 지금까지는 긍정적인 이야기만 해왔지만, 솔직해질 필요가 있습니다 — 이 접근 방식이 모두에게 맞는 것은 아닙니다. 진정한 트레이드오프 (trade-offs)가 존재하며, 이를 무시한다면 고생하게 될 것입니다. 저는 이를 혹독한 경험을 통해 배웠으니, 여러분은 그 고통을 겪지 않도록 제가 미리 말씀드리겠습니다.
첫째, Java 우선(Java-first) 방식이므로 다중 언어(Polyglot) 아키텍처에서는 이점이 크지 않습니다. Capa는 진정으로 Java 팀을 위해 구축되었습니다. 만약 여러분의 조직이 Java, Go, Node.js, Python 등이 섞인 진정한 다중 언어(Polyglot) 환경을 갖추고 있다면, 모든 언어에 대해 각기 다른 SDK를 필요로 하게 될 것입니다. 반면 Sidecar 방식은 앱이 어떤 언어를 사용하는지는 중요하지 않습니다. Sidecar는 그냥 작동하니까요. 이는 다중 언어 환경에서 엄청난 장점이며, 이 점은 저도 부정할 수 없습니다.
둘째, 버전 업그레이드가 애플리케이션별로 발생합니다. Capa는 앱에 직접 연결되어 있기 때문에, 모든 서비스가 각자의 일정에 맞춰 업그레이드를 진행해야 합니다. 만약 모든 서비스를 동일한 Capa 버전으로 유지하고 싶다면, 실제로 모든 서비스를 업그레이드해야 합니다. Sidecar를 사용하면 애플리케이션과 독립적으로 Sidecar를 업그레이드할 수 있어, 모두를 동일한 상태로 맞추기가 훨씬 쉽습니다. 이는 트레이드오프(Trade-off)입니다. 세밀한 업그레이드 유연성(SDK)을 원하는지, 아니면 중앙 집중식 제어(Sidecar)를 원하는지의 문제입니다. 이는 여러분의 조직이 어떻게 운영되느냐에 달려 있습니다.
셋째, 생태계가 더 작습니다. 여기서 명확히 짚고 넘어갑시다. Dapr는 Microsoft의 지원을 받고 있으며, CNCF에 속해 있고, 거대한 커뮤니티를 보유하고 있으며, 수십 개의 컴포넌트를 기본적으로 지원합니다. Capa는 소수의 핵심 팀이 이끄는 커뮤니티 주도 프로젝트입니다. 미리 만들어진 플러그인의 수도 더 적습니다. 만약 매우 생소한 클라우드 서비스에 대한 지원이 필요하다면, 직접 플러그인을 작성해야 할 수도 있습니다. 이것이 현실입니다. 오픈 소스이므로 여러분이 직접 할 수는 있지만, 이미 누군가 해놓은 것을 가져다 쓰는 것과는 다릅니다.
넷째, 여전히 인프로세스(In-process) 방식이므로, 앱과 리소스를 공유한다는 의미입니다. 만약 Capa에 메모리 누수(Memory leak)가 발생한다면(제 경험상 그렇지는 않았지만, 가설적으로), 앱 전체가 함께 다운됩니다. Sidecar 방식에서는 Sidecar의 누수가 반드시 앱의 다운으로 이어지지는 않습니다. 이것 또한 또 다른 트레이드오프입니다. 격리(Isolation)냐, 아니면 추가적인 복잡성(Extra complexity)이냐의 문제입니다.
다섯째, 모든 사람에게 모든 것을 제공하려고 하지 않습니다. Capa는 서비스 간 mTLS(mutual TLS), 트래픽 분할(traffic splitting), 카나리 배포(canary deployments)와 같은 화려한 서비스 메시(Service Mesh) 기능들을 기본적으로 제공하지 않습니다. Capa는 핵심적인 하이브리드 클라우드 이식성(portability) 문제에만 집중하며, 그게 전부입니다. 만약 그 외의 모든 서비스 메시 기능이 필요하다면, 아마도 완전한 사이드카(Sidecar) 방식을 사용하는 것이 더 나을 것입니다.
장단점: 3년 후의 솔직한 평가
사람들이 이 문제를 에둘러 말하는 것을 싫어하기 때문에 명확하게 요약해 보겠습니다. 만약 Capa-Java를 시도해 볼지 결정하려 한다면, 다음 사항들을 알아야 합니다.
✅ Capa-Java가 정말 잘한다고 생각하는 점 (장점)
-
소규모 Java 팀을 위한 압도적인 운영 단순성 — 거대한 플랫폼 팀을 보유하고 있지 않다면, 이것은 게임 체인저입니다. 움직이는 구성 요소가 적고, 잘못될 요소가 적으며, 유지 관리할 것도 적습니다. 이것이 얼마나 좋은지 아무리 강조해도 지나치지 않습니다.
-
지연 시간(Latency)에 민감한 워크로드에 대한 더 나은 성능 — 추가적인 네트워크 홉(network hop)이나 추가적인 직렬화/역직렬화(serialization/deserialization)가 없습니다. 우리의 측정 결과에 따르면 P99 지연 시간이 20-30% 더 낮았습니다. 이는 특정 유스케이스(use case)에서는 중요하지만, 다른 경우에는 중요하지 않을 수 있습니다. 여러분의 상황을 파악하십시오.
-
Java를 위한 진정한 '한 번 작성하여 어디든 배포(write-once deploy-anywhere)' — Capa는 실제로 그 약속을 이행합니다. 우리는 프라이빗 데이터 센터와 AWS에 정확히 동일한 코드를 배포하며, 이는 그냥 작동합니다. 변경 사항도, 해킹(hacks)도, 예상치 못한 일도 없습니다. 클라우드 간 배포(Cross-cloud deployment) 기간이 3~5일에서 몇 분으로 단축되었습니다. 이는 과장이 아니라 실제로 우리에게 일어난 일입니다.
-
시간의 시험을 견뎌낸 검증된 아키텍처 패턴 — 이는 Java가 수십 년 동안 사용해 온 전형적인 표준 API + SPI 플러그인 패턴일 뿐입니다. 화려하지도, 실험적이지도 않으며, 그저 제대로 작동하는 견고한 엔지니어링입니다.
-
관리할 추가 인프라가 전혀 없음 — 컨트롤 플레인(control plane)을 운영할 필요도 없고, 사이드카(Sidecar) 배포를 관리할 필요도 없으며, 그 어떤 것도 필요하지 않습니다. 그저 Maven/Gradle 빌드에 의존성(dependency)을 추가하고 시작하면 됩니다.
❌ Capa-Java가 부족한 점 (단점)
-
Java 전용 (대체로) — 만약 Java를 사용하지 않는 환경이라면, 이 도구는 적합하지 않습니다. 다른 언어에 대한 실험적인 지원이 일부 있긴 하지만, 일류(first-class) 수준은 아닙니다. Go, Python, Node.js를 사용하는 환경이라면 고려하지 마세요.
-
더 작은 생태계 — 규모가 더 큰 프로젝트들에 비해 미리 만들어진 플러그인(plugin)이 적습니다. 니치(niche)한 기능이 필요하다면 아마 직접 작성해야 할 것입니다.
-
내장된 서비스 메시(Service Mesh) 기능 부재 — 카나리 배포(canary deployments), 트래픽 분할(traffic splitting), mTLS 등을 즉시 사용할 수 있는 기능을 원한다면, 이 프로젝트는 그것을 위한 것이 아닙니다. 이 프로젝트는 다른 서비스 메시(Service Mesh) 기능이 아닌 이식성(portability)에 집중합니다.
-
앱별 버전 업그레이드 — 애플리케이션과 독립적으로 중앙 집중식 인프라 업그레이드를 원한다면, 이 방식은 귀하의 워크플로(workflow)에 맞지 않을 것입니다.
-
더 작은 커뮤니티 — Dapr나 Istio만큼 대중적이지 않기 때문에, 블로그 포스트나 튜토리얼가 적으며 팀 내에 이미 이를 알고 있는 사람을 찾기도 어려울 것입니다.
실제로 누가 Capa-Java를 사용해야 하는가?
3년간의 운영 환경(production) 사용 경험을 통해, 저는 Capa가 언제 유효하고 언제 유효하지 않은지에 대해 꽤 명확한 생각을 갖게 되었습니다.
다음과 같은 경우 Capa-Java를 선택하세요:
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기