마이크로서비스 아키텍처
1. 개요
1. 개요
마이크로서비스 아키텍처는 하나의 애플리케이션을 여러 개의 작고 독립적인 서비스로 분해하여 구성하는 소프트웨어 설계 방식이다. 각 서비스는 특정 비즈니스 도메인에 집중하며, 자체 프로세스에서 실행되고 경량 메커니즘을 통해 통신한다. 이는 전통적인 모놀리식 아키텍처와 대비되는 개념으로, 서비스별 독립적인 배포와 확장을 가능하게 한다.
데이터 측면에서 마이크로서비스 아키텍처는 핵심적인 패러다임 전환을 요구한다. 모놀리식 시스템에서는 하나의 중앙 집중식 데이터베이스를 공유하는 것이 일반적이었지만, 마이크로서비스에서는 각 서비스가 자체 데이터베이스를 소유하는 것을 원칙으로 삼는다. 이를 '데이터베이스 퍼 서비스' 패턴이라고 부르며, 서비스 간의 결합도를 낮추고 기술 스택 선택의 자유도를 높이는 장점이 있다.
그러나 데이터의 분산 소유는 새로운 복잡성을 초래한다. 서비스 간의 데이터 일관성 유지, 분산 트랜잭션 관리, 여러 서비스에 걸친 데이터 쿼리 실행 등이 주요 도전 과제로 부상한다. 이러한 문제를 해결하기 위해 SAGA 패턴, CQRS, 이벤트 소싱과 같은 패턴과 기법이 널리 사용된다.
결국 마이크로서비스 아키텍처에서 데이터 관리의 목표는 서비스의 자율성을 최대한 보장하면서도 시스템 전체의 데이터 정합성과 신뢰성을 유지하는 데 있다. 이는 데이터 저장소 선택, 통신 방식, 일관성 모델, 보안 정책에 이르기까지 포괄적인 설계 결정을 필요로 하는 영역이다.
2. 데이터 분산 전략
2. 데이터 분산 전략
마이크로서비스 아키텍처에서 데이터는 각 서비스의 자율성을 보장하기 위해 분산되어 관리된다. 이는 단일 모놀리식 애플리케이션이 하나의 중앙 집중식 데이터베이스를 사용하는 방식과 근본적으로 다르다. 데이터 분산 전략은 서비스 경계를 어떻게 데이터 소유권 경계와 일치시킬지, 그리고 데이터 결합과 일관성 요구사항을 어떻게 해결할지에 대한 결정을 포함한다. 주요 접근 방식으로는 데이터베이스 퍼 서비스 패턴, 공유 데이터베이스 패턴, 그리고 CQRS 패턴이 있다.
가장 일반적으로 권장되는 패턴은 데이터베이스 퍼 서비스 패턴이다. 이 패턴에서는 각 마이크로서비스가 자신의 전용 데이터베이스를 소유하고, 해당 데이터에 대한 접근은 오직 해당 서비스의 API를 통해서만 가능하다. 이는 서비스 간의 결합도를 낮추고, 서비스별로 최적의 데이터베이스 기술(폴리글랏 퍼시스턴스)을 선택할 수 있게 한다. 그러나 이 패턴은 여러 서비스에 걸친 데이터 조회나 트랜잭션을 복잡하게 만드는 도전 과제를 제기한다.
반면, 공유 데이터베이스 패턴은 여러 마이크로서비스가 단일 데이터베이스를 공유하는 방식이다. 이는 초기 단계에서 마이크로서비스로의 점진적 분리를 용이하게 하거나, 강력한 ACID 트랜잭션이 필수적인 경우에 고려될 수 있다. 그러나 이 패턴은 서비스 간의 강한 결합을 초래하여, 데이터 스키마 변경이 여러 서비스에 영향을 미치고 서비스의 독립적 배포를 어렵게 만드는 주요 단점을 가진다.
CQRS(Command Query Responsibility Segregation) 패턴은 데이터 업데이트(명령)와 데이터 조회(쿼리)를 위한 별도의 모델을 사용하는 전략이다. 이 패턴은 복잡한 쿼리 성능을 최적화하거나, 읽기와 쓰기 작업에 서로 다른 데이터 저장소를 사용해야 할 때 유용하다. 아래 표는 세 가지 주요 데이터 분산 전략의 핵심 특징을 비교한다.
패턴 | 데이터 소유권 | 결합도 | 유연성 | 주요 사용 사례 |
|---|---|---|---|---|
서비스 전용 | 낮음 | 높음 | 대부분의 마이크로서비스, 자율성 강조 | |
여러 서비스 공유 | 높음 | 낮음 | 레거시 시스템 분리 초기, 강한 트랜잭션 필요 | |
명령/쿼리 모델 분리 | 중간 | 중간 | 복잡한 쿼리, 높은 읽기 처리량, 이벤트 소싱과 결합 |
전략 선택은 데이터 일관성, 성능, 개발 복잡성, 그리고 운영 부하 사이의 트레이드오프에 기반한다. 데이터베이스 퍼 서비스 패턴이 이상적이지만, 이를 보완하기 위해 API 컴포지션이나 CQRS 같은 다른 패턴이 함께 사용되는 경우가 많다.
2.1. 데이터베이스 퍼 서비스 패턴
2.1. 데이터베이스 퍼 서비스 패턴
데이터베이스 퍼 서비스 패턴은 마이크로서비스 아키텍처의 핵심 데이터 분산 원칙 중 하나이다. 이 패턴은 각 마이크로서비스가 자신만의 전용 데이터베이스를 소유하고 독립적으로 관리하도록 규정한다. 서비스는 자신의 데이터에 대한 모든 권한을 가지며, 다른 서비스는 해당 서비스의 API를 통해서만 데이터에 접근할 수 있다. 이는 데이터의 소유권과 경계를 명확히 정의하여 서비스 간의 결합도를 낮추는 것을 목표로 한다.
이 패턴의 주요 장점은 서비스의 자율성을 극대화하는 데 있다. 각 서비스는 자신의 비즈니스 도메인에 가장 적합한 데이터베이스 종류(SQL, NoSQL)와 스키마를 독립적으로 선택하고 변경할 수 있다. 이는 폴리글랏 퍼시스턴스를 실현하는 기반이 된다. 또한, 한 서비스의 데이터베이스 장애나 성능 저하가 다른 서비스로 직접 전파되는 것을 방지하여 시스템 전체의 회복탄력성을 높인다.
그러나 이 패턴은 몇 가지 도전 과제를 동반한다. 여러 서비스에 걸친 비즈니스 트랜잭션을 구현하기 어려워 데이터 일관성 관리가 복잡해진다. SAGA 패턴이나 이벤트 소싱과 같은 패턴을 추가로 도입하여 최종 일관성을 달성해야 하는 경우가 많다. 또한, 서비스 간 데이터를 통합하여 조회하거나 분석하는 작업이 간단하지 않으며, API 컴포지션이나 별도의 읽기 전용 데이터 저장소를 구축하는 전략이 필요하다.
장점 | 도전 과제 |
|---|---|
서비스 자율성과 독립적인 배포 보장 | 분산 트랜잭션과 데이터 일관성 관리 복잡성 |
기술 스택(데이터베이스) 선택의 유연성 | 서비스 간 데이터 조회 및 분석의 어려움 |
데이터 모델의 독립적 진화 가능 | 데이터 중복 발생 가능성 |
장애 격리와 성능 분리 | 운영 복잡성 증가(모니터링, 백업 등) |
따라서 이 패턴을 채택할 때는 데이터 일관성 요구사항, 쿼리 패턴, 운영 부하 등을 종합적으로 고려해야 한다. 강한 일관성이 필수적인 도메인에서는 공유 데이터베이스 패턴과의 혼용을 고려할 수 있다[1].
2.2. 공유 데이터베이스 패턴
2.2. 공유 데이터베이스 패턴
공유 데이터베이스 패턴은 마이크로서비스 아키텍처에서 여러 서비스가 단일 데이터베이스 인스턴스를 공유하는 데이터 관리 접근 방식이다. 이 패턴은 기존의 모놀리식 아키텍처에서 데이터 계층을 분리하는 방식과 유사하며, 각 서비스는 독립적으로 배포되고 운영되지만 데이터는 중앙 집중화된 저장소에 위치한다. 서비스들은 동일한 데이터베이스 스키마의 서로 다른 테이블이나 뷰를 사용하거나, 경우에 따라 동일한 테이블에 접근하기도 한다.
이 패턴의 주요 장점은 데이터 일관성 유지가 상대적으로 간단하다는 점이다. ACID 트랜잭션을 활용하여 여러 서비스에 걸친 데이터 무결성을 보장할 수 있으며, 복잡한 분산 트랜잭션 메커니즘 없이도 데이터를 안정적으로 관리할 수 있다. 또한, 서비스 간 조인 연산이 데이터베이스 수준에서 직접 수행 가능하여 데이터 집계와 쿼리가 용이하다. 초기 개발 단계나 소규모 시스템에서 빠른 프로토타이핑과 간편한 운영을 필요로 할 때 고려된다.
장점 | 단점 |
|---|---|
강력한 ACID 트랜잭션 보장 | 서비스 간 결합도가 높아짐 |
복잡한 조인과 쿼리 용이 | 단일 장애점이 될 수 있음 |
운영 및 관리가 비교적 단순 | 데이터베이스 스키마 변경이 모든 서비스에 영향 |
데이터 중복 최소화 | 개별 서비스의 독립적인 확장성 제한 |
그러나 이 패턴은 마이크로서비스의 핵심 원칙 중 하나인 서비스의 독립성을 훼손하는 심각한 단점을 지닌다. 데이터베이스 스키마의 변경은 이를 공유하는 모든 서비스에 영향을 미칠 수 있어, 서비스의 자율적인 배포와 진화를 방해한다. 또한, 단일 데이터베이스가 성능 병목 현상이나 시스템 전체의 단일 장애점이 될 위험이 있다. 따라서 이 패턴은 일반적으로 마이크로서비스로의 과도기적 단계나 특정한 제약 조건 하에서만 제한적으로 사용되며, 장기적인 아키텍처 목표로는 데이터베이스 퍼 서비스 패턴이나 CQRS 패턴과 같은 더 분리된 접근법이 권장된다.
2.3. CQRS 패턴
2.3. CQRS 패턴
CQRS 패턴은 명령 조회 책임 분리 패턴의 약자로, 데이터의 읽기와 쓰기 작업을 위한 모델을 분리하는 설계 패턴이다. 이 패턴은 마이크로서비스 아키텍처에서 각 서비스가 자체 데이터를 소유하는 환경에서 복잡한 쿼리 요구사항을 효율적으로 처리하기 위해 고안되었다.
기본 개념은 단일 데이터 모델을 사용하여 읽기와 쓰기를 모두 처리하는 전통적인 방식과 달리, 명령 모델과 조회 모델이라는 두 개의 분리된 모델을 만드는 것이다. 명령 모델은 데이터를 생성, 수정, 삭제하는 쓰기 작업을 처리하며, 도메인 로직과 비즈니스 규칙 검증에 최적화된다. 조회 모델은 데이터를 읽고 표시하는 작업에 특화되어 있으며, 복잡한 조인과 집계를 포함한 다양한 쿼리를 지원하기 위해 데이터를 사전에 가공하여 저장한다. 두 모델 간의 데이터 동기화는 일반적으로 이벤트 소싱이나 메시지 브로커를 통한 도메인 이벤트 발행을 통해 비동기적으로 이루어진다.
CQRS 패턴을 적용하면 몇 가지 뚜렷한 장점을 얻을 수 있다. 먼저, 읽기와 쓰기 작업이 각각 독립적으로 확장될 수 있어 성능과 확장성이 향상된다. 또한, 조회 모델은 사용자 인터페이스나 보고서 요구사항에 맞춰 자유롭게 설계될 수 있어 복잡한 쿼리 구현이 단순해진다. 그러나 이 패턴은 시스템의 복잡성을 증가시키는 단점이 있다. 두 모델을 유지하고 동기화하는 오버헤드가 발생하며, 데이터의 최종 일관성을 보장해야 한다. 따라서 단순한 CRUD 애플리케이션보다는 읽기와 쓰기의 부하가 현저히 다르거나, 쿼리가 매우 복잡한 고성능 시스템에 선택적으로 적용하는 것이 바람직하다.
특징 | 명령(쓰기) 모델 | 조회(읽기) 모델 |
|---|---|---|
주요 책임 | 데이터 상태 변경, 비즈니스 규칙 검증 | 데이터 조회 및 표현 |
최적화 목표 | 데이터 무결성, 트랜잭션 일관성 | 쿼리 성능, 읽기 처리량 |
데이터 저장소 | SQL, NoSQL, 인메모리 데이터베이스 등 다양 | |
일관성 수준 | 강한 일관성 | 최종 일관성 |
3. 데이터 일관성 관리
3. 데이터 일관성 관리
마이크로서비스 아키텍처에서는 데이터가 각 서비스의 자율적인 도메인 경계 내에 분산되어 저장된다. 이로 인해 여러 서비스에 걸친 비즈니스 트랜잭션을 처리할 때 데이터의 일관성을 유지하는 것이 주요 과제가 된다. 단일 데이터베이스에서의 ACID 트랜잭션과 같은 강력한 일관성 모델을 적용하기 어렵기 때문에, 결과적 일관성을 달성하기 위한 다양한 패턴과 기법이 사용된다.
SAGA 패턴은 장기 실행 트랜잭션을 관리하는 핵심 패턴이다. 하나의 비즈니스 프로세스를 여러 개의 로컬 트랜잭션으로 분해하고, 각 로컬 트랜잭션은 특정 서비스에서 실행된다. 한 서비스의 트랜잭션이 완료되면, 그 결과는 이벤트를 통해 다음 서비스로 전달되어 프로세스를 계속 진행한다. 만약 중간 단계에서 실패하면, 이미 성공한 트랜잭션들을 보상 트랜잭션(Compensating Transaction)을 통해 순차적으로 롤백하여 데이터 일관성을 복구한다. SAGA 패턴은 중앙 오케스트레이터에 의해 조율되는 방식(Orchestration)과 각 서비스가 이벤트를 구독하여 자율적으로 다음 단계를 실행하는 방식(Choreography)으로 구현될 수 있다.
이벤트 소싱은 상태 변경을 일련의 불변 이벤트의 스트림으로 저장하는 패턴이다. 서비스의 현재 상태는 이 이벤트 스트림을 처음부터 재생함으로써 파생된다. 이 방식은 모든 상태 변경의 완전한 감사 추적을 제공하며, SAGA 패턴과 결합되어 복잡한 비즈니스 워크플로우의 일관성을 관리하는 데 효과적이다. 특정 시점의 상태를 재구성하거나, 새로운 읽기 모델을 생성하는 것이 가능해진다. 그러나 이벤트 저장소의 설계와 이벤트 버전 관리(스키마 진화)는 추가적인 고려 사항이다.
패턴/기법 | 핵심 개념 | 주요 장점 | 고려 사항 |
|---|---|---|---|
장기 실행 트랜잭션을 로컬 트랜잭션 시퀀스로 분해 | 느슨한 결합, 서비스 자율성 유지 | 보상 트랜잭션 설계의 복잡성, 디버깅 어려움 | |
상태를 이벤트 스트림으로 저장 | 완전한 감사 추적, 시간 여행 쿼리 가능 | 이벤트 스토리지 설계, 이벤트 버전 관리 필요 | |
2단계 커밋(2PC) 등의 프로토콜 사용 | 강한 일관성 제공 | 가용성과 성능 저하, 구현 복잡성 |
전통적인 분산 트랜잭션 프로토콜인 2단계 커밋(2PC)은 여러 서비스의 데이터베이스에 걸쳐 원자성을 보장한다. 그러나 이 방식은 모든 참여자가 트랜잭션 기간 동안 잠금을 유지해야 하므로, 가용성과 성능에 부정적인 영향을 미칠 수 있다. 또한 많은 NoSQL 데이터베이스와 메시징 시스템이 이를 지원하지 않는다. 따라서 마이크로서비스 환경에서는 SAGA 패턴과 이벤트 소싱과 같은 비동기적이고 결과적 일관성을 기반으로 한 접근법이 더 선호되는 경향이 있다.
3.1. SAGA 패턴
3.1. SAGA 패턴
SAGA 패턴은 마이크로서비스 아키텍처에서 여러 서비스에 걸친 데이터 일관성을 보장하기 위한 설계 패턴이다. 단일 ACID 트랜잭션을 사용할 수 없는 분산 환경에서, 하나의 비즈니스 트랜잭션을 여러 개의 로컬 트랜잭션으로 나누고, 실패 시 이를 보상하는 방식으로 전체 작업의 원자성을 달성한다. 각 로컬 트랜잭션은 특정 서비스의 데이터를 업데이트하고, 그 다음 단계를 트리거하기 위해 이벤트를 발행한다.
주요 구현 방식은 크게 두 가지로 나뉜다. 첫째는 코레오그래피(Choreography) 방식으로, 각 서비스가 이벤트를 구독하고 다음 로컬 트랜잭션을 자율적으로 실행한다. 중앙 조정자가 없어 결합도가 낮지만, 트랜잭션 흐름이 분산되어 추적이 어려울 수 있다. 둘째는 오케스트레이션(Orchestration) 방식으로, 중앙 SAGA 오케스트레이터가 모든 단계를 순차적으로 명령하고 상태를 관리한다. 흐름 제어가 명확하지만, 오케스트레이터가 단일 장애점이 될 위험이 존재한다.
패턴 적용 시 고려해야 할 사항은 다음과 같다.
고려 사항 | 설명 |
|---|---|
보상 트랜잭션 | 각 로컬 트랜잭션에 대해 실패 시 원상복구하는 역작업을 설계해야 한다. |
멱등성 | 네트워크 장애로 인한 재시도 상황에서 동일한 작업을 여러 번 실행해도 안전해야 한다. |
비관찰성 | 중간 단계의 데이터 불일치가 외부에 노출되지 않도록 설계해야 한다[2]. |
이 패턴은 주문 처리, 예약 시스템, 금융 거래 등 장시간 실행되는 비즈니스 프로세스에 적합하다. 그러나 보상 로직의 복잡성과 데이터 일관성이 결과적 일관성 수준으로 완화된다는 점이 주요 단점으로 지적된다.
3.2. 이벤트 소싱
3.2. 이벤트 소싱
이벤트 소싱은 마이크로서비스 아키텍처에서 애플리케이션의 상태 변화를 일련의 불변 이벤트로 기록하는 데이터 저장 방식을 말한다. 전통적인 방식은 현재 상태만을 저장하지만, 이벤트 소싱은 상태를 변경시키는 모든 사건(이벤트)을 순서대로 저장한다. 애플리케이션의 현재 상태는 이 이벤트 로그의 처음부터 재생하여 재구성할 수 있다. 이 방식은 도메인 주도 설계와 잘 어울리며, 시스템의 모든 변경 내역에 대한 완전한 감사 추적을 제공한다.
이벤트 소싱의 주요 구성 요소는 다음과 같다.
구성 요소 | 설명 |
|---|---|
이벤트 | 발생한 사건을 나타내는 불변의 데이터 객체이다. 예를 들어 |
이벤트 저장소 | 발생한 모든 이벤트를 순서대로 저장하는 지속성 저장소이다. |
애그리게이트 | 일련의 이벤트를 적용하여 현재 상태를 유지하는 도메인 객체이다. 상태 변경은 새로운 이벤트를 생성하여 저장소에 추가하는 방식으로만 이루어진다. |
프로젝션 | 이벤트 저장소의 이벤트를 읽어 특정 용도(예: 조회용 뷰)에 맞는 물리화된 뷰를 생성하는 구성 요소이다. |
이 패턴은 데이터 일관성 관리에 몇 가지 장점을 제공한다. 첫째, 시스템의 상태 변화에 대한 단일 진실 공급원이 이벤트 로그이므로, 데이터 불일치 문제를 추적하고 해결하기가 용이하다. 둘째, 과거의 모든 상태를 재현할 수 있어 디버깅과 감사에 매우 유용하다. 셋째, 시간 여행이 가능해지며, 특정 시점의 상태로 롤백하거나 새로운 프로젝션을 생성하는 것이 비교적 쉽다. 그러나 이벤트 로그만으로는 복잡한 조회가 어려우므로, CQRS 패턴과 결합하여 조회용 최적화 뷰를 별도로 구축하는 것이 일반적이다.
반면, 이벤트 소싱은 새로운 패러다임을 요구하기 때문에 구현 복잡도가 증가한다. 이벤트 스키마의 버전 관리와 이벤트 재생 성능, 그리고 장기간 운영 시 이벤트 저장소의 크기 관리가 주요 도전 과제가 된다. 또한, 외부 시스템과의 통합 시 이벤트의 의미를 공유해야 하는 등 운영상의 고려사항이 존재한다[3].
3.3. 분산 트랜잭션
3.3. 분산 트랜잭션
분산 트랜잡션은 여러 개의 독립된 마이크로서비스에 걸쳐 있는 데이터 작업을 하나의 논리적 단위로 묶어, 전체가 성공하거나 전체가 실패하도록 보장하는 메커니즘이다. 전통적인 모놀리식 애플리케이션에서는 단일 데이터베이스의 ACID 트랜잭션을 통해 이를 쉽게 달성할 수 있었다. 그러나 서비스별로 데이터 저장소가 분리된 마이크로서비스 환경에서는 각 서비스의 데이터베이스가 자율적으로 관리되기 때문에, 단일 트랜잭션으로 여러 서비스의 데이터를 일관되게 업데이트하는 것이 기술적으로 매우 어렵다.
이를 해결하기 위한 주요 프로토콜로는 2단계 커밋이 있다. 이 프로토콜은 트랜잭션 매니저가 모든 참여 서비스(자원 관리자)에게 커밋 준비를 요청하는 '준비 단계'와, 모든 서비스가 준비에 성공하면 커밋을 지시하는 '커밋 단계'로 구성된다. 만약 한 서비스라도 실패하면 모든 서비스에 롤백을 지시한다. 이를 통해 원자성을 보장할 수 있지만, 모든 서비스가 트랜잭션 기간 동안 관련 리소스를 잠그고 대기해야 하므로 가용성과 성능에 심각한 영향을 미친다. 또한, 트랜잭션 매니저가 단일 장애점이 될 위험이 존재한다.
접근 방식 | 설명 | 장점 | 단점 |
|---|---|---|---|
2단계 커밋 (2PC) | 중앙 조정자(트랜잭션 매니저)가 참여자들의 커밋 준비와 실행을 조율하는 프로토콜 | 강한 일관성 보장, 원자성 달성 | 성능 저하, 잠금 시간 길어짐, 단일 장애점 위험, 부분적 실패 시 복잡한 회복 절차 |
보상 트랜잭션 | 비즈니스 로직에 따라 실패 시 이전 작업을 취소하는 보상 작업을 명시적으로 정의하는 방식 | 느슨한 결합, 서비스 자율성 유지 | 모든 실패 시나리오에 대한 보상 로직을 개발자가 직접 설계하고 구현해야 함 |
이러한 문제들로 인해 마이크로서비스 커뮤니티에서는 분산 트랜잭션 대신 최종 일관성 모델을 채택하고, SAGA 패턴이나 이벤트 소싱과 같은 대안적 패턴을 사용하여 데이터 일관성을 관리하는 것이 일반적이다. 분산 트랜잭션은 철저한 일관성이 절대적으로 필요한 매우 제한적인 시나리오에서만 고려되며, 그 사용은 성능 비용과 시스템 복잡도를 크게 증가시킨다.
4. 데이터 통신과 API
4. 데이터 통신과 API
마이크로서비스 아키텍처에서 서비스 간의 데이터 교환은 API 설계와 통신 방식을 통해 이루어진다. 각 서비스는 독립적으로 배포되고 운영되므로, 명확한 계약과 효율적인 통신 메커니즘이 시스템의 신뢰성과 확장성을 결정한다. 통신 방식은 주로 동기식 REST나 gRPC와 같은 API 호출, 또는 비동기식 메시지 브로커를 통한 이벤트 기반 통신으로 구분된다.
API 설계는 서비스의 경계와 책임을 명확히 정의하는 데 핵심적이다. 일반적으로 RESTful API 설계 원칙을 따르거나, 성능과 강력한 계약이 필요한 경우 gRPC를 사용한다. API 설계 시 고려해야 할 주요 원칙은 다음과 같다.
설계 원칙 | 설명 |
|---|---|
계약 우선 설계 | API 명세(예: OpenAPI, 프로토콜 버퍼)를 먼저 정의하여 클라이언트와 서버 간의 명확한 인터페이스를 확보한다. |
역할 기반 엔드포인트 | 도메인 중심의 리소스와 동작을 정의하며, 서비스의 단일 책임 원칙을 반영한다. |
버전 관리 | API 변경이 기존 클라이언트에 영향을 주지 않도록 버전 관리 전략(예: URI 버전 관리, 헤더 버전 관리)을 수립한다. |
비동기 통신을 위한 이벤트 기반 통신은 서비스 간의 느슨한 결합을 가능하게 한다. 한 서비스에서 발생한 상태 변화나 사건을 이벤트로 발행하면, 관심 있는 다른 서비스들이 이를 구독하여 반응한다. 이 방식은 Apache Kafka, RabbitMQ, Amazon SQS 같은 메시징 플랫폼을 통해 구현되며, 시스템의 가용성과 응답성을 높이는 장점이 있다. 그러나 이벤트ual 일관성을 관리하고, 중복 메시지 처리, 이벤트 순서 보장 등의 문제를 해결해야 한다.
데이터 계약 관리는 서비스 독립적 배포와 진화의 기반이다. API 스키마나 이벤트 페이로드의 변경은 하위 호환성을 유지하며 진행되어야 한다. 일반적으로 필드 추가와 같은 호환되는 변경은 허용되지만, 필드 제거나 타입 변경은 신중히 처리한다. 스키마 레지스트리를 도입하여 중앙에서 스키마 버전을 관리하고 호환성을 검증하는 것이 일반적인 실무이다.
4.1. API 설계 원칙
4.1. API 설계 원칙
마이크로서비스 간의 데이터 통신은 주로 API를 통해 이루어진다. 효과적인 API 설계는 시스템의 결합도를 낮추고 진화를 가능하게 하는 핵심 요소이다. 이를 위해 몇 가지 핵심 원칙이 널리 채택된다.
첫 번째 원칙은 느슨한 결합을 달성하는 것이다. 서비스는 다른 서비스의 내부 구현 세부 사항에 의존하지 않고, 잘 정의된 인터페이스(계약)를 통해서만 상호작용해야 한다. 이를 통해 한 서비스의 변경이 다른 서비스에 영향을 최소화한다. 두 번째 원칙은 API 우선 설계이다. 구현을 시작하기 전에 API의 엔드포인트, 요청/응답 형식, 오류 코드 등을 명확히 정의한다. 이는 팀 간 협업과 클라이언트 개발을 동시에 진행할 수 있게 한다.
다른 중요한 원칙들은 다음과 같다.
원칙 | 설명 | 주요 고려사항 |
|---|---|---|
비즈니스 능력 중심 설계 | API의 경계와 모델은 조직의 비즈니스 도메인과 일치시킨다. | |
버전 관리 | API 변경은 하위 호환성을 깨뜨릴 수 있으므로 체계적인 버전 관리 전략이 필요하다. | URI 경로, 헤더, 미디어 타입 등을 통한 버전 지정이 일반적이다. |
자체 설명적 API | 요청과 응답은 충분한 정보를 포함하여 문서를 보지 않고도 이해할 수 있어야 한다. | 표준화된 오류 응답, 적절한 HTTP 상태 코드 사용이 포함된다. |
클라이언트 필요에 맞춤 | 일반적인 CRUD API보다는 특정 클라이언트 화면이나 작업에 최적화된 API를 설계한다. | 이를 구현하는 패턴으로 Backend For Frontend 패턴이 있다. |
이러한 원칙들을 따르면 마이크로서비스가 독립적으로 배포되고 확장될 수 있는 토대를 마련할 수 있다. 최종적으로는 시스템 전체의 유지보수성과 개발자 생산성을 높이는 데 기여한다.
4.2. 이벤트 기반 통신
4.2. 이벤트 기반 통신
이벤트 기반 통신은 마이크로서비스 아키텍처에서 서비스 간의 느슨한 결합을 실현하는 핵심 패턴이다. 이 방식에서는 한 서비스가 특정 사건(이벤트)의 발생을 알리면, 관심 있는 다른 서비스들이 이를 구독하고 비동기적으로 반응한다. 발행-구독 모델을 따르는 경우가 많으며, 메시지 브로커나 이벤트 버스를 중간 매개체로 활용한다.
이 통신 방식의 주요 구성 요소는 이벤트 생산자, 이벤트 채널, 이벤트 소비자이다. 생산자는 상태 변경이나 비즈니스 활동 완료와 같은 의미 있는 사건을 감지해 이벤트 메시지를 생성하여 채널에 발행한다. 소비자는 자신이 관심 있는 이벤트 유형을 구독하고, 해당 이벤트가 채널에 도착하면 이를 수신하여 자신의 로직을 실행한다. 이 과정은 일반적으로 비동기 통신으로 이루어지므로, 서비스 간의 직접적인 호출과 의존성이 크게 줄어든다.
이 패턴의 장점과 활용 사례는 다음과 같다.
장점 | 설명 |
|---|---|
느슨한 결합 | 서비스들은 서로의 존재나 상태를 직접 알 필요 없이, 이벤트 채널을 통해 간접적으로 소통한다. |
확장성 | 이벤트 처리 부하에 따라 소비자 서비스 인스턴스를 독립적으로 확장할 수 있다. |
복원력 | 생산자나 소비자 중 일부가 일시적으로 불능 상태가 되어도, 메시지 큐에 이벤트가 유지되므로 시스템 전체가 중단되지 않는다. |
실시간 반응 | 상태 변경이 실시간으로 여러 서비스에 전파되어, 통합된 시스템 뷰를 유지하거나 후속 작업을 트리거할 수 있다. |
이벤트 기반 통신은 도메인 주도 설계의 컨텍스트 간 통합이나, CQRS 패턴에서 명령과 조회 모델의 데이터 동기화, SAGA 패턴에서 분산 트랜잭션의 단계 조율 등 다양한 시나리오에 적용된다. 그러나 이벤트의 순서 보장, 중복 처리, 장애 복구를 위한 멱등성 설계, 그리고 분산 시스템 모니터링의 복잡성 증가와 같은 도전 과제도 동반한다.
4.3. 데이터 계약 관리
4.3. 데이터 계약 관리
데이터 계약은 마이크로서비스 간에 교환되는 데이터의 구조, 의미, 형식을 명확히 정의한 규약이다. 이는 서비스 간의 결합도를 낮추고 독립적인 진화를 가능하게 하는 핵심 요소이다. 데이터 계약은 일반적으로 API의 요청 및 응답 페이로드, 또는 이벤트 기반 통신에서 발행되는 이벤트 메시지의 스키마로 정의된다.
데이터 계약을 관리하는 주요 원칙은 명시성과 하위 호환성 유지이다. 계약은 JSON 스키마, Avro, Protobuf와 같은 형식 언어로 명시적으로 문서화되어야 한다. 특히 스키마 레지스트리를 도입하여 중앙에서 계약의 버전을 관리하고, 서비스 간 호환성을 검증하는 것이 일반적이다. 이는 새로운 필드 추가와 같은 변경은 허용하지만, 기존 필드의 삭제나 필수 필드의 의미 변경은 피해야 함을 의미한다.
관리 전략 | 설명 | 주요 도구/기술 |
|---|---|---|
명시적 스키마 정의 | 데이터 형식을 공식적인 스키마 언어로 정의하여 모호성을 제거한다. | |
스키마 레지스트리 | 게시/구독 모델에서 스키마 버전을 중앙 저장소에서 관리하고 호환성을 검증한다. | |
하위 호환성 보장 | 기존 소비자(클라이언트 서비스)를 깨뜨리지 않는 방식으로 계약을 진화시킨다. | 새로운 필드 추가(Optional), 기존 필드 의미 변경 금지 |
계약 테스트 | 생산자와 소비자 서비스가 계약을 준수하는지 자동화된 테스트를 통해 검증한다. |
효과적인 데이터 계약 관리는 스키마 진화 문제를 완화한다. 버전 관리 정책(예: 시맨틱 버저닝)을 수립하고, 오래된 계약 버전에 대한 지원 종료(Deprecation) 및 폐기(Sunsetting) 절차를 명확히 해야 한다. 이를 통해 서비스 팀은 자율적으로 발전할 수 있으면서도 시스템 전체의 안정성을 유지할 수 있다.
5. 데이터 저장소 선택
5. 데이터 저장소 선택
각 마이크로서비스는 자체 데이터를 독립적으로 관리하는 것이 이상적이다. 이를 위해 서비스의 도메인 요구사항과 데이터 특성에 가장 적합한 저장소를 선택하는 폴리글랏 퍼시스턴스 접근 방식이 권장된다. 이는 각 서비스가 관계형 데이터베이스, 문서 데이터베이스, 키-값 저장소, 그래프 데이터베이스 등 다양한 유형의 저장소를 자유롭게 채택할 수 있음을 의미한다. 선택의 핵심은 데이터 모델, 읽기/쓰기 패턴, 확장성 요구사항, 일관성 수준 등을 종합적으로 고려하는 것이다.
SQL과 NoSQL 데이터베이스 사이의 선택은 중요한 결정 사항이다. SQL 데이터베이스는 강력한 ACID 트랜잭션, 정규화된 스키마, 복잡한 조인 쿼리에 적합하다. 반면 NoSQL 데이터베이스는 수평적 확장성, 유연한 스키마, 특정 접근 패턴에 대한 높은 성능을 제공한다. 일반적으로 주문 관리처럼 강한 일관성이 필요한 서비스에는 SQL이, 사용자 세션이나 카탈로그 데이터처럼 대용량 읽기와 유연성이 중요한 서비스에는 NoSQL이 더 적합할 수 있다.
성능 최적화를 위해 캐싱 전략은 필수적이다. 자주 읽히지만 자주 변경되지 않는 데이터는 캐싱의 좋은 후보이다. 캐싱은 여러 수준에서 구현될 수 있다.
캐시 유형 | 위치 | 사용 사례 | 주의사항 |
|---|---|---|---|
애플리케이션 캐시 | 서비스 내 메모리 | 서비스 전용 고빈도 데이터 | 서비스 인스턴스 간 불일치 가능 |
분산 캐시 | 여러 인스턴스가 공유하는 데이터 | 네트워크 지연 발생 | |
데이터베이스 캐시 | 데이터베이스 자체 쿼리 캐시 | 반복되는 복잡한 쿼리 결과 | 데이터 변경 시 캐시 무효화 필요 |
캐시 무효화 정책(예: TTL 설정, 쓰기 시 캐시 삭제)을 신중하게 설계하지 않으면 오래된 데이터를 제공하는 문제가 발생할 수 있다.
5.1. 폴리글랏 퍼시스턴스
5.1. 폴리글랏 퍼시스턴스
마이크로서비스 아키텍처에서 폴리글랏 퍼시스턴스는 각 마이크로서비스가 자체적인 데이터 저장소 기술을 독립적으로 선택하고 운영하는 설계 원칙이다. 이는 단일 모놀리식 아키텍처에서 하나의 범용 데이터베이스를 사용하는 전통적인 방식과 대비된다. 각 서비스는 자신의 특정한 데이터 요구사항, 접근 패턴, 확장성 필요에 가장 적합한 저장소를 채택할 수 있다. 예를 들어, 주문 서비스는 관계형 데이터베이스를, 제품 카탈로그 서비스는 문서 데이터베이스를, 사용자 세션 서비스는 키-값 저장소를 사용할 수 있다.
이 접근법의 주요 장점은 기술적 유연성과 서비스의 느슨한 결합이다. 각 서비스는 최적의 도구를 사용하여 성능과 확장성을 극대화할 수 있으며, 한 서비스의 데이터 저장소 변경이나 장애가 다른 서비스에 직접적인 영향을 미치지 않는다. 또한, 서비스별로 데이터 스키마를 독립적으로 진화시킬 수 있어 개발 속도를 높인다. 그러나 이는 운영 복잡성을 증가시키는 도전 과제를 동반한다.
폴리글랏 퍼시스턴스를 성공적으로 구현하기 위해서는 몇 가지 고려사항이 필요하다. 첫째, 다양한 데이터 저장소를 운영하고 모니터링하는 데 필요한 운영 전문성과 인프라가 요구된다. 둘째, 서비스 간 데이터 일관성을 유지하는 메커니즘이 필요하며, SAGA 패턴이나 이벤트 소싱 같은 패턴이 자주 활용된다. 셋째, 데이터 저장소 선택 시 서비스의 현재와 미래 요구사항을 신중히 평가해야 한다.
장점 | 도전 과제 |
|---|---|
서비스별 최적의 기술 선택 가능 | 운영 및 모니터링 복잡성 증가 |
서비스 간 결합도 감소 | 데이터 일관성 관리 어려움 |
성능과 확장성 최적화 | 개발자에게 다양한 기술 숙련도 요구 |
스키마 독립적 진화 가능 | 디버깅과 트랜잭션 추적이 복잡해짐 |
결론적으로, 폴리글랏 퍼시스턴스는 강력한 자율성과 최적화를 제공하지만, 증가된 복잡성을 관리할 수 있는 조직적, 기술적 역량이 전제되어야 한다. 이는 모든 서비스에 단일 데이터베이스를 강제하는 "원 사이즈 핏츠 올" 접근법의 한계를 해결하는 대안으로 평가받는다.
5.2. SQL vs NoSQL
5.2. SQL vs NoSQL
마이크로서비스 아키텍처에서 각 서비스는 자율적인 데이터 저장소를 선택할 수 있다. 이는 폴리글랏 퍼시스턴스의 핵심 개념으로, 각 서비스의 요구사항에 가장 적합한 데이터베이스 기술을 채택하는 것을 의미한다. 주요 선택지는 관계형 데이터베이스를 지칭하는 SQL과 비관계형 데이터베이스를 통칭하는 NoSQL로 나뉜다.
특성 | SQL (관계형 데이터베이스) | NoSQL (비관계형 데이터베이스) |
|---|---|---|
데이터 모델 | 고정된 스키마와 테이블, 행, 열 구조 | 유연한 스키마 (문서, 키-값, 컬럼, 그래프 등) |
트랜잭션 | 강력한 ACID 트랜잭션 지원 | 기본적으로 BASE 모델, 상황에 따라 제한적 ACID 지원 |
확장성 | 주로 수직 확장(Scale-up)에 적합 | 수평 확장(Scale-out)에 용이 |
쿼리 언어 | 표준화된 SQL 언어 사용 | 데이터베이스별 고유 API 또는 제한적 쿼리 언어 |
사용 사례 | 복잡한 조인, 정교한 트랜잭션, 강한 일관성이 필요한 서비스 | 대규모 분산 데이터, 빠른 읽기/쓰기, 유연한 스키마 변화가 필요한 서비스 |
SQL 데이터베이스는 구조화된 데이터와 복잡한 관계, 데이터 무결성이 가장 중요한 서비스에 적합하다. 예를 들어, 주문 처리나 금융 거래와 같이 정확성과 일관성이 필수적인 바운디드 컨텍스트에서 선호된다. 반면, NoSQL 데이터베이스는 문서형(예: MongoDB)은 유연한 데이터 구조가 필요할 때, 키-값형(예: Redis)은 빠른 캐싱이나 세션 저장에, 컬럼형(예: Cassandra)은 대량의 데이터 쓰기에, 그래프형(예: Neo4j)은 복잡한 관계 분석에 각각 특화되어 있다.
선택은 서비스의 데이터 접근 패턴, 일관성 요구사항, 확장성 목표에 따라 결정된다. 단일 서비스 내에서도 CQRS 패턴을 적용하여 명령(쓰기) 모델에는 강한 일관성의 SQL을, 조회(읽기) 모델에는 고가용성의 NoSQL을 사용하는 하이브리드 접근법도 가능하다. 최종 선택은 기술적 적합성뿐만 아니라 운영 복잡성과 팀의 숙련도도 고려해야 한다.
5.3. 캐싱 전략
5.3. 캐싱 전략
마이크로서비스 아키텍처 환경에서 캐싱은 성능 향상, 데이터 저장소 부하 감소, 그리고 서비스의 응답 시간 개선을 위한 핵심 전략이다. 각 서비스가 독립적으로 배포되고 운영되기 때문에, 캐싱 전략도 서비스의 특성과 데이터 접근 패턴에 맞게 설계되어야 한다. 일반적으로 사용되는 캐싱 계층은 서비스 내 로컬 캐시와 서비스 외부의 분산 캐시로 구분된다.
로컬 캐시는 JVM 힙 메모리나 서비스 인스턴스 내부의 전용 공간에 데이터를 저장하는 방식이다. 접근 속도가 매우 빠르지만, 서비스 인스턴스 간 데이터 일관성을 유지하기 어렵고, 인스턴스가 재시작되면 캐시 데이터가 소실된다는 단점이 있다. 반면, Redis나 Memcached와 같은 분산 캐시는 여러 서비스 인스턴스가 공유할 수 있는 외부 저장소를 제공한다. 이는 데이터 일관성을 보장하기 쉽고, 캐시 서버의 확장이 용이하지만, 네트워크 지연 시간이 추가된다.
효과적인 캐싱을 위해서는 적절한 캐시 만료 정책과 무효화 전략이 필수적이다. 주요 전략은 다음과 같다.
전략 | 설명 | 적합한 상황 |
|---|---|---|
TTL (Time To Live) | 데이터를 캐시에 저장할 때 미리 정의된 수명을 설정한다. 시간이 지나면 자동으로 만료된다. | 자주 변경되지 않고, 정확한 실시간 일관성이 요구되지 않는 읽기 위주의 데이터. |
명시적 무효화 | 원본 데이터가 변경될 때 관련 캐시 항목을 직접 삭제하거나 갱신하는 방식이다. | 데이터 변경 시 즉시 캐시를 갱신해야 하는 강한 일관성이 필요한 경우. |
캐시 aside (Lazy Loading) | 애플리케이션이 먼저 캐시를 조회하고, 데이터가 없을 때만 저장소에서 읽어와 캐시에 저장한다. | 일반적인 읽기 패턴에 널리 사용되며, 캐시 미스 발생 시 저장소 부하가 증가할 수 있다. |
Write-through | 데이터를 저장소에 기록할 때 동시에 캐시에도 기록하는 방식이다. | 쓰기 성능은 저하될 수 있으나, 캐시와 저장소의 데이터 일관성을 높인다. |
캐싱은 성능을 크게 향상시킬 수 있지만, 잘못된 구현은 데이터 부정합을 초래하거나 시스템 복잡성을 증가시킬 수 있다. 특히 분산 환경에서는 캐시 스탬피드(여러 요청이 동시에 캐시 미스를 일으켜 저장소에 집중적으로 접근하는 현상)나 캐시 일관성 문제에 주의해야 한다. 따라서 각 서비스의 도메인과 요구사항을 분석하여 캐시할 데이터, 캐시 수준(로컬/분산), 그리고 무효화 메커니즘을 신중하게 선택해야 한다.
6. 데이터 쿼리와 집계
6. 데이터 쿼리와 집계
마이크로서비스 아키텍처에서 데이터는 각 서비스의 경계 내에 분산되어 저장된다. 이는 데이터의 소유권과 캡슐화를 명확히 하지만, 여러 서비스에 걸친 데이터를 조회하거나 통합된 뷰를 생성해야 할 때 복잡성을 초래한다. 단일 데이터베이스에서 실행하던 조인 쿼리가 더 이상 불가능해지기 때문이다. 따라서 분산 환경에 적합한 쿼리와 집계 전략이 필요하다.
주요 접근 방식으로는 API 컴포지션과 CQRS 패턴이 있다. API 컴포지션은 클라이언트나 전용 API 게이트웨이가 필요한 데이터를 보유한 여러 마이크로서비스를 개별적으로 호출하고, 그 결과를 메모리에서 조합하여 응답을 구성한다. 이 방식은 구현이 비교적 단순하지만, 여러 서비스를 호출하는 오버헤드가 있고, 서비스 간 데이터 일관성이 약한 수준일 수 있다. CQRS 패턴은 명령과 조회의 책임을 분리한다. 조회를 위해 하나 이상의 서비스의 데이터를 미리 집계하여 물리적으로 중복 저장하는 읽기 전용 뷰를 생성하고, 이 뷰를 통해 효율적인 쿼리를 수행한다.
분산 데이터 분석을 위해서는 이벤트 드리븐 아키텍처가 자주 활용된다. 각 서비스는 발생한 사건을 이벤트 형태로 발행하고, 이러한 이벤트 스트림을 아파치 카프카나 아마존 키네시스 같은 플랫폼을 통해 수집한다. 분석용 데이터 웨어하우스나 데이터 레이크는 이 이벤트 스트림을 구독하여 필요한 형식으로 변환하고 저장한다. 이를 통해 운영 시스템의 성능에 영향을 주지 않고 대규모 배치 분석이나 실시간 스트림 처리를 수행할 수 있다.
전략 | 설명 | 장점 | 단점 |
|---|---|---|---|
API 컴포지션 | 쿼리 수행자가 여러 서비스를 호출해 결과를 조합함. | 구현이 단순하고 실시간 데이터 조회 가능. | 성능 오버헤드가 크고, 서비스 장애에 취약함. |
CQRS (읽기 모델) | 조회 전용의 비정규화된 데이터 뷰를 별도로 유지함. | 쿼리 성능이 매우 우수하고, 읽기/쓰기 부하 분리 가능. | 데이터 중복 발생, 뷰의 최신성 유지가 복잡함. |
이벤트 기반 집계 | 이벤트 스트림을 수집해 분석용 저장소에 집계함. | 운영과 분석 시스템 분리, 확장성과 실시간 처리 가능. | 이벤트 스키마 관리와 처리 지연 시간(latency) 문제가 있음. |
6.1. API 컴포지션
6.1. API 컴포지션
API 컴포지션은 여러 마이크로서비스에 분산된 데이터를 조회하여 클라이언트에 통합된 뷰를 제공하기 위한 패턴이다. 이 패턴은 데이터 소유권 경계를 유지하면서 복잡한 쿼리 요구사항을 해결한다. 클라이언트가 여러 서비스의 데이터를 필요로 할 때, API 게이트웨이나 전용 컴포지터 서비스가 중개자 역할을 하여 각 관련 서비스를 개별적으로 호출하고 그 결과를 조합하여 응답한다.
이 방식의 주요 구현 형태는 다음과 같다.
구현 위치 | 설명 | 장점 | 단점 |
|---|---|---|---|
게이트웨이 계층에서 직접 여러 서비스 호출을 조정한다. | 클라이언트가 단일 엔드포인트로 요청 가능, 빠른 구현 가능 | 게이트웨이의 복잡도 증가, 비즈니스 로직 혼재 가능성 | |
전용 컴포지터 서비스 | 컴포지션 로직을 담당하는 별도의 서비스를 생성한다. | 관심사 분리, 컴포지션 로직의 재사용성 향상 | 운영할 서비스가 하나 더 늘어남, 지연 시간 추가 |
API 컴포지션은 CQRS 패턴의 쿼리 측면을 구현하는 일반적인 방법으로 사용된다. 각 서비스는 자신의 데이터에 대한 권위 있는 소스로 남아 있으므로, 데이터 일관성 모델은 각 서비스 내부의 트랜잭션 경계에 의해 관리된다. 컴포지터는 일반적으로 동기 통신 방식을 사용하여 순차적 또는 병렬로 호출을 수행한다.
이 패턴은 단순한 조회 시나리오에 효과적이지만, 여러 서비스에 걸친 복잡한 조인이나 집계 연산에는 한계가 있다. 각 서비스의 응답을 메모리에서 조합해야 하기 때문에 성능이 저하될 수 있으며, 하나의 하위 서비스 호출이 실패하면 전체 쿼리가 실패할 수 있다[4]. 따라서 사용 사례에 따라 명령 쿼리 책임 분리와 함께 이벤트 소싱을 활용한 구체화된 뷰를 생성하는 대안을 고려해야 한다.
6.2. 마이크로서비스 간 조인
6.2. 마이크로서비스 간 조인
마이크로서비스 아키텍처에서 각 서비스는 자체 데이터베이스를 소유하는 것이 원칙이다. 이는 서비스의 자율성을 보장하지만, 여러 서비스에 분산된 데이터를 연결하여 조회해야 하는 복잡한 비즈니스 로직을 구현할 때 어려움을 초래한다. 전통적인 모놀리식 애플리케이션에서 단일 데이터베이스 테이블 간에 수행하던 조인 연산은 마이크로서비스 환경에서는 직접적으로 수행할 수 없다.
이 문제를 해결하기 위한 주요 접근 방식은 API 컴포지션과 CQRS 패턴이다. API 컴포지션은 클라이언트나 전용 API 게이트웨이가 필요한 데이터를 보유한 각 마이크로서비스를 순차적으로 호출하고, 그 결과를 메모리에서 조합하여 최종 응답을 구성하는 방식이다. 이 방법은 구현이 비교적 간단하지만, 여러 서비스를 호출하는 과정에서 성능 지연이 발생할 수 있고, 네트워크 신뢰성에 영향을 받는다. 반면, CQRS 패턴은 명령과 조회의 책임을 분리하여, 조회를 위한 별도의 읽기 전용 데이터 저장소를 구축한다. 이 저장소는 여러 서비스의 데이터를 사전에 조인하여 물리화된 뷰 형태로 저장함으로써, 복잡한 조인 쿼리를 단일 저장소에 대한 간단한 조회로 변환한다.
적절한 전략 선택은 데이터의 실시간성 요구사항, 일관성 수준, 그리고 쿼리의 빈도와 복잡도에 따라 달라진다. 높은 실시간성이 요구되고 조인이 간단한 경우 API 컴포지션이 적합할 수 있다. 반면, 대량의 데이터를 집계하거나 복잡한 조인이 빈번하게 발생하는 비즈니스 인텔리전스 또는 보고서 생성 시나리오에서는 CQRS 기반의 물리화된 뷰가 더 효율적이다. 모든 경우에, 서비스 경계를 넘는 데이터 의존성을 최소화하는 것이 장기적인 유지보수성에 유리하다.
6.3. 분산 데이터 분석
6.3. 분산 데이터 분석
분산 데이터 분석은 마이크로서비스 아키텍처 환경에서 여러 서비스에 걸쳐 흩어져 있는 데이터를 통합하여 분석 인사이트를 도출하는 활동이다. 각 마이크로서비스는 자체 데이터 저장소를 소유하는 데이터베이스 퍼 서비스 패턴을 따르기 때문에, 전통적인 단일 데이터베이스를 대상으로 하는 분석 방식은 적용하기 어렵다. 이로 인해 분석을 위한 데이터 수집과 처리에 특화된 아키텍처 패턴과 도구가 필요해진다.
주요 접근 방식으로는 이벤트 기반 통신을 활용한 데이터 파이프라인 구축이 있다. 각 서비스는 중요한 상태 변경이나 비즈니스 이벤트를 발생시키고, 이러한 이벤트는 Apache Kafka나 Amazon Kinesis 같은 메시지 브로커를 통해 실시간으로 수집된다. 수집된 이벤트 스트림 데이터는 Apache Spark나 Flink 같은 스트림 처리 엔진을 통해 변환되고, 최종적으로 데이터 웨어하우스나 데이터 레이크에 적재되어 분석 쿼리에 사용된다. 이 방식은 데이터의 실시간성과 확장성을 보장한다.
또 다른 방식은 분석용 데이터 저장소에 주기적으로 데이터를 복제하는 것이다. 변경 데이터 캡처 기술을 사용하여 각 서비스 데이터베이스의 변경 사항만 추출하고, 중앙 분석 저장소에 동기화한다. 또는 각 서비스가 분석을 위해 특정 형식의 데이터 스냅샷을 생성하고, 이를 공유 위치에 배치하는 방법도 사용된다. 이 경우 분석 시스템은 원본 운영 시스템에 부하를 주지 않고 복제된 데이터를 자유롭게 조회하고 집계할 수 있다.
분산 데이터 분석의 설계 시에는 다음 사항을 고려해야 한다.
고려 사항 | 설명 |
|---|---|
데이터 일관성 | 분석용 데이터는 운영 데이터의 강한 일관성을 요구하지 않으며, 결국 일관성 모델이 일반적으로 적용된다. |
데이터 변환 | 서비스별로 상이한 데이터 모델과 형식을 통합된 분석 모델로 변환하는 ETL(추출, 변환, 적재) 또는 ELT(추출, 적재, 변환) 프로세스가 필요하다. |
저장소 선택 | 분석 쿼리의 특성에 따라 컬럼 기반 저장소(Amazon Redshift, Google BigQuery), 데이터 레이크(Apache Hadoop), 또는 OLAP 큐브를 선택한다. |
거버넌스 | 데이터 출처, 품질, 민감도, 사용 정책을 관리하는 데이터 거버넌스 체계가 필수적이다. |
7. 데이터 보안과 거버넌스
7. 데이터 보안과 거버넌스
데이터 보안과 거버넌스는 마이크로서비스 아키텍처에서 데이터가 여러 서비스에 분산되어 관리되기 때문에 특히 중요한 고려 사항이다. 각 서비스는 자체 데이터 저장소를 소유하지만, 데이터에 대한 접근 권한과 개인정보 처리, 그리고 모든 데이터 활동의 추적 가능성을 보장해야 한다. 이는 단일 모놀리식 시스템보다 훨씬 복잡한 과제를 제시한다.
데이터 접근 제어는 서비스 경계를 기준으로 세밀하게 정의되어야 한다. 각 마이크로서비스는 자신의 데이터에 대한 게이트키퍼 역할을 하며, 다른 서비스나 외부 클라이언트의 요청을 인증(Authentication)과 권한 부여(Authorization)를 통해 통제한다. 일반적으로 API 게이트웨이에서 초기 인증을 수행한 후, 개별 서비스 수준에서 세부적인 접근 정책을 적용한다. 예를 들어, 주문 서비스는 고객 서비스에 특정 고객 정보를 요청할 수 있지만, 외부 애플리케이션은 직접적인 데이터베이스 접근이 불가능하도록 해야 한다.
개인정보 보호는 GDPR이나 CCPA와 같은 규정 준수 요구사항과 밀접하게 연관된다. 민감한 개인 식별 정보(PII)는 특정 서비스에서만 관리하고, 필요 시 익명화 또는 가명화하여 다른 서비스로 전달하는 전략이 필요하다. 모든 데이터 흐름을 식별하고, 데이터가 어디에 저장되고 어떻게 사용되는지에 대한 명확한 매핑(데이터 흐름도)을 유지 관리하는 것이 필수적이다.
데이터 감사 및 추적은 분산 환경에서 문제 해결과 규정 준수를 위해 결정적으로 중요하다. 모든 데이터 생성, 수정, 삭제 이벤트는 서비스별로 로깅되어야 하며, 이러한 로그는 중앙 집중식 모니터링 시스템으로 전송되어 상관관계 분석이 가능해야 한다. 각 요청에 고유한 상관관계 ID를 부여하여 여러 서비스를 걸친 단일 비즈니스 트랜잭션의 전체 데이터 활동 경로를 추적할 수 있다. 이는 보안 침해 사고 조사나 내부 통제 감사에 핵심적인 자료가 된다.
주제 | 주요 고려사항 | 일반적인 구현 방식 |
|---|---|---|
데이터 접근 제어 | 서비스 간 신뢰, 세분화된 권한, 최소 권한 원칙 | |
개인정보 보호 | 규정 준수, 데이터 최소화, 저장 및 전송 중 암호화 | 데이터 마스킹, 토큰화, 엔드투엔드 암호화 |
데이터 감사 및 추적 | 분산 트레이싱, 불변성 로그, 포렌식 분석 | 구조화된 로깅, 분산 추적 시스템(예: Jaeger, Zipkin), CDC(Change Data Capture) |
7.1. 데이터 접근 제어
7.1. 데이터 접근 제어
각 마이크로서비스는 자체 데이터 저장소를 소유하므로, 데이터에 대한 접근 권한을 세밀하게 관리하는 것이 중요해진다. 데이터 접근 제어는 인증된 사용자나 서비스만이 특정 데이터에 접근할 수 있도록 보장하는 메커니즘을 포함한다. 일반적으로 API 게이트웨이 수준에서 초기 인증을 수행한 후, 개별 서비스가 자신의 도메인 데이터에 대한 최종 권한 부여를 담당한다.
주요 접근 방식으로는 역할 기반 접근 제어(RBAC)와 속성 기반 접근 제어(ABAC)가 있다. RBAC는 사용자의 역할(예: 관리자, 일반 사용자)에 따라 접근 권한을 부여하는 반면, ABAC는 사용자 속성, 리소스 속성, 환경 조건 등 다양한 속성을 결합하여 동적으로 권한을 결정한다. 마이크로서비스 환경에서는 서비스 간 호출 시 사용되는 서비스 계정이나 API 키에 대한 관리도 필수적이다.
데이터 접근 제어를 구현할 때는 다음 원칙을 고려해야 한다.
원칙 | 설명 |
|---|---|
최소 권한 원칙 | 작업 수행에 필요한 최소한의 권한만 부여한다. |
방어적 설계 | 서비스 경계를 넘는 모든 데이터 요청을 검증한다. |
중앙 집중식 정책 관리 | 접근 제어 정책을 중앙에서 정의하고 관리하여 일관성을 유지한다[5]. |
효과적인 데이터 접근 제어는 데이터 유출을 방지하고, 규정 준수 요구사항을 충족시키며, 시스템 전체의 보안 태세를 강화하는 데 기여한다. 이를 위해 OAuth 2.0, JWT(JSON Web Tokens) 같은 표준 프로토콜과 정책 결정점(PDP) 및 정책 실행점(PEP) 아키텍처 패턴이 널리 활용된다.
7.2. 개인정보 보호
7.2. 개인정보 보호
마이크로서비스 환경에서 개인정보 보호는 데이터가 여러 서비스에 분산 저장되고 처리되기 때문에 복잡한 과제를 제기한다. 각 서비스는 자체 데이터 저장소를 관리하며, 개인정보가 여러 경계를 가로지르며 흐를 수 있어 통제와 책임 소재가 모호해질 위험이 있다. 따라서 데이터 주권 원칙을 적용하여 정보 주체의 데이터가 어디에 저장되고 어떻게 사용되는지를 명확히 해야 한다.
핵심 접근법 중 하나는 데이터 최소화 원칙을 설계에 반영하는 것이다. 각 서비스는 오직 필요한 최소한의 개인식별정보만을 보유하고, 이를 위해 익명화 또는 가명화 기술을 활용할 수 있다. 예를 들어, 분석 서비스는 특정 개인을 식별할 수 없는 형태로만 데이터를 처리하도록 설계된다. 또한 데이터 흐름을 매핑하고, 개인정보 영향평가를 정기적으로 수행하여 새로운 서비스 도입이나 변경 시 잠재적 위험을 사전에 평가해야 한다.
기술적 구현 측면에서는 동의 관리를 위한 전용 서비스를 두거나, 중앙 집중식 정책 엔진을 활용하여 데이터 접근 규칙을 일관되게 적용하는 것이 일반적이다. 모든 데이터 접근 로그는 중앙에서 감사 추적이 가능하도록 기록되어야 하며, 암호화는 저장 데이터와 전송 중 데이터 모두에 적용되어야 한다. 특히 유럽 연합 일반 데이터 보호 규칙과 같은 규정을 준수하기 위해서는 데이터 삭제 요청이 모든 관련 서비스에 전파되어 완전히 처리되는 프로세스가 확립되어야 한다.
보호 조치 | 설명 | 관련 패턴/기술 |
|---|---|---|
데이터 분리 | 서비스별로 필요한 최소한의 PII만 보유 | |
접근 통제 | 역할 기반 또는 속성 기반 접근 제어 적용 | |
암호화 | 저장 및 전송 중 데이터 암호화 | |
감사 로깅 | 모든 개인정보 접근 이력 기록 및 모니터링 | |
데이터 생명주기 관리 | 생성, 저장, 사용, 파기의 전 과정 관리 | 보존 정책, 자동화된 삭제 프로세스 |
이러한 조치들은 단일 서비스가 아닌 아키텍처 전체 차원에서 통합되어 설계되고 운영되어야 효과를 발휘한다. 최종 목표는 규정 준수를 넘어서 정보 주체의 신뢰를 얻는 것이다.
7.3. 데이터 감사 및 추적
7.3. 데이터 감사 및 추적
데이터 감사는 마이크로서비스 아키텍처 환경에서 데이터의 생성, 접근, 수정, 삭제에 대한 모든 활동을 기록하고 모니터링하는 과정이다. 이는 규정 준수, 보안 사고 대응, 운영 문제 진단 및 비즈니스 분석을 위해 필수적이다. 분산된 서비스 각각이 독립적인 데이터 저장소를 관리하기 때문에, 중앙 집중식 시스템보다 감사 로그의 수집과 통합이 복잡해진다.
효과적인 감사를 위해서는 각 서비스가 표준화된 형식으로 감사 이벤트를 생성해야 한다. 일반적으로 감사 로그에는 타임스탬프, 사용자 또는 서비스 ID, 수행된 작업(예: CREATE, READ, UPDATE, DELETE), 영향을 받은 데이터 식별자, 작업 전후의 데이터 스냅샷(선택 사항) 및 출처 IP 주소와 같은 컨텍스트 정보가 포함된다. 이러한 이벤트는 종종 이벤트 기반 통신을 통해 중앙 집중식 로그 집계 시스템이나 전용 데이터 웨어하우스로 전송된다.
데이터 추적은 특정 데이터 항목이나 트랜잭션이 여러 서비스를 거치는 흐름을 따라가는 능력을 의미한다. 이를 위해 분산 트레이싱 시스템이 사용된다. 각 외부 요청에는 고유한 추적 ID가 부여되고, 이 ID는 관련된 모든 마이크로서비스 호출을 통해 전파된다. 이렇게 수집된 추적 데이터는 서비스 간의 의존 관계를 시각화하고, 성능 병목 현상을 식별하며, 실패한 트랜잭션의 경로를 재구성하는 데 활용된다.
감사(Audit) 목적 | 주요 구현 방법 | 관련 패턴/기술 |
|---|---|---|
규정 준수 및 법적 요구사항 충족 | 서비스별 감사 로그 생성 및 장기 보관 | 이벤트 소싱, 로그 집계기(Log Aggregator) |
보안 모니터링 및 이상 탐지 | 실시간 로그 스트림 분석 및 알림 | |
운영 문제 해결 및 디버깅 | 요청의 엔드투엔드 흐름 추적 | 분산 트레이싱(예: OpenTelemetry, Jaeger) |
비즈니스 활동 분석 | 감사 이벤트를 분석 플랫폼으로 전송 | CQRS 패턴, 데이터 파이프라인 |
데이터 감사 및 추적 체계를 설계할 때는 데이터 개인정보 보호 규정을 준수해야 한다. 로그에 기록되는 개인 식별 정보는 최소화하거나 익명화/가명화 처리해야 한다. 또한 감사 로그 자체에 대한 무단 접근과 변조를 방지하기 위해 강력한 접근 제어와 무결성 검증 메커니즘을 마련하는 것이 중요하다.
8. 도전 과제와 해결 방안
8. 도전 과제와 해결 방안
마이크로서비스 아키텍처에서 데이터는 각 서비스의 경계 내에 분산되어 캡슐화된다. 이는 서비스의 독립적인 개발과 배포를 가능하게 하지만, 데이터 중복, 스키마 변경, 그리고 시스템 전체에 걸친 데이터 이동과 같은 새로운 도전 과제를 야기한다. 이러한 문제들을 효과적으로 관리하지 않으면 데이터 무결성이 훼손되거나 시스템 복잡도가 급격히 증가할 수 있다.
데이터 중복은 의도적이거나 비즈니스 요구에 따른 경우가 많지만, 관리되지 않을 경우 일관성 문제를 초래한다. 일반적인 해결 방안은 단일 진실 공급원을 명확히 정의하고, 이벤트 기반 통신을 통해 상태 변경을 전파하는 것이다. 예를 들어, 고객 정보가 '고객 서비스'에 진실 공급원이 있다면, '주문 서비스'가 이를 참조용으로 복제할 수 있다. 이때 도메인 이벤트를 발행하여 복제본을 비동기적으로 갱신함으로써 궁극적인 일관성을 달성한다. 중복 데이터의 수명 주기와 동기화 전략을 문서화하는 것이 중요하다.
스키마 진화는 서비스가 독립적으로 진화해야 한다는 원칙 하에 특히 까다로운 문제이다. 한 서비스의 데이터 구조 변경이 다른 종속 서비스의 장애를 유발하지 않도록 해야 한다. 이를 위한 핵심 전략은 하위 호환성을 유지하는 변경을 수행하고, 버전 관리를 명확히 하는 것이다. API나 이벤트 스키마에 새로운 필드를 선택적으로 추가하거나, 오래된 필드를 사용 중단 표시하는 방식이 일반적이다. 스키마 레지스트리를 도입하여 이벤트나 메시지의 스키마 호환성을 중앙에서 검증하고 관리할 수도 있다.
데이터 마이그레이션은 단일 데이터베이스를 대상으로 하는 모놀리식 접근법과 근본적으로 다르다. 모든 서비스의 데이터를 정지 없이 이동시키는 것은 불가능에 가깝다. 따라서 점진적이고 협력적인 접근이 필요하다. 일반적으로 이중 쓰기 패턴을 사용한다. 즉, 일정 기간 동안 데이터를 새 위치와 기존 위치에 동시에 기록하면서, 새 시스템이 정상적으로 작동하는지 검증한다. 검증이 완료되면 트래픽을 점진적으로 새 시스템으로 전환하고, 최종적으로 기존 데이터 소스를 사용 중단한다. 각 서비스 팀 간의 긴밀한 협의와 철저한 롤백 계획이 필수적이다.
8.1. 데이터 중복 관리
8.1. 데이터 중복 관리
마이크로서비스 아키텍처에서 각 서비스는 자체 데이터를 소유하고 독립적으로 관리한다. 이 원칙에 따라, 동일하거나 유사한 데이터가 여러 서비스에 걸쳐 중복 저장되는 상황이 빈번하게 발생한다. 이러한 데이터 중복은 단일 데이터베이스를 사용하는 모놀리식 아키텍처와는 근본적으로 다른 접근 방식을 요구한다.
데이터 중복은 성능 향상과 서비스 간 결합도 감소라는 장점을 제공하지만, 일관성 유지라는 주요 도전 과제를 동반한다. 중복된 데이터 간의 불일치는 시스템의 신뢰성을 해칠 수 있다. 이를 관리하기 위한 일반적인 전략은 데이터의 주인(도메인 주도 설계의 애그리게이트 루트 개념과 유사)을 명확히 정의하고, 모든 데이터 변경은 오직 해당 주인 서비스를 통해서만 이루어지도록 하는 것이다. 다른 서비스는 필요한 데이터를 복제본 형태로 보유하되, 주인 서비스가 발행하는 도메인 이벤트를 구독하여 자신의 복제본을 비동기적으로 갱신한다.
데이터 중복의 수준과 관리 방식은 데이터의 특성에 따라 달라져야 한다. 아래 표는 일반적인 데이터 유형별 중복 관리 접근법을 보여준다.
데이터 유형 | 중복 허용 수준 | 일관성 요구사항 | 일반적 관리 방식 |
|---|---|---|---|
참조 데이터 (국가 코드, 제품 카테고리) | 높음 | 최종적 일관성[7] | 이벤트 기반 비동기 복제 |
거래 데이터 (주문, 계좌 이체) | 낮음 | 강한 일관성[8] | 주인 서비스에서 단일 소스로 관리, SAGA 패턴 활용 |
파생 데이터 (집계 리포트, 대시보드 지표) | 높음 | 최종적 일관성 | 주기적 배치 작업 또는 이벤트 소싱을 통한 스트림 처리 |
데이터 중복을 효과적으로 관리하려면 명확한 데이터 소유권 정책, 신뢰할 수 있는 이벤트 버스, 그리고 데이터 불일치를 탐지하고 수정할 수 있는 모니터링 및 복구 메커니즘이 함께 구축되어야 한다. 또한, 스키마 진화가 발생할 때 중복된 모든 복제본에 대한 변경 사항의 전파도 고려해야 한다.
8.2. 스키마 진화
8.2. 스키마 진화
마이크로서비스 아키텍처에서 각 서비스는 독립적으로 배포되고 진화한다. 이는 각 서비스가 소유한 데이터의 스키마도 독립적으로 변경될 수 있음을 의미한다. 스키마 진화는 서비스의 기능 변경이나 성능 개선을 위해 데이터 구조를 수정하는 과정이며, 이를 관리하지 않으면 서비스 간 통신 장애나 데이터 불일치가 발생할 수 있다.
스키마 변경은 하위 호환성을 유지하는 방식으로 접근하는 것이 일반적이다. 예를 들어, 새로운 필드를 추가하는 것은 기존 클라이언트에 영향을 주지 않으므로 비교적 안전하다. 반면, 필드명 변경, 데이터 타입 변경, 필드 삭제 등은 기존 데이터를 처리하는 다른 서비스나 클라이언트를 중단시킬 위험이 크다. 이러한 변경을 위해서는 일반적으로 이전 스키마와 새 스키마를 병행 지원하는 전략을 사용한다. 서비스는 일정 기간 동안 두 버전의 API를 모두 제공하거나, 데이터 변환 계층을 도입하여 내부적으로 새 스키마를 사용하되 외부에는 기존 형식으로 응답할 수 있다.
효과적인 스키마 진화를 위한 구체적인 전략과 도구는 다음과 같다.
전략/도구 | 설명 | 주요 고려사항 |
|---|---|---|
확장 가능한 데이터 형식 | 스키마 ID 관리, 호환성 규칙(forward/backward compatibility) 이해 필요 | |
점진적 롤아웃 | 새 스키마 버전을 사용하는 서비스 인스턴스를 점차적으로 증가시키며 배포 | 카나리아 배포, 기능 플래그와 결합하여 위험 관리 |
스키마 레지스트리 | 중앙 저장소에서 스키마 버전을 관리하고 호환성을 검증[11] | 생산자와 소비자 간의 의존성 및 변경 통제 가능 |
버저닝 전략 | API 엔드포인트(예: | 지나친 버전 축적 방지와 구버전 서비스의 적시 폐기 계획 수립 |
최종적으로는 오래된 스키마 버전과 클라이언트를 폐기하는 정리 단계가 필요하다. 서비스 소유자는 변경 로드맵을 명시하고, 충분한 유예 기간을 제공한 후 사용 중단 정책을 통해 구버전 지원을 중단한다. 이 과정은 서비스 간 느슨한 결합을 유지하면서도 지속적인 혁신을 가능하게 하는 핵심 활동이다.
8.3. 데이터 마이그레이션
8.3. 데이터 마이그레이션
마이크로서비스 환경에서 데이터 마이그레이션은 단일 모놀리식 시스템의 마이그레이션보다 복잡한 문제를 제기한다. 각 서비스가 독립적인 데이터 저장소를 소유하는 데이터베이스 퍼 서비스 패턴을 따를 경우, 마이그레이션 작업은 서비스 단위로 분산되어 계획되고 실행되어야 한다. 이는 마이그레이션의 범위와 영향을 국지화할 수 있는 장점이 있지만, 여러 서비스에 걸친 데이터 의존성을 가진 경우 조정이 어려워질 수 있다.
주요 접근 방식은 크게 빅뱅(Big Bang) 방식과 점진적(Strangler Fig) 방식으로 나눌 수 있다. 빅뱅 방식은 모든 데이터를 한 번에 새로운 스키마나 저장소로 이동시키는 방법이다. 이는 계획과 실행이 비교적 단순하지만, 다운타임이 발생할 수 있고 롤백이 어려운 위험이 있다. 반면, 점진적 방식은 스트랭글러 피그 패턴을 적용하여 기존 시스템과 새 시스템을 병행 운영하면서 점차 트래픽과 데이터를 이전한다. 이 방식은 다운타임을 최소화하고 안전하게 롤백할 수 있는 장점이 있으나, 데이터 일관성을 유지하기 위한 임시 변통책(예: 이중 기록)이 필요해 복잡성이 증가한다.
마이그레이션을 성공적으로 수행하기 위한 핵심 고려사항은 다음과 같다.
고려사항 | 설명 |
|---|---|
데이터 일관성 보장 | 마이그레이션 중 ACID 트랜잭션이 깨질 수 있으므로, 이벤트 소싱이나 SAGA 패턴을 활용한 보상 메커니즘이 필요하다. |
다운타임 최소화 | 점진적 마이그레이션, 블루-그린 배포, 카나리 릴리스 등을 통해 서비스 중단을 방지한다. |
데이터 검증 | 마이그레이션 후 데이터의 정확성과 완전성을 검증하는 자동화된 스크립트가 필수적이다. |
롤백 전략 | 문제 발생 시 이전 상태로 안전하게 복귀할 수 있는 명확한 계획과 도구를 마련해야 한다. |
팀 간 협업 | 데이터 소유권이 분산되어 있으므로, 관련된 모든 서비스 팀과의 긴밀한 조율이 중요하다. |
마지막으로, 스키마 진화는 지속적인 과정이므로, 마이그레이션을 일회성 작업이 아닌 서비스 수명 주기의 일부로 관리하는 접근이 필요하다. 데이터 변환 로직을 코드로 정의하고 버전 관리하며, 변경 시 데이터 계약 관리를 통해 영향을 받는 다른 서비스와의 호환성을 유지해야 한다.
