Monolithic Architecture
![](https://blog.kakaocdn.net/dn/b4kuM7/btsaERSy858/N1jrNiKh6SBvQv4prrJf5K/img.png)
왼쪽처럼 하나의 DB와 하나의 프로젝트로 모든 서비스가 구성되어 있는 구조가 Monolithic 구조이다.
Monolithic Architecture 장점
- 배포 과정이 간단하다.(파이프라인 구성이 쉽다)
- 하나의 프로젝트만 관리하니 배포하는 과정이 비교적 간단하고 쉽다.
- 통합 테스트를 하기 용이하다.
- 하나의 프로젝트에 전체 서비스의 로직이 같이 관리되기 때문에 통합 테스트에 유리하다.
- 프로젝트의 규모가 작을수록 유지보수가 간편하다.
- 일정 규모 이하의 프로젝트에서는 유지보수가 간편하다.
- 하지만 규모가 매우 크다면 그만큼 복잡해지기 때문에 유지보수가 용이하지 않다.
Monolithic Architecture 단점
- 전체적인 구조 파악이 어렵다.
- 전체 서비스가 프로젝트 하나에 있어 규모가 커질수록 전체적인 구조를 파악하기 힘들다.
- 빌드, 테스트, 배포 시간이 많이 소요된다.
- 서비스 규모가 커질수록 빌드, 테스트, 배포에 많은 시간이 소요된다.
- 조금만 수정해도 전체적으로 빌드, 테스트, 배포해야 하기 때문에 비효율적이다.
- 특정 부분의 scale-out 어려움
- 한 서버에 모든 서비스가 몰려 있기 때문에 트래픽이 집중되는 서비스에만 Scale-out하여 성능과 비용 효율성 모두 확보하는 전략을 사용하지 못한다.
- 개발 기술에 종속적이다.
- 전체 서비스가 하나의 언어, 프레임워크로 구성되어 있다.
- 높은 결합도
- 내부적으로 결합도가 높아서 특정 기능에 문제가 생기면 다른 기능에도 영향을 끼칠 가능성이 높다.
MSA (Micro Service Architecture)
단일 프로그램을 각 컴포넌트 별로 나누어 작은 서비스의 조합으로 구축하는 것이다. 마이크로서비스는 완전히 독립적으로 배포가 가능하고 서로 다른 기술 스택이 사용 가능한 단일 사업 영역에 초점을 둔다.
MSA 장점
- 서비스별로 개별 배포가 가능하다.
- 각각 독립된 서비스이기 때문에 개별 배포가 가능하다.
- 배포 시 전체 서비스를 중단하지 않고 배포가 가능하다.
- 특정 서비스에 대한 확장성에 유리하다.
- 특정 서비스만 확장이 용이하다.
- 일부 서비스의 장애가 전체 장애로 확장될 가능성이 낮다.
- 마찬가지로 독립되어 있기 때문이다.
- 각각의 서비스에 최적인 기술을 사용할 수 있다.(언어나 프레임워크 등)
- 각각의 서비스는 독립적이기 때문에 가능하다.
- 독립된 서비스이기 때문에 전체적인 구조 파악이 기존 Monolithic Architecture보다 쉽고 빠르게 이해가 가능하다.
- 특정 문제사항을 고립시켜 살펴볼 수 있기 때문에 내가 맡고 있는 부분만 보면 된다.
MSA 단점
- 독립된 서비스 간 호출 시 API를 사용하므로 통신에 대한 비용, 지연시간을 고려해야 한다.
- 통합테스트가 어렵다. 개발 환경과 실제 운영환경을 동일하게 가져가는 것이 쉽지 않다.
- DB가 분산되어 있어서 데이터 관리 및 트랜잭션 관리가 어렵다.
- 서버가 분산되어 있어서 로깅, 모니터링이 어렵다.
MSA를 적용하거나 전환하는 것이 항상 옳은가?
여기선 왜? 사용해야 하는가에 대한 답이 중요하다고 생각한다. 어떤 것이 좋다기보다는 왜 사용해야 하는지, 어떤 이점이 있는지에 초점을 맞춰서 사용하는 것이 좋을 것 같다.
나 같은 경우 기존의 Monolithic Architecture로 팀 프로젝트를 진행하며 프로젝트 규모가 커질수록 유지 보수가 힘들고 팀원들의 코드를 합칠 때 전체적인 구조를 파악하고 이해하는데 시간이 꽤 걸려서 불편했다고 느꼈다. 또한 내가 CI/CD를 담당했기 때문에 규모가 커질수록 배포 시간이 점점 오래 걸리는 문제점을 알고 있었다.
그래서 MSA를 사용해야 하나?라는 생각에 위의 장단점을 정리해 보았다. 하지만 팀 프로젝트를 진행하며 내가 하나의 서비스만 맡아서 개발하는 경우보다는 팀원들이 문제가 생겼을 때나 도움이 필요할 때 도와주는 경우가 많았다. 또한 각 서비스끼리 정보를 주고받는 경우도 있었다. 그렇기 때문에 MSA로 개발한다면 IDE를 여러 개 띄워서 개발해야 할 것이고 많이 불편할 것 같았다. 또한 DB도 각자 사용하기 때문에 각 서비스마다 일치하게 관리를 한다면 그 비용도 꽤나 든다고 생각했다. 그래서 이렇게 기능 별로 잘 분리되었지만 팀원끼리 이해하기도 쉽고 유지보수도 편한 것이 있을 것 같아 찾아본 것이 Multi Module이다.
Multi Module
처음 멀티 모듈을 알았을 때 새로운 방식의 아키텍처인줄 알았지만 Monolithic 아키텍처에 포함되는 것이었다.
Monolithic 아키텍처는 크게 단일 모듈 멀티 프로젝트와 멀티 모듈 단일 프로젝트로 나뉜다.
멀티 모듈은 서비스들을 모듈화 시켜서 각 기능들을 독립시켜서 운영하는 것이다. 이렇게만 보면 MSA와 크게 다를 것이 없어 보였다.
단일 모듈 멀티 프로젝트
각각의 프로젝트 단위를 가진 것으로 IDE를 사용한다면 각 모듈 단위별로 IDE를 띄워서 개발을 진행하는 형태이다.
예시로 member-api와 member-batch라는 기능의 프로젝트가 있다면 이 둘 모두 Member라는 클래스가 공유되어 사용될 것이다. 만약 member-api에서 Member클래스가 수정된다면 member-batch에도 수정한 내용을 복붙 해야 할 것이다. 그리고 여기서 코드를 옮길 때 실수를 한다면 서비스에 문제가 생길 것이다. 즉 코드를 작성하는 사람에게 의존하는 형태이다.
이를 해결하기 위해 사설 저장소를 사용하여 공유하고 있는 클래스들을 저장한다면 코드의 중복 및 일관성 문제를 해결할 수 있다.
단일 모듈 멀티 프로젝트 + 사설 공유 저장소
찾아보니 Nexus라는 사설 저장소를 사용하여 Member 같은 도메인 클래스나 DTO를 분리하여 프로젝트화 시켜서 업로드하는 방식이다. 이렇게 하니 코드의 일관성은 보장되지만 매우 번거롭다. 여전히 IDE도 여러 개 켜야 하고 member-api에서 공통 클래스를 수정하면 Nexus에 배포하고 배포한 내용을 member-batch에서 다운로드하는 그런 과정이 생긴다. 기능 하나를 개발해도 적용하는 시간이 많이 들고 개발에 집중하기가 어려운 문제가 있다.
그래서 최종적으로 결정한 것이 멀티 모듈 단일 프로젝트이다.
멀티 모듈 단일 프로젝트
우선 프로젝트는 하나이고 그 프로젝트 안에서 여러 개의 모듈을 사용하는 것이다. 이렇게 사용하면 IDE도 하나만 사용할 수 있고 프로젝트 하나에 각 기능을 가진 서비스들이 모듈화 되어 있으므로 일관성도 보장되며 개발도 빨리 할 수 있을 것이다. 나 같은 경우 아래와 같은 구조로 만들었다. 크게 공통으로 사용할 모듈과 비즈니스 로직을 담당하는 모듈이 있다.
![](https://blog.kakaocdn.net/dn/JEOb3/btsayU9Vz6W/MHIceBnksDh6f5dmhOJe4K/img.png)
여기까지 만드는 과정에서 많은 고민을 하고 모듈화를 시켰다. 왜냐하면 공통으로 코드를 제거하여 모듈화 시킨다는 단순한 접근으로는 문제가 생기기 때문이다.
코드가 중복된다고 여러 기능들을 common에 넣어버리면 common안에서 비즈니스 로직이 생기고 다른 기능들을 추가하며 어쩔 수 없이 common에 기능을 점점 추가하는 문제가 발생할 수 있다. 결국엔 다른 모듈들은 굉장히 기능이 작고 common만 거대한 프로젝트가 될 수 있다. 만약 이렇게 된다면 의존성을 최대한 줄이기 위해 모듈화를 했지만 common에서 의존성이 생기게 되고 규모가 커질수록 의존성이 강해지기 때문에 나중에 분리하고 싶어도 불가능하다. 이러면 특정 기능을 제거해도 관련 클래스들의 의존도가 높아서 제거가 불가능하다.
나의 경우 authentication 모듈에서 고민을 많이 했는데 API 모듈에 각각 인증, 인가 기능을 넣을지, 지금처럼 분리할지 고민을 많이 했다. API모듈에 같은 코드를 복붙 한다면 기능들이 많이 생겼을 때 문제가 발생할 수 있다고 생각하여 따로 모듈화를 했다. 하지만 API 모듈에서 인증, 인가 모듈의 기능을 가져와야 하는 문제가 있었고 그때 발생하는 예외가 @RestControllerAdvice로 처리되지 않아서 인증, 인가 모듈에 인증, 인가에 해당하는 비즈니스 로직만 추가할지 고민을 했다. 하지만 그렇게 했을 때 common에 비즈니스가 흐르며 점점 규모가 커지고 의존성도 높아져서 복잡해지는 문제가 생길 수 있기 때문에 해당 예외만 try/catch로 처리를 했다.
사실 내가 모듈화를 한 방식이 무조건 맞다!라는 것은 모르겠지만 확실한 건 내가 기존에 느꼈던 불편함이 해결됐다는 점이다. 기존의 Monolithic 방식은 저 모듈들이 하나에 다 들어있어서 팀 프로젝트를 진행할 때 매우 복잡했다. 팀원과 코드를 합치며 여러 클래스들이 추가되고 파일 구조가 복잡해지며 이해도 힘들었고 내 코드가 아닌 팀원들의 코드를 수정할 때도 많이 힘들었다. 하지만 지금은 기능별로 분리가 되어 문제가 발생해도 보다 쉽게 찾아서 문제 해결이 가능했고 각 모듈의 구조가 확실히 간단해졌다. 그리고 각 API 모듈들은 독립적으로 실행이 가능하고 여기서 common 모듈과의 데이터 교환하는 과정이 일어나지 않기 때문에 MSA와 같은 복잡한 과정 또한 없다. 이게 가능한 이유는 한 모듈에서 다른 모듈의 기능이 필요하다면 의존성을 추가하여 jar 파일 형태로 해당 모듈에 추가되는 형태이기 때문이다.
팀 프로젝트를 할 때는 시간에 쫓겨 개발을 진행하니 불편함도 모르고 했지만 지나고 나서 회고하니 이런 아키텍처를 적용해서 함께 개발하지 못해서 조금 아쉬웠다. 처음 적용해서 시간이 오래 걸려도 팀원들과 더 좋은 아키텍처에 대해 고민하고 여러 시도를 해보았으면 좋았을 것 같다.