마이크로서비스 Microservices (7) 모놀리스 리팩토링

🗓 ⏰ 소요시간 7 분

모놀리스에서 마이크로서비스로

모놀리식(monolithic) 애플리케이션을 마이크로서비스(microservices) 애플리케이션으로 바꾸고 싶다면 어떻게 하시겠습니까? 처음부터 마이크로서비스 기반으로 다시 작성해야 할까요?

폭파시키고 처음부터 작성해야 할 때는 진짜로 폭파했을 때 뿐이다.

Martin Fowler

아닙니다. 처음부터 다시 만드는 일은 너무나 어려운 작업이고 큰 위험이 따르는 작업이죠. 대신에 점차적으로 개선해나가야 합니다. 하나의 큰 애플리케이션을 작은 서비스로 조금씩 쪼개나가야 합니다. 열대우림을 상상해봅시다. 여기엔 Strangler vine 이라는 식물이 있는데 아래 사진처럼 생겼습니다. 열대우림은 나무가 너무 많다보니 땅에 있으면 햇빛을 많이 받을 수가 없습니다. 그래서 이 식물은 햇빛을 받기 위해 다른 나무를 타고 올라갑니다. 결국 원래 나무는 죽고 이 식물만 남게 됩니다.

https://www.nginx.com/blog/refactoring-a-monolith-into-microservices/

갑자기 왜 나무 이야기냐구요? 우리는 바로 이 식물의 전략을 따라 리팩토링 하려고 합니다. 기존 애플리케이션을 따라 새로운 마이크로서비스를 만들고 기존 애플리케이션은 자연스럽게 없어집니다.

이를 위한 세 가지 전략을 한 번 살펴보겠습니다.

1. 삽질은 그만!

우리가 구덩이에 있다면 삽질을 멈춰야 합니다.[1] 파면 팔수록 더 깊이 빠지게 되죠. 관리되지 않는 모놀리식 애플리케이션에 딱 맞는 말이죠. 즉 모놀리스 애플리케이션이 더 커지는 걸 막아야 합니다. 커지면 커질수록 더 관리하기 어려워지기 때문입니다. 그래서 여기에 새로운 코드를 추가하면 안됩니다! 대신 새로운 코드를 새로운 마이크로서비스로 만드는 것이 이 전략의 핵심입니다.

https://www.nginx.com/blog/refactoring-a-monolith-into-microservices/

새로운 기능을 새로운 서비스로 만들고 나면 요청을 라우팅해줄 라우터(router)가 필요합니다. 마치 API 게이트웨이처럼 말이죠. 그리고 모놀리스와 새로운 서비스 사이에 글루 코드(glue code)가 필요합니다. 글루 코드는 두 서비스를 연결해주는 코드를 말하는데요, 서비스를 새로 만들긴 했지만 아직 완전히 분리되기엔 데이터가 완전하지 않기 때문에 글루 코드를 이용해 모놀리스에서 데이터를 가져와야 합니다. 이런 글루 코드는 아직 오염되지 않은 서비스와 오염된 모놀리스 사이를 나눠주기 때문에 오염 방지 레이어(anti-corruption layer)라고도 합니다. 글루 코드는 다음과 같이 구현할 수 있습니다.

  • 모놀리스가 제공하는 API 사용
  • 모놀리스의 DB에 바로 접근
  • 모놀리스 DB를 복사해서 새로운 서비스에서 관리

이 전략은 좋지만 모놀리스가 아직 건재합니다. 이제 모놀리스를 쪼갤 전략을 살펴보겠습니다.

2. 레이어 분리하기

모놀리스 로직을 줄여가기 위해서 프론트엔드와 백엔드를 분리하겠습니다. 보통 엔터프라이즈 애플리케이션은 3계층 구조로 이루어져 있습니다.

  • 프레젠테이션 레이어(Presentation layer): HTTP 요청을 다루는 계층. REST API 를 제공하거나 HTML 기반의 웹 UI를 제공.
  • 비즈니스 로직 레이어(Business logic layer): 애플리케이션의 비즈니스 로직이 들어있는 핵심 계층.
  • 데이터 액세스 레이어(Data-access layer): 데이터베이스나 메시지 브로커(message brokers)에 접근하는 계층.

여기서 프레젠테이션 레이어와 나머지 비즈니스 로직 레이어 + 데이터 액세스 레이어는 쉽게 분리할 수 있습니다. 따라서 프레젠테이션 레이어로 하나의 서비스를 만들고, 나머지 비즈니스 로직 레이어와 데이터 액세스 레이어를 하나의 서비스로 해서 두 개의 서비스로 분리할 수 있습니다. 그리고 나서 프레젠테이션 레이어가 호출할 수 있는 API 를 제공합니다.

https://www.nginx.com/blog/refactoring-a-monolith-into-microservices/

프레젠테이션 레이어에는 비즈니스 로직이 없기 때문에 쉽게 분리할 수 있습니다. 그리고 자연스럽게 비즈니스 로직 쪽에서 API 를 제공할 수 있게 됩니다. 그럼 마지막 세 번째 전략으로 나머지 부분도 모두 마이크로서비스로 바꿔봅시다.

3. 서비스 뽑아내기

서비스 고르기

세 번째 전략은 기존의 모놀리스 속 모듈을 하나의 독립적인 마이크로서비스로 만드는 것입니다. 모두 서비스로 분리하고 나면 모놀리스는 완전히 사라지게 됩니다.

모놀리스 안에는 수십에서 수백 개의 모듈이 있습니다. 어떤 모듈을 서비스로 뽑아내야 할까요?

우선 뽑아내기 쉬운 모듈부터 작업을 해나가면 뽑아내는 작업에 익숙해질 수 있습니다. 그리고 변경이 자주 일어나는 모듈을 먼저 뽑아내는 것이 좋습니다. 왜냐하면 마이크로서비스로 뽑아내고 나면 독립적으로 개발하고 배포가 가능하기 때문에 시간을 아낄 수 있습니다.

자원을 많이 사용하는 모듈은 따로 뽑아내서 관리하는 것이 좋습니다. 예를 들면 인메모리 DB 를 사용하는 모듈이 있다면 메모리를 많이 잡아먹을 것이고, 복잡한 알고리즘을 수행하는 로직이 있다면 CPU 자원을 많이 사용할 겁니다. 이런 모듈은 따로 뽑아서 관리하는 것이 좋습니다.

서비스가 뭉쳐있는 부분도 뽑아내기 쉽습니다. 예를 들어 특정 모듈들이 다른 모듈들과 비동기 메시지 방식으로 통신하는 경우에는 분리가 쉽습니다.

서비스 뽑아내기

먼저 뽑아낼 모듈과 모놀리스 사이에 인터페이스를 정의합니다. 서비스와 모놀리스는 서로 데이터가 필요해서 양방향 API 인 경우가 많고, 종속성이 얽혀 있어서 인터페이스를 정의하기 어려울 수 있습니다. 특히 도메인 모델 패턴(Domain Model pattern)을 이용해 구현한 경우라면 도메인 모델 클래스를 나누기가 어렵습니다. 이런 종속성을 끊으려면 중요한 코드를 변경해야 할 때가 있습니다.

인터페이스를 정의하고 난 후에 이를 이용해서 모놀리스 모듈을 마이크로서비스로 분리합니다. 아래 그림은 인터페이스를 정의하고 분리하는 모습을 보여줍니다. 호출 관계에 따라서 API 의 방향이 정해집니다.

https://www.nginx.com/blog/refactoring-a-monolith-into-microservices/

이런 작업을 반복해서 마이크로서비스를 늘려갈수록 모놀리스는 작아지고 개발 속도는 빨라지게 됩니다.

참고