실패를 통해 배운 아키텍처 개선 배움

프로젝트 개요 및 MSA 하게된 이유

1. 프로젝트 소개: 헬스케어 서비스의 모놀리식 전환

이 프로젝트는 기존의 헬스케어 모놀리식 아키텍처Microservice Architecture (MSA)로 전환하고 구축하는 과정을 담고 있습니다.
인증, 사용자 관리, 헬스 데이터, 커뮤니티 등 명확히 분리되는 도메인을 기반으로 안정적인 분산 시스템을 구축하는 것이 목표였습니다.

2. 왜 MSA를 선택했는가?

MSA를 선택한 주요 이유는 다음과 같습니다.

  • 도메인 분리 및 명확한 책임:
    • 서비스: 인증 (service.auth), 사용자 관리 (service.usermanagement), 헬스케어 데이터 (service.healthcare), 커뮤니티 (service.comm).
    • 각 서비스가 독립적인 비즈니스 로직을 담당하여 단일 책임 원칙 (SRP)을 준수했습니다.
  • 독립적인 확장성 (Scalability):
    • 특히 헬스케어 데이터 처리 서비스는 트래픽이 집중될 가능성이 높아, 이 서비스만 독립적으로 스케일링할 수 있도록 설계했습니다.
  • 기술 스택의 유연성:
    • 각 서비스의 특성에 맞는 최적의 기술 스택 (Polyglot Persistence)을 자유롭게 선택할 수 있는 기반을 마련했습니다.

실수와 개선을 통한 MSA 핵심 컴포넌트 구축 경험

MSA를 처음 구축하면서 발생했던 '실수 (Mistake)'와 이를 해결하기 위한 '개선 (Improvement)' 과정을 중점적으로 다루며, 학습 과정을 강조했습니다.

1. 보안 설정: 하드코딩된 키 \rightarrow 환경변수 관리로 전환

초기 실수: 코드에 박제된 민감 정보

AES256 암호화 키, JWT 시크릿 키, 데이터베이스 접속 정보 등을 코드나 설정 파일에 하드코딩하여 관리했습니다. 이는 보안상 매우 취약하며 환경별 분리가 불가능했습니다.

// AES256Util.java - 하드코딩된 키 (매우 위험!)
private static final byte[] KEY = { /* ... 32바이트 하드코딩 ... */ };

개선: 외부 환경변수 주입 (Externalized Configuration)

Spring Boot의 Placeholders 기능을 활용하여 민감 정보를 외부 환경변수로 분리하고, 환경별 (dev/prod) 설정을 명확히 분리했습니다.

# application-prod.yml - 환경변수 사용 및 기본값 설정
token:
  secret: ${JWT_SECRET:fallback-key-for-prod} 

spring:
  datasource:
    url: jdbc:postgresql://${DB_HOST:localhost}/${DB_NAME:healthcare_auth}
    username: ${DB_USERNAME:user}

배운 점: 민감 정보는 절대 코드에 포함해서는 안 됩니다. Docker나 Kubernetes 환경에서 Secrets 또는 ConfigMap으로 주입하여 관리하는 것이 필수입니다.


2. 서비스 통신: 직접 URL 참조 \rightarrow Eureka Service Discovery 도입

초기 실수: Gateway의 서비스 URL 하드코딩

API Gateway가 백엔드 서비스의 URL (IP 및 Port)을 직접 알고 하드코딩된 설정으로 통신했습니다.
이는 서비스의 위치가 변경되거나 인스턴스가 늘어날 때마다 수동으로 설정을 변경해야 하는 치명적인 문제점을 야기했습니다.

# web.healthcare/application.yml - 하드코딩된 서비스 URL
gateway:
  auth:
    uri: http://localhost:8082
  healthcare:
    uri: http://localhost:8081 # ... 수동 관리의 시작

개선: Eureka Service Discovery와 로드밸런싱

Spring Cloud Netflix Eureka를 도입하여 서비스의 자동 등록/발견 체계를 구축했습니다.
Gateway는 서비스의 이름으로만 통신하며, Ribbon (Client-Side Load Balancer)을 통해 자동으로 인스턴스에 부하를 분산시킬 수 있게 되었습니다.

# api.gateway/application.yml - Eureka를 통한 서비스 발견
spring:
  cloud:
    gateway:
      routes:
        - id: healthcare
          uri: lb://service.healthcare # 서비스 이름(lb://)으로 로드밸런싱

배운 점: MSA에서 서비스 간의 유연한 연결은 핵심입니다. Service Discovery는 동적인 환경에서 필수적이며, 로드밸런싱헬스체크를 자동화하여 운영 복잡도를 낮출 수 있습니다.


3. 품질 보증: 테스트 코드 부재 \rightarrow Mockito 기반 단위 테스트 도입

초기 실수: 테스트 없는 불안정한 코드

대부분의 서비스에 테스트 코드가 없어 리팩토링 시 버그 발생 위험이 높았고, 배포 후에도 불안정했습니다.

개선: 체계적인 단위 테스트 작성

JUnit 5Mockito를 활용하여 서비스 레이어의 핵심 비즈니스 로직에 대한 단위 테스트를 작성했습니다.

// CommunityServiceTest.java - Mockito를 활용한 단위 테스트
@Mock
private CommunityRepository communityRepository;
    
@InjectMocks
private CommunityServiceImpl communityService;
    
@Test
@DisplayName("게시글 작성 성공 테스트")
void writeBoard_Success() {
    // when/then: Mock 객체 행동 정의 및 결과 검증
    when(communityRepository.writeBoard(any(Community.class))).thenReturn(1);
    int result = communityService.writeBoard(testCommunity);
    assertThat(result).isEqualTo(1);
}

배운 점: 테스트 코드는 코드의 안정성을 담보하며, 리팩토링의 안전망 역할을 합니다. TDD (Test-Driven Development) 방식으로 개발하면 더 응집도 높은 코드를 작성할 수 있습니다.


프로젝트를 통해 얻은 주요 교훈 및 다음 계획

1. 아키텍처 및 운영의 중요성

  • 설정 중앙화: 향후 Spring Cloud Config를 도입하여 모든 서비스의 설정을 중앙에서 관리하여 설정 배포 및 버전 관리를 자동화할 예정입니다.
  • API Gateway 활용: 모든 요청의 단일 진입점으로서 라우팅 외에도 인증/인가, Rate Limiting 등의 역할을 수행하도록 설계했습니다.
  • 관측 가능성 (Observability): Spring Boot Actuator로 헬스체크 및 메트릭을 수집하고, Log4j2로 구조화된 로그를 남겨 문제 발생 시 신속하게 추적할 수 있도록 기반을 마련했습니다. (향후 Prometheus/GrafanaELK Stack 도입 예정)

2. 다음 단계: 안정적인 서비스 배포 및 운영을 위한 DevOps 구축

이번 프로젝트를 기반으로 다음 단계에서는 실제 운영 환경을 고려한 DevOps 파이프라인 구축에 집중할 계획입니다.

  • 컨테이너화: 모든 서비스를 Docker 이미지로 빌드 및 관리합니다.
  • CI/CD 자동화: GitHub Actions 또는 Jenkins를 활용하여 빌드, 테스트, 배포를 자동화합니다.
  • 오케스트레이션: Kubernetes를 도입하여 컨테이너의 배포, 스케일링, 복구를 자동 관리하여 고가용성 환경을 구축할 예정입니다.
  • 모니터링 강화: Jaeger를 통한 분산 추적 (Distributed Tracing) 시스템을 구축하여 복잡한 MSA 환경에서 요청의 흐름과 병목 지점을 정확하게 파악할 계획입니다.

MSA를 학습하고나서 느낌..

MSA는 복잡하지만, 이번 프로젝트를 통해 아키텍처 설계 원칙의 중요성, **분산 시스템 컴포넌트 (Service Discovery, Gateway)**의 역할, 그리고 안정적인 개발을 위한 테스트 전략을 깊이 있게 학습할 수 있었습니다. 실패를 통한 개선이야말로 가장 가치 있는 학습 경험이었습니다.

MSA의 장점을 최대한 살리면서, 다음 프로젝트에서는 더 견고한 DevOps 및 모니터링 환경을 구축하여 엔지니어링 역량을 한 단계 더 끌어올릴 것입니다.

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

0개의 댓글