이번 프로젝트에서는 Spring Modulith를 사용하기로 결정했습니다. 그 이유와 선택 과정에 대해 공유하고자 합니다.
1. 왜 Modular Architecture를 선택하였는가?
Monolithic(Monolith)
세 가지 이유로 모놀리틱 아키텍처를 제외하게 되었습니다.
첫째, 모놀리틱 아키텍처는 프로젝트가 진행될수록 도메인 간 의존성이 높아질 것이라고 예상했습니다. 현재 촉박한 오픈 일정으로 업무를 진행하고 있습니다. 시간이 부족하면 도메인 의존성을 충분히 고려하지 못한 채 프로젝트의 진행 속도를 높이기 위해 쉬운 길을 선택할 가능성이 큽니다. 이는 ‘깨진 창문 효과’로 인해 점점 더 쉬운 결정을 하게 만들고, 결국 스파게티 코드로 이어질 것입니다.
둘째, 비즈니스 변경을 빠르게 적용하기 어려워집니다. 첫 번째 문제로 인해 도메인 간 의존성이 커지면 하나의 도메인 로직을 변경할 때 의존하고 있는 다른 도메인도 함께 변경해야 합니다. 비즈니스 변경을 쉽고 빠르게 할 수 없다는 것은 치명적인 단점이라고 생각합니다. 소프트웨어 설계의 궁극적인 목적이 고객에게 비즈니스를 빠르고 안정적으로 제공하는 것이라고 본다면, 이를 외면할 수 없습니다.
셋째, 추후 특정 도메인을 분리하여 서비스 독립성을 높이고 확장성을 확보하는 데 어려움이 있을 것으로 예상되었습니다. 도메인 간 의존성이 높은 상태에서의 분리와 낮은 상태에서의 분리는 그 부담의 차이가 큽니다. 심한 경우, 도메인의 분리 및 확장성이 필요함에도 불구하고 진행이 어려울 수도 있습니다. 마틴 파울러가 말했듯이, “소프트웨어는 필요할 때 잘 분리될 수 있어야 하고, 필요할 때 잘 통합될 수 있어야 한다.“라는 의견에 전적으로 동의합니다. 여기서 ‘필요할 때’란 ‘고객에게 더 나은 서비스를 제공해야 할 때’라고 생각하기에, 두 번째 문제만큼이나 중요한 요소입니다.
MSA(Micro Service Architecture)
MSA를 제외하는 결정은 모놀리틱을 배제하는 것보다 더 쉬웠습니다. 그 이유는 두 가지입니다.
첫째, 기술적 전문성 부족입니다. 저는 MSA 실무 경험이 없으며, 개인적인 호기심으로 진행한 Spring Cloud 토이 프로젝트가 전부였습니다. 전문성이 부족한 상태에서 MSA를 도입하면 결국 ‘분산된 똥덩어리’가 될 가능성이 높다고 판단했습니다. 또한, MSA의 러닝 커브가 높다는 점도 고려해야 했습니다. 현재 팀의 목표는 MVP 모델이 아니라, 서비스 초기부터 매출을 창출하는 것입니다. 이러한 상황에서 MSA 실무 경험이 없는 제가 운영 서비스에 도입하는 것은 무리라고 판단했습니다.
둘째, 시간을 효과적으로 사용하기 위해서입니다. 현재 팀의 백엔드 개발자는 저 혼자입니다. 모놀리틱 아키텍처를 선택하면 소스 코드와 배포를 한곳에서 집중 관리할 수 있으며, 이를 통해 시간적 이점을 확보하여 비즈니스 로직 개발에 더욱 집중할 수 있다고 생각했습니다.
Modular Monolithic(Monolith)
두 가지 이유로 Modular Monolithic 아키텍처를 선택했습니다.
첫째, 구조적으로 모듈(도메인) 간 의존성을 낮출 가능성이 높기 때문입니다. 여기서 ‘구조적’이라는 말은 각 모듈 간 의존성을 명시적으로 선언함으로써 불필요한 의존성을 쉽게 파악할 수 있으며, 의존성을 추가하는 과정에서 불편함을 주어 무분별한 의존성 증가를 방지한다는 의미입니다. 모듈러 모놀리틱 방식이 의존성 추가를 완전히 차단하는 것은 아닙니다. 다만, 의존성 추가를 어렵게 만들고 제한함으로써 개발자가 불필요한 의존성을 최소화할 수 있도록 유도합니다.
둘째, 서비스 확장을 더욱 유연하게 만들 수 있습니다. 비즈니스가 고도화됨에 따라 서비스 요청이 많은 특정 도메인(모듈)이 생길 수 있습니다. 만약 모듈 간 의존성이 잘 관리된다면, 도메인 간 결합도가 낮아지고 서비스 분리가 더욱 쉬워질 것입니다. 결과적으로, 특정 모듈의 독립성을 확보하여 필요할 때 MSA로 자연스럽게 전환할 수 있는 기반이 마련됩니다.
총평
소프트웨어 아키텍처의 궁극적인 목적은 비즈니스를 고객에게 빠르고 안정적으로 제공하는 것이라고 생각합니다. 결국, Modular Monolithic(모듈러 모놀리틱) 아키텍처를 선택한 이유도 고객을 최우선으로 고려했기 때문입니다. 도메인 간 의존성을 낮춤으로써 도메인(서비스) 변경을 쉽고 빠르게 고객에게 전달하고, 서비스 분리 및 확장을 통해 안정적인 서비스를 제공하는 것이 핵심 목표입니다.
이러한 목적을 바탕으로 볼 때, 소프트웨어는 쉽게 분리되고 다시 결합될 수 있어야 한다는 마틴 파울러의 말에 깊이 공감합니다. 저의 소프트웨어 설계에 대한 전략적 목표는 고객에게 최고의 서비스를 제공하는 것이며, 전술적 목표는 서비스 분리 및 확장이 용이한 구조를 만드는 것입니다. 이 목표를 잊지 않고, 설계와 코딩에 최선을 다하려고 합니다.
결국, 위의 목표를 최우선으로 고려하면서, 제가 현실적으로 실행 가능한 범위 내에서 최선의 선택을 했습니다. 각자의 가용한 시간과 기술 수준이 다르듯, 상황에 따라 모놀리틱이 최적의 답이 될 수도 있고, MSA가 더 적합할 수도 있습니다. 정답은 없습니다. 주어진 상황에서 최적의 설계를 찾는 것이 중요할 뿐입니다. 지금도 제가 올바른 방향으로 가고 있는지 계속 고민하고 의심하고 있습니다. 그러나 그 의심 속에서 더 나은 결정을 내릴 수 있도록 노력하며 나아가려고 합니다.
2. 왜 Spring Modulith를 선택하였는가?
Modular Monolithic 아키텍처를 구현하기 위해 Gradle을 활용한 방식과 Spring Modulith 중에서 고민했습니다. JPMS(Java Platform Module System)도 고려했지만, Spring Boot와의 호환성이 좋지 않고 Gradle과의 통합이 원활하지 않으며, 추가적인 학습이 필요했기 때문에 제외했습니다. Gradle을 이용한 멀티 모듈 방식은 기존에 경험이 있었고, Spring Modulith는 설정이 간단하여 시간적 비용을 절약할 수 있다고 판단했습니다.
Gradle 멀티 모듈과 비교했을 때, Spring Modulith를 선택한 가장 큰 이유는 Spring Event를 활용하여 상태 처리를 용이하게 할 수 있기 때문입니다. 향후 서비스가 확장되어 특정 도메인을 분리해 마이크로서비스(MSA)로 전환하게 된다면, 도메인 간 상태 관리를 이벤트 기반으로 할 예정입니다. 이 경우, 기존 소스 코드가 이미 이벤트 기반으로 작성되어 있기 때문에 서비스 분리 결정이 더 쉬워질 것입니다. 서비스 분리가 필요한 상황에서도 소스 코드 분리의 어려움으로 인해 포기하는 경우를 줄일 수 있을 것이라 기대하고 있습니다.
Spring Modulith는 Spring 팀에서 꾸준히 업데이트하고 있으며, 점점 발전해 나가고 있습니다. 다만, Gradle과 비교하면 관련 커뮤니티가 아직 작으며, 국내에서는 사용 사례가 많지 않다는 점에서 다소 두려움도 있습니다. 새로운 도전에는 두려움이 따르는 법이죠. 그럼에도 불구하고 저는 이 도전을 기꺼이 안고 나아가 보려 합니다.