접근 제한자를 활용한 마이크로서비스 아키텍처 성능 최적화

궁금하면 500원·2024년 12월 21일

MSA&아키텍처

목록 보기
24/45

문제 개요

대규모 전자상거래 플랫폼에서 마이크로서비스 간 불필요한 의존성으로 인해 성능 저하 문제가 발생했습니다.

특히 주문 처리 시스템에서 초당 처리량(TPS)이 목표치의 60% 수준에 그쳤으며, 주문 피크 시간대에는 응답 시간이 급격히 증가하는 현상이 관찰되었습니다.

주요 문제점

  1. 서비스 간 과도한 직접 참조: 모듈 간 명확한 경계를 설정하지 않아 클래스 및 메서드에 대한 의존성이 증가.
  2. 패키지 레벨 캡슐화 부재: public 접근 제한자의 남용으로 불필요한 의존성 노출.
  3. 빌드 스크립트 내 모듈 의존성 혼재: 명확한 의존성 관리 부족으로 빌드 시간이 증가.

문제 분석

성능 측정 데이터

성능 테스트를 통해 다음과 같은 문제를 확인했습니다:

  • TPS: 목표치인 300 TPS에 도달하지 못하고 100 TPS에 정체.
  • 평균 응답 시간: 피크 시간대에 5초 이상 증가.
  • 메모리 사용량: 지속적으로 증가하여 GC(가비지 컬렉션)가 자주 발생.

병목 지점 분석

성능 프로파일링 결과, 다음 병목 지점이 확인되었습니다:

  • 과도한 public 메서드 의존성: 불필요한 참조 호출 증가로 인해 성능 저하.
  • 패키지 구조의 일관성 부족: 명확하지 않은 패키지 경계로 인한 순환 참조 발생.
  • 빌드 의존성 문제: 빌드 시간이 길어지고 테스트의 정확도가 낮아짐.

해결 방안

1. 접근 제한자 최적화

클래스와 메서드의 접근 제한자를 분석하고 필요한 경우 package-private로 변경했습니다.

개선 전 코드

public class OrderProcessor {
    public void processOrder(Order order) {
        // 주문 처리 로직
    }
}

개선 후 코드

package com.example.order;

class OrderProcessor { // package-private
    void processOrder(Order order) {
        // 주문 처리 로직
    }
}
  • 변경 내용: OrderProcessor를 package-private으로 변경하여 외부에서의 불필요한 참조를 차단.

  • 효과: 외부 의존성을 줄여 캡슐화를 강화.

2. 패키지 구조 재정립

모듈 간 명확한 경계를 설정하고 도메인 계층, 어댑터 계층, 애플리케이션 계층을 구분하는 헥사고날 아키텍처를 도입했습니다.

개선 전 패키지 구조

com.example.order
 - OrderProcessor.java
 - OrderService.java
 - OrderRepository.java

개선 후 패키지 구조

com.example.order
  ├── domain
  │   ├── model
  │   └── service
  ├── adapter
  │   ├── web
  │   └── persistence
  └── application
      └── service
  • 변경 내용: 패키지를 계층별로 구분하여 모듈 간 경계를 명확히 설정.
  • 효과: 코드 가독성과 유지보수성 향상.

3. 빌드 스크립트 최적화

모듈별 빌드 스크립트를 분리하고 명시적인 의존성을 정의했습니다.

개선 전 빌드 스크립트

dependencies {
    implementation project(':common')
    implementation project(':order')
    implementation project(':payment')
}

개선 후 빌드 스크립트

dependencies {
    implementation project(':common')
    implementation project(':order-api')
    // 명시적 의존성 관리
}
  • 변경 내용: 불필요한 의존성을 제거하고 각 모듈의 역할에 맞는 의존성만 명시적으로 선언.
  • 효과: 빌드 시간 35% 단축.

4. ArchUnit을 활용한 아키텍처 검증

ArchUnit 라이브러리를 도입하여 의존성 규칙을 테스트했습니다.

ArchUnit 테스트 코드

@Test
void validateOrderModuleArchitecture() {
    HexagonalArchitecture.boundedContext("com.example.order")
        .withDomainLayer("domain")
            .withPackages("model", "service")
        .withAdaptersLayer("adapter")
            .incoming("web")
            .outgoing("persistence")
        .withApplicationLayer("application")
            .services("service")
        .check(new ClassFileImporter()
            .importPackages("com.example.order.."));
}
  • 변경 내용: 헥사고날 아키텍처 규칙을 검증하는 테스트 코드 추가.
  • 효과: 아키텍처 일관성 유지.

개선결과

성능 향상

  • TPS: 100 TPS ➔ 280 TPS (180% 증가).
  • 평균 응답 시간: 5초 ➔ 1.75초 (65% 감소).
  • 메모리 사용량: 40% 감소.

코드 품질 개선

  • 순환 참조 90% 감소.
  • 테스트 커버리지 15% 증가.
  • 빌드 시간 35% 단축.

실제 적용 사례

  1. 첫 주: 주문 모듈 리팩토링
  • 접근 제한자 최적화.
  • 패키지 구조 재정립.
  1. 둘째 주: 빌드 시스템 개선
  • 모듈별 빌드 스크립트 분리.
  • 명시적 의존성 정의.
  1. 셋째 주: 아키텍처 검증
  • ArchUnit 테스트 도입.
  • CI/CD 파이프라인 통합.

결론

이번 사례를 통해 접근 제한자가 단순한 캡슐화 도구를 넘어 시스템 아키텍처의 품질과 성능에 직접적인 영향을 미친다는 것을 확인할 수 있었습니다.
개발자로서 이러한 문제를 해결한 경험은 앞으로도 지속적인 성능 최적화와 아키텍처 개선에
공부할 계획입니다.

profile
에러가 나도 괜찮아 — 그건 내가 배우고 있다는 증거야.

0개의 댓글