이전에 다루던 도메인은 VMware·Citrix·Nutanix 같은 온프레미스 멀티벤더 오케스트레이션이었습니다. 벤더마다 API가 다르고, 스펙 표현 방식이 다르고, 인증 방식도 제각각인 환경에서 "하나의 통일된 인터페이스로 여러 벤더를 다룬다"는 문제를 계속 마주쳤습니다.
이 경험을 클라우드 영업 도메인으로 옮겨보면 어떨까 생각했습니다. 영업/기획 담당자가 고객에게 클라우드 견적을 낼 때 실제로 하는 일은:
이 반복을 없애는 게 목표였습니다. 워크로드 스펙(vCPU·RAM·스토리지·리전·OS)을 한 번 입력하면, AWS·Azure·GCP의 실제 공개 가격 API를 조회해서 벤더별 월 비용을 자동으로 비교해주는 서비스를 만들기로 했습니다.
중요한 제약을 하나 걸었습니다: 하드코딩된 가격표를 쓰지 않는다. 클라우드 가격은 계속 바뀌는데, 정적 테이블을 유지보수하는 건 결국 "며칠 지난 틀린 견적"을 내는 것과 같습니다. 그래서 반드시 벤더가 공개한 실제 가격 API를 연동하는 것을 전제로 잡았습니다.
이후 GCP 연동, PDF 견적서 내보내기, 조건별 필터, Docker 배포 구성까지 단계적으로 확장했습니다.
| 구분 | 기술 |
|---|---|
| Backend | Java 17, Spring Boot 3.3, Spring Data JPA, Spring Scheduler, Spring Validation |
| Frontend | React 18, TypeScript, Vite, Axios, Recharts |
| DB | H2(로컬 개발) / MySQL 8(Docker·운영) |
| 외부 연동 | Azure Retail Prices API, AWS Price List Query API(SDK v2), GCP Cloud Billing Catalog API |
| 그 외 | OpenPDF(견적서), springdoc(Swagger), JUnit 5·PDFBox·Playwright(테스트) |
Backend는 익숙한 스택이라 큰 고민이 없었지만, Frontend는 SPA + REST 조합이 이 프로젝트의 핵심(비교 테이블·차트·필터 상호작용)을 표현하기에 적합하다고 판단해 React + TypeScript로 정했습니다.
[React] ──REST──▶ [Spring Boot] ──▶ [H2 / MySQL]
▲ Vendor / PriceSnapshot
│ QuoteRequest / QuoteItem
PriceSyncScheduler (매일 1회)
│
├─(HTTP)──────────▶ Azure Retail Prices API (인증 없음)
├─(AWS SDK/SigV4)─▶ AWS Price List Query API (IAM 자격증명)
└─(HTTP + API Key)▶ GCP Cloud Billing Catalog API (기본 비활성)
여기서 가장 먼저 정한 설계 원칙이 하나 있습니다.
견적 요청이 들어올 때마다 벤더 API를 직접 호출하지 않는다.
대신 스케줄러가 주기적으로 가격을 수집해서 PriceSnapshot 테이블에 캐싱하고, 견적 API는 이 캐시만 조회합니다. 이렇게 나눈 이유는 세 가지입니다.
이후 편에서는 이 아키텍처를 구현하면서 실제로 부딪힌 문제들을 다룹니다.
null vs undefined 버그기능을 나열하기보다 왜 그렇게 만들었는지, 그 과정에서 무엇이 틀렸고 어떻게 고쳤는지를 위주로 쓸 예정입니다.