MSA의 환상과 현실 사이: 볼트업이 선택한 '목적 지향' 아키텍처와 진화 전략
지난 Jay가 올려주신 아키텍처 1편 글에서 볼트업 시스템이 MSA(Microservices Architecture)의 일부 개념을 어떻게 기술 실무적인 수준에서 도입했는지 소개해 드린 바 있습니다. 오늘은 그 기반에 깔린 원칙과 철학 그리고 그 이후의 고민과 진화 과정에 대해 이야기해 보려 합니다.
전기차 충전 시장은 빠르게 성장하고 있고, 볼트업은 하드웨어 기획부터 품질, 소프트웨어, 운영 시스템까지 모든 과정을 인하우스(In-house)로 소화하며 기민하게 움직이고 있습니다. 이 회사가 초기에 설립되고 사업에 뛰어들 때 우리는 "MSA가 과연 모든 문제의 해결책인가?"라는 본질적인 질문에 마주했습니다.
1. MSA 도입, 그 이면의 '비효율'

MSA는 훌륭한 아키텍처지만, 맹목적인 도입은 초기 기업이나 신사업 조직에 독이 될 수 있습니다. 저희 역시 다음과 같은 문제들을 경계해야 했습니다.
- 인프라 자원의 낭비: 지나치게 잘게 쪼개진 Pod 개수로 인한 리소스 누수.
- 높은 인지 부하: 개발자가 여러 서비스의 컨텍스트를 넘나들며 발생하는 스위칭 비용.
- 목적 의식의 소실: 과도한 모듈화로 인해 내가 만드는 기능이 전체 비즈니스에서 어떤 역할을 하는지 흐릿해지는 현상.
- 인터페이스 지옥: 서비스 간 통신 정의에 드는 소통 비용이 개발 시간보다 길어지는 상황.
우리는 이러한 비효율을 해결하고, '속도'와 '안정성'이라는 두 마리 토끼를 잡기 위해 조직과 아키텍처를 고려했습니다.
2. 조직의 변화: 직무가 아닌 '목적'을 향해

아키텍처를 바꾸기 위해선 조직 구조가 먼저 바뀌어야 합니다. (콘웨이의 법칙)
- 목적 중심 조직(Cross-Functional Team): 개발 조직을 백엔드/프론트엔드 직무 단위가 아닌, 비즈니스 맥락(Context) 단위로 재편했습니다.
- AI를 통한 엔지니어링 통합: AI 기술을 적극 활용하여 백엔드/프론트엔드 경계를 허물고, 단일 소프트웨어 엔지니어가 하나의 도메인 기능을 온전히 구현할 수 있도록 통합했습니다.
- SRE의 전문화: 대신 장애 대응이나 성능 최적화 같은 인프라/신뢰성 엔지니어링(SRE)은 별도 조직에서 집중하여, 목적 조직이 비즈니스 로직에 집중할 수 있게 했습니다.
3. 아키텍처의 진화: 모듈러 모놀리스(Modular Monolith)와 도메인 기반 아키텍쳐

우리는 무조건적인 마이크로서비스 분리 대신, '진화 가능한 도메인 기반 아키텍처'를 택했습니다.
논리적 분리, 물리적 통합 초기에는 여러 도메인을 하나의 서버(Deployment Unit)로 배포하되, 내부적으로는 철저하게 멀티 모듈 구조를 유지합니다.
- 도메인 고립: 각 모듈은 도메인 관점에서 서로 간섭하지 않도록 설계합니다.
- 통합 계층: 각 도메인 모듈은 인터페이스를 통해서만 메인 서버 애플리케이션과 소통하며, 메인 서버는 이를 조립하는 역할만 수행합니다.
이 구조의 핵심은 '유연성'입니다. 차후 트래픽이 폭주하거나 조직이 분화될 때, 해당 모듈만 떼어내어(Detach) 독립적인 마이크로서비스로 전환하기가 매우 쉽습니다. 반대로 필요하다면 다시 통합하기도 용이합니다.
4. 인터페이스 지옥 탈출: 이벤트 기반 설계 (Event-Driven)

서비스가 분리되거나 모듈 간 통신이 필요할 때, 가장 큰 골칫덩이는 '동기 호출(HTTP)'로 인한 강한 결합입니다. 이를 해결하기 위해 우리는 CQRS(명령과 조회의 책임 분리) 패턴과 이벤트 기반 인터페이스를 적극 도입했습니다.
핵심 전략
- Sync & Async 듀얼 구현: 데이터 업데이트 로직은 HTTP(동기) 요청뿐만 아니라, 이벤트(비동기)로도 처리할 수 있도록 구현체를 각각 설계합니다.
- 장애 격리: 예를 들어, '충전 종료' 프로세스에서 '포인트 적립' 서버가 죽더라도 충전 종료는 정상적으로 처리되어야 합니다. 메시지 브로커(GCP Pub/Sub)를 통해 이벤트를 발행하면, 후속 작업이 실패해도 메인 로직에는 영향을 주지 않습니다.
- 신뢰성 보장: 메시지 브로커를 통한 재처리 보장 및 백업 DB를 활용해 메시지 유실을 방지합니다.
📣 구독(Subscription) 모델과 발견(Discovery)
도메인 별 중요 이벤트(예: 포인트 지급, 충전 종료 등)는 브로커에 발행되고, 이를 필요로 하는 타 모듈이 구독하여 처리합니다. 무엇보다 중요한 것은 이러한 인터페이스와 이벤트의 '가시성'입니다. 우리는 Swagger와 차후 MCP(Model Context Protocol) 등을 통해 API뿐만 아니라 구독 가능한 이벤트 리스트까지 색인화할 수 있게 하여, 개발자가 쉽게 찾고 연동할 수 있도록 접근성을 높일 수 있도록 고려했습니다.
마치며 볼트업은 "MSA냐 Monolithic이냐"라는 이분법적인 논쟁에서 벗어나, "현재 우리 비즈니스에 가장 효율적인 구조는 무엇인가?"에 집중했습니다.
우리는 인프라 비용과 소통 비용을 줄이면서도, 언제든 확장 가능한 유연한 구조를 만들었습니다. 하드웨어부터 소프트웨어까지, 전기차 충전의 A to Z를 다루는 볼트업의 복잡한 문제를 함께 풀어나갈 동료를 기다립니다.
----
부록: 시스템 설계 시 고려했던 원칙과 시나리오 원문
- 개발 조직 단위를 직무 단위보다는 맥락/목적 단위로 재편성
- 큰 카테고리인 맥락 내에 도메인을 모듈화하여 마이크로 서비스로 조직화
- AI를 활용하여 백엔드/프론트엔드 개발자를 단일 소프트웨어 엔지니어로 통합
- 본인 조직 맥락에 해당하는 여러 마이크로 서비스를 직접 구현
- sre 관점에서의 장애 대응, 성능 향상의 경우 별도 기능 조직에서 집중 수행
- 초기엔 여러 도메인으로 나눠지더라도 하나의 서버로 구현하나 마이크로 서비스로의 확장을 위해 멀티 모듈 구조로 재편성
- 각 멀티 모듈을 도메인 관점에서 상호 간섭이 없도록 한다
- 각 도메인 모듈은 각 모듈의 인터페이스를 통해 메인 서버 어플리케이션 모듈로 통합된다.
- 서버 어플리케이션 모듈 내에 멀티 모듈로 각 도메인 모듈의 서비스에 맞게 조작하거나 조합하는 계층을 둔다.
- 조직의 분화나 도메인 간의 트래픽/의존도 불균형이 발생하는 시점에 따라 마이크로서비스로 쉽게 분리할 수 있게 한다.
- 조직이 다시 합쳐지거나 트래픽 조정이 필요할 때는 2개 이상의 마이크로서비스를 다시 하나의 마이크로서비스로 통합할 수 있다.
- 인터페이스의 경우에는 조회 성 api외의 업데이트 또는 특히 대기타임이 긴 외부 호출의 경우엔 이벤트 기반 인터페이스로 처리
- CQRS 기반 설계를 적용한다.
- 업데이트가 발생하는 로직들은 인터페이스 설계 시에 이벤트 기반으로도 받을 수 있도록 이벤트(비동기식), HTTP(동기식) 호출 구현체를 각각 구현하도록 설계한다.
- 이벤트 기반 호출 시 장애 전파가 되지 않도록 할 수 있으며 메시지 브로커를 통해 재처리를 통합 수행하여 실행을 보장한다.
- 메시지 유실에 대해서도 백업 디비 등을 통해 고려한다.
- 해당 인터페이스를 스웨거의 형태로 배포, MCP를 통해 색인해서 접근성을 높인다.
- 이벤트를 호출하는 형태 뿐만 아니라 이벤트를 발급한 것을 구독하는 형태 정의
- 각 도메인 별 중요 이벤트를 메시지 브로커에 발행하도록 한다.
- 해당 이벤트에 의존성이 있는 서비스나 모듈이 추가될 때 마다 해당 이벤트를 구독해서 후처리를 한다. (포인트 지급, 충전 종료, 등등)
- 구독가능한 이벤트 또한 MCP를 통해 접근성을 높인다.
