
이 프로젝트는 기존의 헬스케어 모놀리식 아키텍처를 Microservice Architecture (MSA)로 전환하고 구축하는 과정을 담고 있습니다.
인증, 사용자 관리, 헬스 데이터, 커뮤니티 등 명확히 분리되는 도메인을 기반으로 안정적인 분산 시스템을 구축하는 것이 목표였습니다.
MSA를 선택한 주요 이유는 다음과 같습니다.
service.auth), 사용자 관리 (service.usermanagement), 헬스케어 데이터 (service.healthcare), 커뮤니티 (service.comm).MSA를 처음 구축하면서 발생했던 '실수 (Mistake)'와 이를 해결하기 위한 '개선 (Improvement)' 과정을 중점적으로 다루며, 학습 과정을 강조했습니다.
AES256 암호화 키, JWT 시크릿 키, 데이터베이스 접속 정보 등을 코드나 설정 파일에 하드코딩하여 관리했습니다. 이는 보안상 매우 취약하며 환경별 분리가 불가능했습니다.
// AES256Util.java - 하드코딩된 키 (매우 위험!)
private static final byte[] KEY = { /* ... 32바이트 하드코딩 ... */ };
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으로 주입하여 관리하는 것이 필수입니다.
API Gateway가 백엔드 서비스의 URL (IP 및 Port)을 직접 알고 하드코딩된 설정으로 통신했습니다.
이는 서비스의 위치가 변경되거나 인스턴스가 늘어날 때마다 수동으로 설정을 변경해야 하는 치명적인 문제점을 야기했습니다.
# web.healthcare/application.yml - 하드코딩된 서비스 URL
gateway:
auth:
uri: http://localhost:8082
healthcare:
uri: http://localhost:8081 # ... 수동 관리의 시작
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는 동적인 환경에서 필수적이며, 로드밸런싱과 헬스체크를 자동화하여 운영 복잡도를 낮출 수 있습니다.
대부분의 서비스에 테스트 코드가 없어 리팩토링 시 버그 발생 위험이 높았고, 배포 후에도 불안정했습니다.
JUnit 5와 Mockito를 활용하여 서비스 레이어의 핵심 비즈니스 로직에 대한 단위 테스트를 작성했습니다.
// 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) 방식으로 개발하면 더 응집도 높은 코드를 작성할 수 있습니다.
이번 프로젝트를 기반으로 다음 단계에서는 실제 운영 환경을 고려한 DevOps 파이프라인 구축에 집중할 계획입니다.
MSA는 복잡하지만, 이번 프로젝트를 통해 아키텍처 설계 원칙의 중요성, **분산 시스템 컴포넌트 (Service Discovery, Gateway)**의 역할, 그리고 안정적인 개발을 위한 테스트 전략을 깊이 있게 학습할 수 있었습니다. 실패를 통한 개선이야말로 가장 가치 있는 학습 경험이었습니다.
MSA의 장점을 최대한 살리면서, 다음 프로젝트에서는 더 견고한 DevOps 및 모니터링 환경을 구축하여 엔지니어링 역량을 한 단계 더 끌어올릴 것입니다.