1.5.1.1 확장성(Scalability)과 유지보수성(Maintainability)의 상충 관계 조율
시스템 고도화 과정에서 최고기술책임자(CTO)가 흔히 직면하는 가장 치명적인 딜레마는 확장성(Scalability)과 유지보수성(Maintainability) 간의 상충 관계(Trade-off)이다. 시스템 확장을 대비해 구조를 복잡하게 쪼갤수록 코드를 이해하고 관리하는 난이도가 급증하며, 반대로 유지보수성을 위해 코드를 한데 묶어두면 트래픽이 몰렸을 때 시스템이 붕괴될 위험에 놓이게 된다.
1. 극단적 확장성 추구가 초래하는 생산성 저하의 역설
초기 스타트업이나 신규 프로젝트에 참여하는 기술 담당자(Tech Lead)들은 종종 “유니콘 기업 급의 대규모 트래픽“을 가정한 오버엔지니어링(Over-engineering)의 함정에 빠지곤 한다.
완벽한 무중단 수평적 확장(Scale-out)을 위해 수십 개의 마이크로서비스(Microservices)를 설계하게 되면, 단순한 기능 추가 하나를 위해 4~5개의 개별 저장소(Repository) 코드를 수정해야 하는 사태가 벌어진다. 분산 트랜잭션(Distributed Transaction) 관리, 네트워크 지연(Network Latency), 디버깅 지옥이라는 새로운 복잡성이 더해지면서 개발자들의 유지보수성은 현저히 하락한다. 당장 시장 검증(Product Market Fit, PMF)이 시급한 상황에서, 존재하지도 않는 백만 명의 사용자를 대비하느라 오늘 구현해야 할 스펙을 3주 뒤로 미루는 것은 심각한 기술 경영의 실패이다.
2. 수평적 확장(Scale-out)과 수직적 확장(Scale-up)의 균형
일반적으로 확장성은 데이터베이스와 애플리케이션 서버 두 가지 레벨에서 논의된다.
2.1 애플리케이션 레벨의 확장
웹/앱 서버(WAS)의 경우는 비교적 쉽게 상태 없음(Stateless) 구조를 채택할 수 있다. 세션이나 토큰을 외부 스토리지(예: Redis)에 분리함으로써, 로드 밸런서(Load Balancer) 뒤쪽으로 서버 인스턴스(Instance)를 가로로 무한정 늘리는 Scale-out은 유지보수성에 큰 타격을 주지 않는 훌륭한 전략이다.
2.2 데이터베이스 레벨의 딜레마
문제는 상태를 영구 저장해야 하는 데이터베이스 시스템이다. 이 단계에서 확장성과 유지보수성의 충돌이 가장 격렬하게 일어난다. 단일 데이터베이스 서버의 등급을 높이는 Scale-up(메모리와 CPU 클럭 향상)은 가장 단순하고 유지보수성이 좋은 방식이지만 비용의 한계선이 명확하다.
반면 데이터베이스의 읽기/쓰기 용량을 가로로 늘리기 위한 샤딩(Sharding), 레플리케이션(Replication), 그리고 CAP 정리(CAP Theorem)에 따른 분산 데이터베이스 환경은 엄청난 유지보수 오버헤드를 동반한다.
3. 합리적 조율 전략: 모듈형 모놀리스 (Modular Monolith)
단일 서버가 감당할 수 없을 정도의 명백한 트래픽 지표가 확인되기 전까지 CTO는 유지보수성에 가중치를 두어야 한다. 이러한 맥락에서 최근 학계와 실무 전문가들 사이에서 대안으로 떠오르는 아키텍처 패턴이 바로 “모듈형 모놀리스(Modular Monolith)“이다.
graph TD
subgraph 기존 모놀리스 스파게티 코드
A1[주문/결제 로직 혼재]
A2[재고/사용자 로직 혼재]
A3[전역 변수 기반 공유]
end
subgraph 모듈형 모놀리스 (Modular Monolith)
B1(주문 모듈) --- B2(결제 모듈)
B1 --- B3(재고 모듈)
B1 --- B4(사용자 모듈)
note[각 모듈은 독자적인 디렉토리와<br>인터페이스를 유지하며,<br>배포판만 하나로 묶음]
end
subgraph 마이크로서비스 (Microservices)
C1[주문 서비스] -->|네트워크 HTTP| C2[결제 서비스]
C1 -->|gRPC| C3[재고 서비스]
end
기존 모놀리스 스파게티 코드 ==리팩토링==> 모듈형 모놀리스 (Modular Monolith)
모듈형 모놀리스 (Modular Monolith) -.필요 시 일부 분리.-> 마이크로서비스 (Microservices)
모듈형 모놀리스는 로직이 단일 프로세스(Single Process) 내에서 실행되기 때문에 네트워크 지연이나 분산 트랜잭션의 유지보수 어려움이 없다. 그러면서도 내부적인 코드는 도메인 주도 설계(Domain-Driven Design, DDD)를 통해 철저하게 분리 및 캡슐화되어 있다. 이 방식을 택하면 당장의 릴리즈 속도(유지보수성)을 극대화하면서, 훗날 특정 트래픽이 폭증할 때 언제든 그 모듈만 떼어내 마이크로서비스로 전환(확장성 충족)할 수 있는 징검다리 역할을 수행하게 된다.
4. 결론
“완벽하게 확장 가능한 시스템“이라는 기술적 몽상에 빠져 유지보수성을 방치해선 안 된다. CTO는 시장 트래픽과 기업의 자금 상황에 발맞춰 시스템의 복잡도를 점진적으로 올리는 중재자 역할을 맡아야 한다. 트래픽의 스파이크가 오기 직전까지는 구조적 유연성(Flexibility)과 코드 생명주기 관리의 편의성을 극대화하는 방향으로 두 가치의 상충 관계를 조율해야 한다.
참고 문헌 및 추천 논문:
- Newman, S. (2015). Building Microservices: Designing Fine-Grained Systems. O’Reilly Media.
- Dehghani, Z. (2018). “Monolith First”. MartinFowler.com.
- Richards, M., & Ford, N. (2020). Fundamentals of Software Architecture: An Engineering Approach. O’Reilly Media.