Spring Microservice in Action 책을 통해 마이크로서비스란 무엇이고, 다른 아키텍처들과 어떠한 차이점이 있는지 알아보려고 한다. 먼저 중소 규모의 많은 웹 애플리케이션이 사용하고 있고, 대중적인 모놀리스 아키텍처에 대해서 알아보자.
모놀리스 아키텍처(Monolith Architecture)
모놀리스 아키텍처는 하나의 큰 단일 단위로 구축하는 소프트웨어 아키텍처 패턴이다. 하나의 소프트웨어를 구성하는 모듈과 코드를 한 프로젝트 단위로 관리한다. 애플리케이션을 실행하기 위해서 단일 프로젝트를 빌드하고 실행시키면 된다. 개발자들에게 제일 익숙한 방식일 것이다.
위 그림과 같이 단일 UI, 코드, 데이터베이스로 동작된다. 매우 일반적으로 활용되고 있는 만큼, 마이크로서비스 아키텍처보다 초기 개발과 구축 및 배포가 간단하다. 그렇지만 서비스가 커지면서 복잡해진다면 유연함은 떨어지고, 유지 보수는 더 어려워진다. 특히, 여러 개발팀이 함께 작업해야 하는 큰 조직에서는 모놀리스의 단일 코드베이스로 인한 협업과 확장의 어려움을 겪는다.
예시
- 아래는 쇼핑몰 프로젝트를 모놀리스 아키텍처로 구현하는 예시이다.
- 간단한 쇼핑몰 백엔드 서버를 java, spring으로 구축한 전체 프로젝트의 구조이다.
shopping-mall-monolith/
│
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ ├── com.example.shoppingmall/
│ │ │ │ ├── config/
│ │ │ │ │ ├── AppConfig.java
│ │ │ │ │ ├── WebConfig.java
│ │ │ │ │ └── SecurityConfig.java
│ │ │ │ ├── controller/
│ │ │ │ │ ├── HomeController.java
│ │ │ │ │ ├── ProductController.java
│ │ │ │ │ └── UserController.java
│ │ │ │ ├── service/
│ │ │ │ │ ├── ProductService.java
│ │ │ │ │ ├── UserService.java
│ │ │ │ │ └── OrderService.java
│ │ │ │ ├── model/
│ │ │ │ │ ├── Product.java
│ │ │ │ │ ├── User.java
│ │ │ │ │ └── Order.java
│ │ │ │ ├── repository/
│ │ │ │ │ ├── ProductRepository.java
│ │ │ │ │ ├── UserRepository.java
│ │ │ │ │ └── OrderRepository.java
│ │ │ │ ├── exception/
│ │ │ │ │ ├── ResourceNotFoundException.java
│ │ │ │ │ └── BadRequestException.java
│ │ │ │ └── ShoppingMallApplication.java
│ │ │ │
│ │ │ ├── resources/
│ │ │ │ ├── static/
│ │ │ │ ├── templates/
│ │ │ │ └── application.properties
│ │ │ │
│ │ │ └── webapp/
│ │ │ ├── WEB-INF/
│ │ │ │ └── views/
│ │ │ │ ├── home.jsp
│ │ │ │ ├── product.jsp
│ │ │ │ └── user.jsp
│ │ │ └── assets/
│ │ │ ├── css/
│ │ │ ├── js/
│ │ │ └── images/
│ │ │
│ │ └── resources/
│ │ └── application.properties
│ │
├── test/
│ └── java/
│ └── com.example.shoppingmall/
│ ├── controller/
│ │ ├── HomeControllerTest.java
│ │ ├── ProductControllerTest.java
│ │ └── UserControllerTest.java
│ ├── service/
│ │ ├── ProductServiceTest.java
│ │ ├── UserServiceTest.java
│ │ └── OrderServiceTest.java
│ └── repository/
│ ├── ProductRepositoryTest.java
│ ├── UserRepositoryTest.java
│ └── OrderRepositoryTest.java
│
├── target/
├── .gitignore
├── mvnw
├── mvnw.cmd
├── pom.xml
└── README.md
하나의 프로젝트 내에서 모든 모듈과 코드가 존재한다. 서버를 실행하기 위해선 ShoppingMallApplication.java만 실행하면 된다.
모놀리식 아키텍처의 장점과 단점
장점
- 쉬운 개발과 배포
- 하나의 코드베이스와 데이터베이스를 관리하기 때문에 초기 개발과 배포가 상대적으로 쉽다.
새로운 기능이 추가되거나 버그가 수정될 때에도 단일 애플리케이션만 수정 후 재배포하면 된다.
- 간편한 테스트와 디버그
- 애플리케이션의 모든 부분이 통합되어 있어서 단일 유닛 테스트 및 통합 테스트가 간편하다.
모든 코드가 단일 애플리케이션에 있어서 디버깅에 용이하다.
- 단일 코드베이스로 인한 일관성
- 하나의 코드베이스에서 작업하므로 일관성을 유지하기쉽다.
- 변경 사항이 생기면 필요한 코드가 모두 한 프로젝트에 있다.
단점
- 유연성 부족과 스케일링 어려움
- 애플리케이션이 커지면 유연성이 저하된다.
- 특히, 새로운 기능 추가나 기존 기능 수정이 어려울 수 있다.
모놀리스는 특정 부분만 확장하기 어려워 수평 확장이 제한된다. 이로 인해 스케일링에 어려움을 겪는다.
- 그러므로 수직 확장을 통한 전체 애플리케이션을 확장할 수 밖에 없다.
- 큰 규모에 따른 유지 보수가 어려움
큰 규모의 코드베이스를 관리하면서 특정 모듈 또는 기능을 수정, 관리하기 어려워진다.
- 애플리케이션 코드가 점점 많아지고 서비스가 확장되면 전체 구조나 코드 흐름을 쉽게 이해하기 어렵다.
- 기술 스택 업그레이드 어려움
- 모놀리스는 단일 애플리케이션이기 때문에 기술 스택을 업그레이드할 경우 전체 애플리케이션을 업그레이드해야 하므로 기술 스택의 업그레이드가 어렵다.
- 협업의 어려움
- 다수의 개발자 및 팀이 함께 작업하는 경우 모놀리스에서는 단일 코드베이스를 공유하므로 코드 병합 시 충돌이 발생하기 쉽다.
- 기능 변경이 다른 팀의 작업에 영향을 미칠 우려가 크다.
모놀리스 아키텍처는 언제 사용해야 할까?
- 프로젝트 초기 단계
프로젝트가 초기에 시작되고 예측 가능한 규모에서 출발하는 경우, 모놀리스 아키텍처는 초기 개발과 빠른 시장 진입을 가능하게 한다. 초기에는 간단하게 시작하고 기능을 빠르게 구현해야 할 때 모놀리스는 효과적이다.
- 작은 규모의 팀
작은 규모의 팀이 프로젝트를 담당하는 경우, 코드베이스를 단일로 유지하고 개발과 유지보수를 간소화하는 모놀리스 아키텍처가 효과적일 수 있다.
- MVP(Minimum Viable Product)
제품이나 서비스의 최소 기능 제품으로, 가장 기본적인 기능을 가지고 사용자에게 제공할 때 모놀리스 아키텍처가 MVP 상태에서 사용자로부터 피드백을 수집하는데 빠른 방법이 될 수 있다.
마이크로서비스 아키텍처(Microservice Architecture)
마이크로서비스는 작고 느슨하게 결합된 분산 서비스 아키텍처 패턴이다. 마이크로서비스는 대규모 애플리케이션을 책임이 명확하고 관리하기 쉽게 작은 서비스들로 분할한다. 서로 독립적이기(느슨하게 결합된 형태) 때문에 서비스 간의 기술에 영향을 받지 않으며 각각의 고유한 데이터베이스를 소유할 수도 있다.
마이크로서비스의 핵심은 분해(decomposing)와 분리(unbundling)이다. 애플리케이션의 기능은 완전히 상호 독립적이어야 하며, 빌드, 배포, 테스트 또한 독립적으로 수행된다. 팀의 코드, 소스 제어 레포지터리, 인프라스트럭처(애플리케이션 서버와 데이터베이스)가 모두 독립적이다.
예시
- 아래는 쇼핑몰 프로젝트를 마이크로서비스 아키텍처로 구현하는 예시이다.
- 간단한 쇼핑몰 백엔드 서버를 구축한 마이크로서비스 프로젝트의 구조이다.
- 상품, 사용자, 주문 3가지 마이크로서비스로 분할한 경우를 나타냈다.
- 상품, 사용자 서비스는 Java와 Spring을 사용했고, 주문 서비스는 Node.js와 Express 사용했다.
product-service/
│
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ ├── com.example.shoppingmall.productservice/
│ │ │ │ ├── controller/
│ │ │ │ │ └── ProductController.java
│ │ │ │ ├── service/
│ │ │ │ │ └── ProductService.java
│ │ │ │ ├── model/
│ │ │ │ │ └── Product.java
│ │ │ │ ├── repository/
│ │ │ │ │ └── ProductRepository.java
│ │ │ │ └── ProductServiceApplication.java
│ │ │ └── resources/
│ │ │ └── application.properties
│ │ └── test/
│ │ └── java/
│ │ └── com.example.shoppingmall.productservice/
│ │ ├── controller/
│ │ │ └── ProductControllerTest.java
│ │ └── service/
│ │ └── ProductServiceTest.java
│ └── resources/
│ └── application.properties
user-service/
│
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ ├── com.example.shoppingmall.userservice/
│ │ │ │ ├── controller/
│ │ │ │ │ └── UserController.java
│ │ │ │ ├── service/
│ │ │ │ │ └── UserService.java
│ │ │ │ ├── model/
│ │ │ │ │ └── User.java
│ │ │ │ ├── repository/
│ │ │ │ │ └── UserRepository.java
│ │ │ │ └── UserServiceApplication.java
│ │ │ └── resources/
│ │ │ └── application.properties
│ │ │
│ │ └── test/
│ │ └── java/
│ │ └── com.example.shoppingmall.userservice/
│ │ ├── controller/
│ │ │ └── UserControllerTest.java
│ │ └── service/
│ │ └── UserServiceTest.java
│ │
│ └── resources/
│ └── application.properties
order-service/
│
├── src/
│ ├── controllers/
│ │ └── OrderController.js
│ ├── models/
│ │ └── Order.js
│ ├── repositories/
│ │ └── OrderRepository.js
│ ├── services/
│ │ └── OrderService.js
│ ├── app.js
│ ├── config.js
│ └── routes.js
│
├── tests/
│ ├── OrderController.test.js
│ └── OrderService.test.js
│
├── config/
│ └── default.json
│
├── package.json
├── package-lock.json
├── .gitignore
└── README.md
위 세가지 도메인은 각각 다른 프로젝트에서 개발되고 배포된다. 3개의 서버 모두가 정상적으로 동작해야지만 쇼핑몰 웹사이트 백엔드 서버가 정상적으로 동작한다고 할 수 있다. 세 서버는 각각 다른 기술 스택으로 구현할 수 있으며, 그에 따라 다른 서버가 받는 영향은 지극히 적다. 각 서비스(도메인)에 맞는 최적의 기술 스택으로 구현할 수 있는 것이다.
세 서버는 필요에 따라 HTTP 통신으로 상호 간의 정보를 주고 받을 수 있다. 세 서버를 통해서 사용자는 쇼핑몰 웹사이트의 최종 기능을 제공 받는다. 마이크로서비스 아키텍처는 모놀리스보다 조금 더 복잡하지만, 전체 서비스가 복잡해질수록 도메인 간의 분리가 명확하며 각 서비스 만의 장점을 극대화할 수 있다.
마이크로서비스 아키텍처의 장점과 단점
장점
- 유연성과 확장성
- 마이크로서비스는 다른 서비스와 독립적이기 때문에 유연하고 확장성이 좋다.
- 주로 도메인으로 서비스를 나누기 때문에 도메인에 맞는 기능을 추가하고 확장하는데 용이하다.
- 특정 마이크로서비스만 확장하거나 기술 스택을 변경하기 쉽다.
- 기술 다양성과 폴리글랏 프로그래밍
- 각 서비스는 독립적으로 구현되기 때문에 서비스 간에 다양한 기술 스택을 사용할 수 있고, 필요에 따라 언어 및 프레임워크를 선택할 수 있다.
- 독립적 배포 및 업데이트
- 각 마이크로서비스는 독립적으로 배포되고 업데이트될 수 있어 시스템 전체를 중단시키지 않고도 특정 서비스를 업데이트할 수 있다.
- 스케일링 용이성
- 특정 서비스에 대한 수요가 증가하면 해당 서비스만 확장할 수 있으며, 전체 애플리케이션을 확장할 필요가 없다.
- 수직, 수평 확장에 모두 용이하다.
단점
- 초기 구축 비용과 운영 복잡성
- 적절한 서비스의 크기와 경계를 정의하고, 각 서비스의 기술 스택과 데이터베이스 선택을 적절히 고려해야 한다.
- 기존의 모놀리식에서 마이크로서비스로 전환 비용이 많이 발생할 수 있다.
다수의 서비스를 운영하면 모니터링, 로깅, 트랜잭션 관리 등 운영 측면에서의 복잡성이 증가할 수 있다.
- 분산 시스템의 어려움
- 데이터 일관성, 분산 트랜잭션 등을 관리하기 위해서는 추가적인 노력과 구현이 필요합니다.
- 서비스 간의 동기/비동기 통신을 고려해야 한다.
- 테스트와 디버깅의 어려움
- 각 서비스는 독립적으로 테스트되어야 하므로 통합 테스트가 어려울 수 있다.
- 하나의 기능이 여러 마이크로서비스를 통과해야한다면 디버깅이 어렵다.
- 데이터 관리의 어려움
- 서비스마다 자체 데이터베이스를 가질 수 있어 데이터 관리가 복잡해지며, 데이터 일관성을 유지하기 어려울 수 있다.
- 만약, 첫 번째 마이크로서비스에 대한 요청에서 문제 발생 시 모든 데이터를 롤백할 수 있도록 데이터 일관성 유지에 신경 써야 한다.
마이크로서비스 아키텍처는 언제 사용해야 할까?
많은 기업들을 보면 MSA로의 전환을 하고 있다. 기존의 모놀리스 아키텍처로 시작한 서비스가 비대해지고, 수직 확장에서 어려움을 겪을 때 전환하는 모습을 볼 수 있다.
- 대규모 및 복잡한 애플리케이션
- 비즈니스가 계속 성장함에 따라 모놀리스 아키텍처에서 수직 확장의 어려움, 잦은 마비 현상을 겪고 있다면 고려할 수 있다.
- 모놀리스에서는 유지보수와 확장이 어려워짐에 따라 마이크로서비스 아키텍처를 고려할 수 있다.
- 비즈니스 도메인의 분리, 독립적인 확장성 요구
- 서로 다른 비즈니스 도메인으로의 명확한 분리가 가능할 때 MSA를 고려할 수 있다.
- 특정 비스니스 서비스만 확장이 필요할 때 MSA를 고려할 수 있다.
- 규모가 크고 독립적인 팀들의 존재
여러 팀이 독립적으로 작업하고 배포해야 할 때, 마이크로서비스 아키텍처는 팀 간 협업을 용이하게 만들 수 있이다. 이는 각 팀이 각각의 마이크로서비스를 담당함으로써 개별적인 빌드와 배포가 가능하기 때문이다.
마이크로서비스는 작고(small), 단순하고(simple), 분리된(decoupled) 서비스이다. 이는 확장 가능하고(scalable), 회복적이며(resilient) 유연한(flexible) 애플리케이션을 뜻한다.
🏷️이미지 출처 및 참고한 사이트
Uploaded by N2T