모놀리스를 마이크로서비스로 리팩토링

이 포스트는 마이크로서비스로 애플리케이션을 구축하는 것에 대한 제 시리즈의 일곱 번째이자 마지막 포스트입니다. 첫 번째 포스트에서는 마이크로서비스 아키텍처 패턴을 소개하고 마이크로서비스 사용의 장점과 단점에 대해 설명합니다. 다음 포스트에서는 API Gateway 사용, 프로세스 간 통신, 서비스 검색, 이벤트 기반 데이터 관리 마이크로서비스 배포와 같은 마이크로 서비스 아키텍처의 다양한 측면에 대해 설명합니다. 이 포스트에서는 모놀리식 애플리케이션을 마이크로서비스로 마이그레이션하기 위한 전략을 살펴봅니다.

이 포스트를 통해 마이크로서비스 아키텍처, 장점과 단점, 사용 시기에 대해 잘 이해할 수 있기를 바랍니다. 아마도 마이크로서비스 아키텍처가 조직에 적합할 것입니다.

그러나 크고 복잡한 모놀리식 애플리케이션에서 작업할 가능성이 상당히 높습니다. 애플리케이션을 개발하고 배포하는 일상적인 경험은 느리고 고통스럽습니다. 마이크로서비스는 머나먼 열반처럼 보입니다. 다행히도 단일 지옥에서 탈출하는 데 사용할 수 있는 전략이 있습니다. 이 포스트에서는 모놀리식 애플리케이션을 마이크로서비스 세트로 점진적으로 리팩토링하는 방법을 설명합니다.

목차

1. 마이크로서비스로의 리팩토링 개요
2. 전략 1 – Stop Digging
3. 전략 2 – Frontend 와 Backend 분할(Split)
4. 전략 3 – 서비스 추출(Extract)
4-1. 서비스로 변환할 모듈의 우선 순위 지정
4-2. 모듈을 추출하는 방법
5. 요약

1. 마이크로서비스로의 리팩토링 개요

모놀리식 애플리케이션을 마이크로서비스로 변환하는 프로세스는 애플리케이션 현대화의 한 형태입니다. 이것이 개발자들이 수십 년 동안 해온 일입니다. 결과적으로 애플리케이션을 마이크로서비스로 리팩토링할 때 재사용할 수 있는 몇 가지 아이디어가 있습니다.

사용하지 않는 한 가지 전략은 “빅뱅” 재작성입니다. 새로운 마이크로서비스 기반 애플리케이션을 처음부터 구축하는 데 모든 개발 노력을 집중할 때입니다. 매력적으로 들리지만 매우 위험하고 실패로 끝날 가능성이 높습니다. Martin Fowler가 말했듯이 “Big Bang rewrite가 보장하는 유일한 것은 Big Bang입니다!”

Big Bang 재작성 대신 모놀리식 애플리케이션을 점진적으로 리팩토링해야 합니다. 마이크로서비스로 구성된 새 애플리케이션을 점진적으로 빌드하고 모놀리식 애플리케이션과 함께 실행합니다. 시간이 지남에 따라 모놀리식 애플리케이션에 의해 구현되는 기능의 양은 완전히 사라지거나 또 다른 마이크로서비스가 될 때까지 축소됩니다. 이 전략은 시속 70마일로 고속도로를 운전하면서 자동차를 수리하는 것과 비슷합니다. 이는 도전적이지만 Big Bang 재작성을 시도하는 것보다 훨씬 덜 위험합니다.

Martin Fowler는 이 애플리케이션 현대화 전략을 Strangler 애플리케이션이라고 합니다. 이름은 열대 우림에서 발견되는 스트랭글러 덩굴(strangler fig)에서 따왔습니다. 교살 덩굴은 숲 캐노피 위의 햇빛에 도달하기 위해 나무 주위에 자랍니다. 때로는 나무가 죽어 나무 모양의 덩굴이 남습니다. 애플리케이션 현대화도 동일한 패턴을 따릅니다. 우리는 결국 죽게 될 레거시 애플리케이션을 중심으로 마이크로서비스로 구성된 새로운 애플리케이션을 구축할 것입니다.

이를 위한 다양한 전략을 살펴보겠습니다.

2. 전략 1 – Stop Digging

구덩이의 법칙에 따르면 구덩이에 빠졌을 때는 파기를 멈춰야 합니다. 이것은 모놀리식 애플리케이션을 관리할 수 없을 때 따라야 할 훌륭한 조언입니다. 즉, 모노리스를 더 크게 만드는 것을 중단해야 합니다. 즉, 새로운 기능을 구현할 때 모놀리스에 코드를 더 추가해서는 안 됩니다. 대신 이 전략의 큰 아이디어는 새 코드를 독립 실행형 마이크로서비스에 넣는 것입니다. 다음 다이어그램은 이 접근 방식을 적용한 후의 시스템 아키텍처를 보여줍니다.

microservices pull module from monolith

새로운 서비스와 레거시 모놀리스 외에도 두 가지 다른 구성 요소가 있습니다. 첫 번째는 들어오는(HTTP) 요청을 처리하는 요청 라우터입니다. 이전 사례에서 설명한 API Gateway와 유사합니다. 라우터는 새 기능에 해당하는 요청을 새 서비스에 보냅니다. 레거시 요청을 모놀리스로 라우팅합니다.

다른 구성 요소는 서비스를 모놀리스와 통합하는 글루 코드입니다. 서비스는 단독으로 존재하는 경우가 거의 없으며 모놀리스가 소유한 데이터에 액세스해야 하는 경우가 많습니다. 모놀리스, 서비스 또는 둘 다에 있는 글루 코드는 데이터 통합을 담당합니다. 이 서비스는 글루 코드를 사용하여 모놀리스가 소유한 데이터를 읽고 씁니다.

서비스가 모놀리스의 데이터에 액세스하는 데 사용할 수 있는 세 가지 전략이 있습니다.

  • 모놀리스에서 제공하는 원격 API 호출
  • 모놀리스의 데이터베이스에 직접 액세스
  • 모놀리스의 데이터베이스와 동기화된 자체 데이터 복사본 유지

글루 코드를 부패 방지 계층이라고도 합니다. 글루 코드는 고유한 도메인 모델이 있는 서비스가 레거시 모노리스의 도메인 모델 개념에 의해 오염되는 것을 방지하기 때문입니다. 글루 코드는 서로 다른 두 모델 간에 변환됩니다. 반부패 계층이라는 용어는 Eric Evans의 필독서인 Domain Driven Design에서 처음 등장한 후 백서에서 구체화되었습니다. 반부패 계층을 개발하는 것은 쉬운 일이 아닙니다. 그러나 모놀리식 지옥에서 벗어나려면 하나를 만드는 것이 필수적입니다.

새로운 기능을 경량 서비스로 구현하면 몇 가지 이점이 있습니다. 모놀리스가 더욱 관리하기 어려워지는 것을 방지합니다. 서비스는 모놀리스와 독립적으로 개발, 배포 및 확장할 수 있습니다. 생성하는 각각의 새로운 서비스에 대해 마이크로서비스 아키텍처의 이점을 경험하게 됩니다.

그러나 이 접근 방식은 모놀리스의 문제를 해결하지 못합니다. 이러한 문제를 해결하려면 모노리스를 분해해야 합니다. 이를 위한 전략을 살펴보겠습니다.

3. 전략 2 – Frontend 와 Backend 분할(Split)

모놀리식 애플리케이션을 축소하는 전략은 프레젠테이션 계층을 비즈니스 논리 및 데이터 액세스 계층에서 분리하는 것입니다. 일반적인 엔터프라이즈 애플리케이션은 최소한 세 가지 유형의 구성 요소로 구성됩니다.

  • 프레젠테이션 계층 – HTTP 요청을 처리하고 (REST) API 또는 HTML 기반 웹 UI를 구현하는 구성 요소입니다. 정교한 사용자 인터페이스가 있는 응용 프로그램에서 프레젠테이션 계층은 종종 상당한 코드 본문입니다.
  • 비즈니스 로직 계층 – 애플리케이션의 핵심이며 비즈니스 규칙을 구현하는 구성 요소입니다.
  • 데이터 액세스 계층 – 데이터베이스 및 메시지 브로커와 같은 인프라 구성 요소에 액세스하는 구성 요소입니다.

일반적으로 한쪽의 프레젠테이션 논리와 다른 쪽의 비즈니스 및 데이터 액세스 논리가 명확하게 분리되어 있습니다. 비즈니스 계층에는 비즈니스 논리 구성 요소를 캡슐화하는 하나 이상의 파사드로 구성된 거친 API가 있습니다. 이 API는 모놀리스를 두 개의 더 작은 애플리케이션으로 분할할 수 있는 자연스러운 이음새입니다. 하나의 응용 프로그램에는 프레젠테이션 계층이 포함되어 있습니다. 다른 응용 프로그램에는 비즈니스 및 데이터 액세스 논리가 포함되어 있습니다. 분할 후 프레젠테이션 논리 응용 프로그램은 비즈니스 논리 응용 프로그램을 원격으로 호출합니다. 다음 다이어그램은 리팩토링 전과 후의 아키텍처를 보여줍니다.

microservices refactoring

이러한 방식으로 모놀리스를 분할하면 두 가지 주요 이점이 있습니다. 이를 통해 두 애플리케이션을 서로 독립적으로 개발, 배포 및 확장할 수 있습니다. 특히, 프리젠테이션 레이어 개발자는 사용자 인터페이스에서 빠르게 반복하고 A/B 테스트를 쉽게 수행할 수 있습니다. 이 접근 방식의 또 다른 이점은 개발자가 개발하는 마이크로서비스에서 호출할 수 있는 원격 API를 노출한다는 것입니다.

그러나 이 전략은 부분적인 해결책일 뿐입니다. 애플리케이션 중 하나 또는 둘 모두가 관리할 수 없는 단일체일 가능성이 매우 높습니다. 나머지 단일체 또는 단일체를 제거하려면 세 번째 전략을 사용해야 합니다.

4. 전략 3 – 서비스 추출(Extract)

세 번째 리팩토링 전략은 모놀리스 내의 기존 모듈을 독립형 마이크로서비스로 전환하는 것입니다. 모듈을 추출하여 서비스로 전환할 때마다 모놀리스가 축소됩니다. 충분한 모듈을 변환하면 모놀리스가 더 이상 문제가 되지 않습니다. 완전히 사라지거나 다른 서비스일 정도로 작아집니다.

4-1. 서비스로 변환할 모듈의 우선 순위 지정

크고 복잡한 모놀리식 애플리케이션은 수십 또는 수백 개의 모듈로 구성되며 모두 추출 대상입니다. 먼저 변환할 모듈을 파악하는 것은 종종 어려운 일입니다. 좋은 접근 방식은 추출하기 쉬운 몇 가지 모듈로 시작하는 것입니다. 이것은 일반적으로 마이크로 서비스와 특히 추출 프로세스에 대한 경험을 제공합니다. 그런 다음 가장 큰 이점을 제공하는 모듈을 추출해야 합니다.

모듈을 서비스로 변환하는 데는 일반적으로 시간이 많이 걸립니다. 받을 혜택을 기준으로 모듈의 순위를 지정하려고 합니다. 자주 변경되는 모듈을 추출하는 것이 일반적으로 유리합니다. 모듈을 서비스로 변환한 후에는 모놀리스와 독립적으로 모듈을 개발 및 배포할 수 있으므로 개발 속도가 빨라집니다.

리소스 요구 사항이 나머지 모놀리스의 요구 사항과 상당히 다른 모듈을 추출하는 것도 유용합니다. 예를 들어, 메모리 내 데이터베이스가 있는 모듈을 서비스로 전환한 다음 많은 양의 메모리가 있는 호스트에 배포할 수 있도록 하는 것이 유용합니다. 마찬가지로 많은 CPU가 있는 호스트에 서비스를 배포할 수 있으므로 계산 비용이 많이 드는 알고리즘을 구현하는 모듈을 추출하는 것이 좋습니다. 특정 리소스 요구 사항이 있는 모듈을 서비스로 전환하면 애플리케이션을 훨씬 쉽게 확장할 수 있습니다.

추출할 모듈을 파악할 때 기존의 거친 경계(이음새라고도 함)를 찾는 것이 유용합니다. 모듈을 서비스로 전환하는 것이 더 쉽고 저렴합니다. 이러한 경계의 예로는 비동기 메시지를 통해 나머지 애플리케이션과만 통신하는 모듈이 있습니다. 해당 모듈을 마이크로서비스로 전환하는 것은 비교적 저렴하고 쉬울 수 있습니다.

4-2. 모듈을 추출하는 방법

모듈을 추출하는 첫 번째 단계는 모듈과 모놀리스 간의 거친 인터페이스를 정의하는 것입니다. 모노리스는 서비스가 소유한 데이터가 필요하고 그 반대의 경우도 마찬가지이므로 대부분 양방향 API일 가능성이 높습니다. 모듈과 나머지 애플리케이션 간의 얽힌 종속성과 세분화된 상호 작용 패턴으로 인해 이러한 API를 구현하는 것은 종종 어려운 일입니다. 도메인 모델 패턴을 사용하여 구현된 비즈니스 로직은 도메인 모델 클래스 간의 수많은 연관 때문에 특히 리팩토링하기 어렵습니다. 이러한 종속성을 깨기 위해 코드를 크게 변경해야 하는 경우가 많습니다. 다음 다이어그램은 리팩토링을 보여줍니다.

거친 인터페이스를 구현하면 모듈을 독립형 서비스로 전환합니다. 그렇게 하려면 IPC(프로세스 간 통신) 메커니즘을 사용하는 API를 통해 모놀리스와 서비스가 통신할 수 있도록 코드를 작성해야 합니다. 다음 다이어그램은 리팩토링 전, 중, 후 아키텍처를 보여줍니다.

microservices extract

이 예에서 모듈 Z는 추출할 후보 모듈입니다. 해당 구성 요소는 모듈 X에서 사용하고 모듈 Y를 사용합니다. 첫 번째 리팩토링 단계는 거친 API 쌍을 정의하는 것입니다. 첫 번째 인터페이스는 모듈 X가 모듈 Z를 호출하는 데 사용하는 인바운드 인터페이스입니다. 두 번째 인터페이스는 모듈 Z가 모듈 Y를 호출하는 데 사용하는 아웃바운드 인터페이스입니다.

두 번째 리팩토링 단계는 모듈을 독립형 서비스로 전환합니다. 인바운드 및 아웃바운드 인터페이스는 IPC 메커니즘을 사용하는 코드로 구현됩니다. 모듈 Z를 서비스 검색과 같은 교차 문제를 처리하는 마이크로서비스 섀시 프레임워크와 결합하여 서비스를 구축해야 할 가능성이 큽니다.

모듈을 추출하면 모놀리스 및 기타 서비스와 독립적으로 개발, 배포 및 확장할 수 있는 또 다른 서비스가 있습니다. 서비스를 처음부터 다시 작성할 수도 있습니다. 이 경우 서비스를 모놀리스와 통합하는 API 코드는 두 도메인 모델 간에 변환되는 부패 방지 계층이 됩니다. 서비스를 추출할 때마다 마이크로서비스 방향으로 또 다른 단계를 밟게 됩니다. 시간이 지남에 따라 모놀리스는 줄어들고 마이크로서비스의 수는 증가할 것입니다.

5. 요약

기존 애플리케이션을 마이크로서비스로 마이그레이션하는 프로세스는 애플리케이션 현대화의 한 형태입니다. 애플리케이션을 처음부터 다시 작성하여 마이크로서비스로 이동해서는 안 됩니다. 대신 애플리케이션을 마이크로서비스 세트로 점진적으로 리팩토링해야 합니다. 사용할 수 있는 세 가지 전략이 있습니다. 새로운 기능을 마이크로서비스로 구현합니다. 비즈니스 및 데이터 액세스 구성 요소에서 프레젠테이션 구성 요소를 분할합니다. 모노리스의 기존 모듈을 서비스로 변환합니다. 시간이 지남에 따라 마이크로서비스의 수가 증가하고 개발 팀의 민첩성과 속도가 증가할 것입니다.