티스토리 뷰

728x90
반응형

개요

앞선 포스팅에서 마이크로서비스 분산DB 환경에서 데이터 조회 및 데이터 분할/동기화 설계 방법에 대해 살펴보았다. 자세한 내용은 아래 포스팅을 참고하기 바란다.

이번 포스팅에서는 데이터베이스 분리에 대해 알아보도록 하자. 데이터베이스 분리 방법은 어플리케이션 기준 분리와 데이터베이스 기준 분리를 고려할 수 있다. 두방식 모두 비즈니스의 설계 방향에 따라 데이터를 처리하는 방식이 달라질 수 있으므로, 그 영향도를 파악하고 점진적인 전환을 진행하는 것이 바람직하다.


물리/논리 데이터베이스

데이버베이스를 분리해 나가는 방법에 대해 살펴보기 전에 논리적 분리와 물리적 분리가 어떤 관련이 있는지 먼저 알아보도록 하자. 

먼저 살펴볼 내용은 바로 논리적 분리 구조이다. 우리가 이야기 하는 대부분의 데이터베이스 분리는 논리적 분리를 의미한다. 단일 데이터베이스는 논리적으로 분리된 둘 이상의 스키마를 완벽하게 호스팅할 수 있다.

다음으로 물리적 분리 구조이다. 아래와 같이 데이터베이스 엔진에 각 논리적 스키마를 가질 수 있다.

위와 같이 논리적/물리적 분리를 구분하는 이유는 무엇일까?

1) 리소스 독립 : 기본적으로 논리적 분리와 물리적 분리는 서로 다른 목표를 달성한다. 논리적 분리는 엔진에 대한 독립적인 변경이 어렵고 정보 은익을 허용하는 반면, 물리적 분리는 잠재적으로 시스템 견고성을 개선하고 리소스 경합을 제거하여 처리량이나 지연 시간을 개선할 수 있다.

2) 장애 독립 : 논리적으로 데이터베이스 스키마를 분리할 경우 단일 장애 지점이 발생할 수 있다. 데이터베이스 엔진이 중단되면 두 서비스 모두에 영향을 미친다. 이를 해소하기 위한 방법으로 다중 데이터베이스 모드, Warm Failover 등과 같은 단일 장애 지점을 방지하기 위한 메커니즘이 있으며, 실제로 조직에 복원력이 뛰어난 데이터베이스 클러스터를 만들기 위해 상당한 노력을 기울일 것이다.

물론 단순히 물리적으로 독립된 데이터베이스를 구성하는 것이 좋은 것은 아니다. 여러 이유가 있겠지만, 물리적으로 독립된 DB를 사용할 경우 지속적으로 증가하는 비용, 유지보수를 감당해야 한다.

그럼 본격적으로 데이터베이스 분리에 대해 알아보도록 하자. 데이터의 분리를 위한 과정은 크게 데이터베이스의 분리와 어플리케이션의 분리로 구분해 볼 수 있다.  마이크로서비스는 자체 서비스에서 실행되고 제어되는 데이터가 논리적으로 분리된 데이터베이스로 추출될 때까지 지속적으로 분리해 나가야 한다. 이는 최종 목적지가 될 수 있으며, 목적을 달성하기 위해 점진적으로 접근해 나가야 할 것이다. 분리 방법은 아래와 같이 접근할 수 있다.

  • 먼저 데이터베이스를 분리한 다음 코드를 분할합니다.
  • 먼저 코드를 분리한 다음 데이터베이스를 분할합니다.
  • 한 번에 둘을 나눕니다.

이제 이러한 옵션과 사용하는 방식에 따라 도움이 될 수 있는 몇 가지 패턴을 살펴보도록 하자.


데이터베이스 분리 우선

비즈니스 단위 별 별도의 스키마를 사용하면 단일 작업을 수행하기 위한 데이터베이스 호출 수를 잠재적으로 늘릴 수 있다. 이전에는 단일 SELECT 문에서 원하는 모든 데이터를 가질 수 있었지만 이제는 두 위치에서 데이터를 다시 가져와 메모리에 결합해야 할 수 있다. 또한 두 개의 스키마로 이동할 때 트랜잭션 무결성이 깨져 결국 애플리케이션에 상당한 영향을 미칠 수 있다.

어플리케이션은 그대로 유지한 채 데이터베이스만 분리할 경우 소비자에게 영향을 미치지 않고 변경 사항을 되돌리거나 복원할 수 있다. DB 분리가 의미가 있다고 판단되면 애플리케이션 코드를 두 개의 서비스로 분할하는 것에 대해 생각할 수 있다.

다만, 이 접근 방식이 단기간에 많은 이익을 얻을 가능성은 낮다. 데이터의 분리에 따라 모놀리스 서비스가 갖는 문제점을 진단하고 개선할 수 있다는 장점은 있지만, 그 기간이 길어질 수 있기 때문에 잠재적인 성능 또는 데이터 일관성 문제가 특히 우려되는 경우 이와 같은 분리 방식을 권고한다. 또한 모놀리스 자체가 상용 소프트웨어와 같은 블랙박스 시스템인 경우 이 옵션을 사용할 수 없다는 점도 고려해야 한다.

분리 패턴 1. Bounded Context 단위의 리포지토리

일반적인 방법은 Hibernate와 같은 프레임워크에 의해 백업되는 저장소 계층을 가지고 코드를 데이터베이스에 바인딩하여 객체나 데이터 구조를 데이터베이스와 쉽게 매핑할 수 있도록 하는 것이다. 우리의 모든 데이터 액세스 문제에 대해 단일 저장소 계층을 갖는 대신, Bounded Context의 경계를 따라 저장소를 세분화한다.

Bounded Context란 DDD 모델의 Context 즉 모델 간의 경계를 Bounded Context라고 한다. 하나의 도메인 안에는 여러 Context가 존재할 수도 있고, 여러 도메인에 걸처 하나의 Context가 생성될 수도 있다. 이와 같은 경계는 데이터를 분리하는 기준이 되며, 나아가 마이크로서비스를 구분하는 기준이 되기도 한다.

주어진 컨텍스트에 대해 코드 내부에 데이터베이스 매핑 코드를 같은 위치에 배치하면 어떤 코드가 데이터베이스의 어떤 부분을 사용하는지 이해하는 데 도움이 될 수 있다. 예를 들어, Hibernate는 Bounded Context 당 매핑 파일을 사용하는 경우 이 문제를 명확하게 할 수 있다. 따라서 우리는 Bounded Context가 스키마의 어떤 테이블에 접근하는지를 볼 수 있다. 이는 향후 데이터베이스 분리의 일부로 어떤 테이블이 이동해야 하는지 이해하는 데 큰 도움이 될 수 있다.

위와 같이 각각의 Context 별 Repository를 분리하면 데이터의 분리 구조로써 활용될 수 있을 것이다. 물론 데이터베이스 수준의 Constraint를 하나하나 따져봐야하겠지만, 앞서 이야기한 바와 같이 Repository를 구분하는 것부터 결합도를 분리하는 시작점이 될 수 있을 것이다. 이 모든 것은 나중에 서비스 경계가 될 테이블 간의 결합을 이해하는 데 도움이 될 것이다.

이 패턴은 DDD 개념에 따라 리포지토리 계층을 분류하면 데이터베이스뿐만 아니라 코드 자체에서도 마이크로서비스 용 Context를 분리하는데 많은 도움이 될 것이며, 대부분의 경우에 적용하기 용이한 패턴이다.

분리 패턴 2. Bounded Context 단위의 데이터베이스

애플리케이션 관점에서 데이터 액세스를 명확하게 분리했으면 이 접근 방식을 스키마에 계속 적용하는 것이 좋다. 마이크로서비스의 독립적인 배포는 자체 데이터를 소유해야 가능하다. 어플리케이션 코드를 분리하기 전에 식별된 Bounded Context를 중심으로 데이터베이스를 명확하게 분리하여 이 분해를 시작할 수 있다.

각 Bounded Context에는 자체 데이터베이스 Schema가 존재한다. 나중에 마이크로서비스로 분리해야 하는 경우 손쉽게 전환해 나갈 수 있는 형태를 마련하게 된다. 이는 단일 데이터베이스 Schema 보다 더 많은 작업이 필요하지만 마이크로서비스로 이동하는 것과 관련하여 결합도를 낮추고, 사전에 이슈 사항들을 점검할 수 있는 기틀을 마련하게 된다.

이와 같은 패턴은 신규 프로젝트를 구성할때 항상 권장하는 패턴이다. DDD를 통해 마이크로서비스를 설계하는 것과 비교할 때 사실 이 패턴은 굉장히 단순하고, 실패의 가능성을 내재하고 있다. 다만, DDD에 대해 충분한 내재화가 되지 않은 상황에서 무턱대고 설계에 뛰어들기에는 대부분의 기업에서 안정적인 도메인 Bounded Context를 식별할 만큼 충분히 성숙하지 않다. 이때 이 패턴은 좋은 중간 지점이 될 수 있다. 미래에 서비스 분리가 발생할 수 있다고 생각되는 곳에 스키마 분리를 유지하고, 마이크로서비스 설계에 대응하거나, 또는 마이크로서비스로 변화하지 않더라도 시스템의 복잡성을 줄이면서 시스템 개선 포인트를 명확히 잡아 나가는데 도움이 될 수 있는 패턴이다.


코드 분리 우선

아마도 많은 프로젝트에서 데이터베이스 분리 이전에 코드를 먼저 분리한다. 전통적인 개발 방법론 중 하나인 FDD 중심의 기능 분해도를 작성하고, 이를 기준으로 설계를 진행하기 때문에 더더욱 그러한 경향이 있다. 대체로 프로젝트에서는 서비스 설계 > 인터페이스 설계 > 테이블 설계 순으로 진행하게 되며, 이때 데이터베이스의 분리는 가장 마지막에 고려하게 된다.

애플리케이션 계층을 분할하면 새 서비스에 필요한 데이터를 훨씬 쉽게 이해할 수 있다. 또한 독립적으로 배포할 수 있는 코드 아티팩트를 더 일찍 확보할 수 있는 이점을 얻을 수 있다. 이 패턴은 영향도를 최소화 하여 분리해 나갈 수 있지만, 결국 데이터의 분리를 이뤄내지 못하고 중단되는 경우도 발생한다.

분리 패턴 1. 모놀리스 API Server

모놀리스 데이터에 직접 액세스하는 대신 모놀리스 서비스에 API를 생성하는 형태를 고려해 볼 수 있다. 아래와 같이 고객서비스는 고객 정보에 액세스할 수 있도록 모놀리스 서비스에 API를 만든다.

이와 같은 패턴은 사실 프로젝트에서 선호하는 패턴은 아니다. 모놀리스 어플리케이션 서비스는 조만간 사라질 서비스라는 인식으로 부터 모놀리스에 살을 붙이는 것은 불필요한 개발이라고 생각하기 때문일 것이다. 그러나 여기서 얻을 수 있는 이점은 분명하다. 데이터 분해 문제를 해결할 필요가 없고 정보를 숨겨야 하므로 새로운 서비스를 모노리스와 분리하여 보다 쉽게 유지할 수 있다. 만약 모놀리스의 데이터가 그대로 유지될 수 있다면 복잡한 분산데이터 처리를 적용하기에 앞서 이와 같은 형태를 검토해 보기를 추천한다.

초기 모놀리스가 제공하던 계좌API는 결국 계좌서비스로 분리되어 마이크로서비스 형태로 변형되어 갈 것이다.

이 패턴은 데이터를 관리하는 코드가 여전히 모놀리스 어플리케이션에 있을 때 가장 잘 작동한다. 물론 반드시 데이터를 마이크로서비스 내에 소유해야 하는 경우 모놀리스 데이터베이스와 마이크로서비스 분산DB 간의 중복 데이터를 저장하는 것보다 이 패턴을 생략하고 데이터를 분할하는 것을 고려해야 한다.

분리 패턴 2. 다중 스키마 스토리지

모놀리스 서비스와 고객 서비스 간 공존하는 데이터에 대해 항상 모든 데이터가 동기화 되어야 하는 것은 아니다. 모놀리스 서비스에서 사용하는 데이터 또는 타 서비스에서 사용하는 데이터에 대해 동기화 하되 신규로 서비스 되는 데이터에 대해서는 고객 서비스에 저장하는 방식이 적합하다.

예를 들어 아래와 같이 고객 서비스의 핵심 데이터는 CUSTID, PW, OrgID로 모놀리스, 고객서비스 모두에 저장된다. EventID의 경우 신규로 추가된 서비스로 고객서비스에 저장된다.

좀 더 확장해서 생각해 보자면, 모놀리스 서비스에 기존 데이터를 유지하고 고객서비스에 신규 추가된 Column을 추가한 후 이를 Key를 통해 관리할 수 있다. 아래와 같이 CUSTID를 기준으로 데이터가 관리되어 질 것이다.

이 예에서 우리는 Foreign-Key 관계가 스키마 경계에 효과적으로 걸쳐 있을 때 어떤 일이 일어나는지 고려해야 한다. 이는 다음 포스팅에서 더 자세히 살펴보도록 하자.

이 패턴은 마이크로서비스에 새로운 기능을 추가할 때 적용하기 용이하다. 모놀리스가 필요로 하는 데이터가 아니므로 고객서비스에만 해당 데이터를 유지하면 된다.


데이터베이스와 코드를 함께 분리

앞선 두 단계를 하나의 큰 단계로 코드와 데이터베이스를 한번에 분리할 수 있다.

다만, 이와 같은 접근이 단계를 뛰어 넘어 빠르게 수행될 것이라 판단하는 경우가 있지만, 대체로 더 오래 걸리는 작업이다. 특히 리스크를 감안할 때 충분한 검증 없이 빅뱅 방식의 전환이 될 수 있어 가능하면 이와 같은 전환은 배제하는 것이 바람직하며 대신 스키마 또는 애플리케이션 계층을 먼저 분할하는 것이 좋다.


결론

자 그럼 데이터베이스와 어플리케이션 중 어떤 것 부터 분리해 나가는 것이 좋은가에 대한 답변이 필요하다. 프로젝트에 따라 다르다는 답변을 하는 경우가 대부분이겠지만, 이 글을 읽은 독자들께서 원하는 답변은 아닐 수 있기에 개인적인 주관이 들어간 기준을 세워보도록 하겠다.

모놀리스를 변경할 수 있고 성능이나 데이터 일관성에 대한 잠재적인 영향이 우려되는 경우 먼저 스키마를 분리하는 방법을 권고한다. 그렇지 않으면 코드를 먼저 분리하고 데이터 소유권에 미치는 영향을 파악해 나가는 것이 좋다. 물론 이것이 모든 상황을 결정할 수 있는 절대적인 기준은 아니며, 상황에 맞게 선택하고 수행해 나가는 것은 당연한 이치일 것이다.

이번 포스팅에서는 데이터베이스와 코드의 분리 기준에 대해 살펴보았다. 다음 포스팅에서는 데이터베이스의 실질적인 분리 방안인 Schema 분리에 대해 알아보도록 하자.

728x90
반응형