[최적화] 불필요한 JPA 설정 제거와 읽기 전용 작업 성능 향상

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

MSA&아키텍처

목록 보기
23/45
post-thumbnail

문제 상황: 대규모 마이크로서비스 환경에서의 성능 저하

최근 대규모 마이크로서비스 환경에서 운영 중인 주문 관리 시스템의 주문 조회 API에서 심각한 성능 저하 현상이 발생했습니다.

해당 API는 주문 정보를 조회하는 중요한 엔드포인트로, 평균 응답 시간은 2초 이상을 기록하며 전체 시스템 성능의 병목 지점으로 작용했습니다. 문제를 해결하기 위한 분석이 필요했습니다.

분석한 주요 원인

1. 불필요한 JPA 설정이 로드됨

모든 마이크로서비스에서 JPA 설정이 불필요하게 활성화되어 데이터베이스와의 연결 비용이 과도하게 발생하고 있었습니다.

특히 읽기 전용 작업에서도 JPA 설정을 비활성화할 수 있는 상황이었음에도 불구하고, JPA 설정이 계속 활성화되면서 성능 저하를 일으켰습니다.

2. 읽기 전용 작업에서 불필요한 도메인 모델 변환 발생

도메인 모델에서 직접 데이터를 조회하고 변환하는 과정에서 불필요한 엔티티 변환이 이루어졌습니다.

읽기 전용 작업에서는 변환 비용을 줄이고, 필요한 데이터만 조회할 수 있는 방법이 필요했습니다.

3. 테스트 환경 구성의 어려움

테스트 환경에서 독립적인 테스트 환경 구축이 어려워 개발자의 생산성이 저하되었고, 문제를 신속하게 파악하기 어려웠습니다.

해결 방안: 최적화된 JPA 설정과 읽기 전용 작업 구현

해결책은 불필요한 JPA 설정을 선택적으로 활성화하고, 읽기 전용 작업에서 불필요한 엔티티 변환을 제거하는 것이었습니다.

이를 위해 다음과 같은 방안을 적용했습니다.

1. @Profile과 @ConditionalOnProperty를 활용한 선택적 JPA 활성화

JPA 설정이 필요 없는 환경에서는 설정을 비활성화하고, 읽기 전용 모드에서는 JPA를 활성화하여 불필요한 리소스 소모를 줄였습니다.

2. DTO 직접 조회로 불필요한 엔티티 변환 제거

읽기 전용 API에서는 DTO 직접 조회 방식을 사용하여 엔티티 변환을 피하고, 필요한 데이터만을 빠르게 조회할 수 있도록 최적화했습니다.

3. 읽기 전용 Repository 구현

기존 Repository를 최적화하여, 읽기 전용 작업에 맞는 별도의 구현을 추가했습니다.

최적화된 코드 예시:

@Configuration
@Profile("persistence")
public class OptimizedJpaConfiguration {

    @Bean
    @ConditionalOnProperty(name = "spring.jpa.enabled", havingValue = "true")
    public OrderReadRepository orderReadRepository() {
        return new OptimizedOrderReadRepository();
    }

    @Bean
    public QueryOptimizer queryOptimizer() {
        return new QueryOptimizer(1000, true);  // 캐시 크기 및 읽기 전용 모드 설정
    }
}

@Repository
public class OptimizedOrderReadRepository {

    @Cacheable(cacheNames = "orders")
    public OrderReadDTO findOrderById(Long id) {
        return entityManager
            .createQuery("SELECT NEW com.example.OrderReadDTO(o.id, o.status) FROM Order o WHERE o.id = :id", OrderReadDTO.class)
            .setParameter("id", id)
            .setHint(QueryHints.HINT_READONLY, true)
            .getSingleResult();
    }
}

최적화된 코드 설명

  • @Profile("persistence"): 해당 환경에서만 JPA 설정을 활성화합니다.
  • @ConditionalOnProperty: 프로퍼티 값에 따라 JPA 활성화를 조건부로 설정합니다.
  • @Cacheable: 자주 조회되는 데이터를 캐싱하여 데이터베이스 접근 횟수를 줄입니다.
  • QueryHints.HINT_READONLY: 읽기 전용 작업에 최적화된 설정을 추가하여 성능을 향상시킵니다.

성능 측정 결과: 성능 최적화 전후 비교

개선 전

  • 평균 응답 시간: 2.1초
  • 초당 트랜잭션 처리량 (TPS): 150 TPS
  • 메모리 사용량: 2.4GB

개선 후

  • 평균 응답 시간: 0.3초 (85.7% 감소)
  • 초당 트랜잭션 처리량 (TPS): 800 TPS (433% 증가)
  • 메모리 사용량: 1.1GB (54% 감소)

성능 개선 시각화

위 그래프는 성능 최적화 전후의 응답 시간 및 TPS 변화를 보여줍니다.

실제 적용 사례: 대형 이커머스 시스템에 적용한 성과

이번 최적화 작업은 특정 대형 이커머스 프로젝트에 적용되었습니다.

이 프로젝트는 블랙프라이데이 세일 기간 동안 트래픽이 급증했으나, 성능 최적화 후 시스템은 안정적으로 운영되었습니다.

  • 주문 조회 API의 성능이 대폭 개선되었습니다.
  • 세일 기간 동안에도 트래픽 증가에 대응하며 응답 시간을 크게 개선했습니다.
  • 시스템 자원 사용률이 최적화되어 인프라 비용 절감 효과를 거두었습니다.

결론: 최적화 전략의 중요성

성능 최적화는 단순히 아키텍처 원칙을 따르는 것이 아니라, 실제 사용 케이스에 맞는 전략을 선택하는 것이 중요합니다.

읽기 전용 작업에서는 불필요한 객체 변환을 제거하고, JPA 설정을 최적화함으로써 큰 성능 향상을 얻을 수 있었습니다.

이와 같은 최적화 작업은 성능 개선뿐만 아니라, 유지보수성테스트 용이성을 함께 향상시킬 수 있었습니다.

이러한 접근 방식은 대규모 마이크로서비스 환경에서의 시스템 안정성 및 효율성 향상에 크게 기여할 수 있습니다.

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

0개의 댓글