Loading...
Skip to Content

마이크로서비스에서의 효과적인 데이터 관리: 데이터 지역성, 공통 데이터 복제, 원격 호출

  도입: 모놀리식 아키텍처의 문제 - "갓테이블"

모놀리식 아키텍처에서는 종종 "Customer"라는 단일 테이블에 고객과 관련된 모든 데이터를 통합해 관리합니다. 이 테이블은 다양한 도메인(예: 영업, 마케팅, 예약)의 데이터를 포함하고 있어 "갓테이블(God Table)"로 불립니다.

 갓테이블의 예

CustomerId Name Gender Social Security Number Purchase History Campaigns Current Location Discount Rate
12345 John Doe Male 123-45-6789 ["Order1", "Order2"] ["Campaign1"] Seoul, South Korea 10%

갓테이블 구조의 문제점은 다음과 같습니다.

  1. 도메인 응집도 부족: 영업, 마케팅, 예약 등 서로 다른 문제 영역이 하나의 테이블에 섞여 있습니다.
  2. 변경 영향 확대: 특정 도메인의 데이터가 변경되면 다른 도메인에도 영향을 미칩니다.
  3. 확장성 문제: 데이터를 중앙에서 관리하다 보니 성능 저하와 병목 현상이 발생합니다.

 마이크로서비스 아키텍처로의 전환

마이크로서비스 아키텍처는 이러한 문제를 해결하기 위해 데이터를 각 도메인의 **Bounded Context(BC)**별로 분리합니다. 이를 위해 세 가지 원칙인 데이터 지역성, 공통 데이터 복제, 원격 호출을 적용합니다.

 원칙 1: 데이터 지역성(Data Locality)

데이터 지역성은 특정 서비스의 경계(Context) 내에서 자주 접근하거나 수정되는 데이터를 로컬에서 관리하는 원칙입니다. 각 Bounded Context는 자신의 문제 영역과 관련된 데이터만 포함해야 하며, 다른 도메인의 데이터는 ID Value Object로 참조합니다.

갓테이블을 Bounded Context로 나누기

아래는 모놀리식 아키텍처의 "Customer" 갓테이블을 Sales, Marketing, Booking이라는 세 가지 Bounded Context로 나눈 예시입니다.

1. Sales Context (영업 BC)

영업 BC는 고객의 구매와 관련된 데이터를 관리하며, 고객을 참조하기 위해 CustomerId Value Object를 사용합니다.

java class Lead { private CustomerId customerId; // 고객 식별자 private List<String> purchaseHistory; // 구매 이력 private double discountRate; // 할인율 public Lead(CustomerId customerId) { this.customerId = customerId; } // Getters and setters }

CustomerId Value Object

java class CustomerId { private final String id; public CustomerId(String id) { if (id == null || id.isBlank()) { throw new IllegalArgumentException("Customer ID cannot be null or blank"); } this.id = id; } public String getId() { return id; } }

2. Marketing Context (마케팅 BC)

마케팅 BC는 고객의 캠페인 참여와 선호 연락 채널을 관리합니다.

java class Subscriber { private CustomerId customerId; // 고객 식별자 private List<String> campaignParticipations; // 캠페인 참여 이력 private String preferredChannel; // 선호 연락 채널 (SMS, Email 등) public Subscriber(CustomerId customerId) { this.customerId = customerId; } // Getters and setters }

3. Booking Context (예약 BC)

예약 BC는 고객의 예약 데이터를 관리하며, 고객의 현재 위치와 예약 내역이 포함됩니다.

java class Passenger { private CustomerId customerId; // 고객 식별자 private List<String> bookingHistory; // 예약 내역 private String currentLocation; // 현재 위치 public Passenger(CustomerId customerId) { this.customerId = customerId; } // Getters and setters }

 원칙 2: 공통 데이터 복제(Common Data Duplication)

각 Aggregate는 CustomerId를 통해 고객을 참조하지만, 고객의 불변 데이터(예: 이름, 성별, 주민등록번호)는 각 서비스에서 복제하여 사용합니다. 이는 동기화 비용을 줄이고 성능을 향상시킵니다.

예시: 공통 데이터 복제

CustomerId VO 객체

CustomerId VO 객체는 이벤트를 통해 전달받은 고객의 불변 데이터를 포함하며, 각 Bounded Context에서 이를 복제해 사용합니다.

java class CustomerId { private final String id; // 고객 ID private final String name; // 고객 이름 private final String gender; // 성별 private final String socialSecurityNumber; // 주민등록번호 public CustomerId(String id, String name, String gender, String socialSecurityNumber) { if (id == null || id.isBlank()) { throw new IllegalArgumentException("Customer ID cannot be null or blank"); } this.id = id; this.name = name; this.gender = gender; this.socialSecurityNumber = socialSecurityNumber; } // Getters and equals/hashCode methods }

 원칙 3: 원격 호출(Remote Invocation – 마지막 선택)

원격 호출은 네트워크 비용이 높고 장애에 취약하기 때문에 최후의 수단으로 사용해야 합니다. 데이터가 자주 변경되거나 로컬 관리가 불가능한 경우, CustomerId와 같은 Value Object에서 플라이웨이트 패턴을 활용하여 호출 시점에만 데이터를 가져오도록 설계할 수 있습니다.

CustomerId에서 플라이웨이트 패턴과 캐시 구현

아래는 CustomerId 클래스에서 getCurrentLocation 메서드를 호출할 때 원격 데이터를 가져오고, 이를 캐싱 및 만료 처리하는 예시입니다.

java import java.time.LocalDateTime; class CustomerId { private final String id; private String currentLocation; // 현재 위치 (캐시) private LocalDateTime lastFetched; // 마지막 데이터 가져온 시간 private static final long CACHE_EXPIRY_MINUTES = 10; // 캐시 유효 시간 (10분) private final CustomerService customerService; public CustomerId(String id, CustomerService customerService) { if (id == null || id.isBlank()) { throw new IllegalArgumentException("Customer ID cannot be null or blank"); } this.id = id; this.customerService = customerService; } public String getCurrentLocation() { if (currentLocation == null || isCacheExpired()) { currentLocation = customerService.getCustomerLocation(id); lastFetched = LocalDateTime.now(); } return currentLocation; } private boolean isCacheExpired() { return lastFetched == null || LocalDateTime.now().isAfter(lastFetched.plusMinutes(CACHE_EXPIRY_MINUTES)); } }

CustomerService 구현

CustomerService는 원격 호출을 통해 고객의 현재 위치를 가져오는 역할을 합니다. 이 예제에서는 원격 호출의 시뮬레이션을 위해 간단한 메서드를 제공합니다.

java class CustomerService { public String getCustomerLocation(String customerId) { System.out.println("Fetching location for Customer ID: " + customerId); return "Seoul, South Korea"; } }

 결론

마이크로서비스 아키텍처에서 데이터를 효과적으로 관리하려면 다음의 원칙을 따르는 것이 중요합니다.

  1. 데이터 지역성: 각 도메인이 자신의 문제 영역에 맞는 데이터만 관리하도록 분리합니다.
  2. 공통 데이터 복제: 변경 가능성이 낮은 데이터를 각 서비스에서 복제하여 사용합니다.
  3. 원격 호출(마지막 선택): 데이터가 자주 변경되거나 로컬 관리가 불가능한 경우에만 사용하며, 캐시와 플라이웨이트 패턴을 활용해 비용을 줄입니다.

이러한 접근법은 갓테이블 문제를 해결하고, 데이터 관리의 효율성과 안정성을 동시에 확보할 수 있습니다.