M2M, MSA 환경에서의 내부 통신 고민
개요
알림 서비스(Notification Service)를 개발하던 중, 이벤트 발생 시 발신자와 수신자의 Slack ID를 가져와 슬랙 알림을 전송해야 하는 기능이 필요했다.
Slack ID 정보는 사용자 서비스(User Service)에서 관리하고 있었기에 서비스 간 통신이 필요한 상황이었다.
발생한 문제
- 의존성 문제: 알림 발송 시 사용자 서비스의 API(FeignClient, RestClient 등)를 호출하여 Slack ID를 조회하려고 함
- 접근 권한 : 사용자 서비스에서 사용자 정보를 조회하는 기존 기능이 Master(관리자) 전용 엔드포인트로 설정되어 있었음
고민 과정
- Feign 통신만을 위한 **내부 전용 엔드포인트(Internal API)**를 별도로 만들어 달라고 사용자 서비스 팀에 요청해야 할까?
- 아니면 내부 통신용 **Master 권한(Admin Token/Key)**을 발급받아 그대로 호출하는 게 맞을까?
현업에서는 이러한 M2M(Machine-to-Machine) 통신을 어떻게 처리하며, 어떤 방식이 백엔드 아키텍처 관점에서 최선일지 깊게 고민했다.
특히 우리 팀은 이벤트 기반 로컬 데이터 동기화 방식은 일단은 차용하지 않기로 했기 때문에, 더 좋은 방법이 있는지 깊은 고민이 필요했다.
현업 표준 아키텍처 분석 및 해결책
고민하고 튜터님께 상담한 결과, M2M 통신 및 타 도메인 데이터 참조 방식에는 크게 2가지 핵심 접근법이 존재하며, 상황에 맞는 선택이 필요함을 정리했다.
내부 통신 전용 엔드포인트 (Internal API) 방식
"외부 유저용 API와 내부 시스템 통신용 API는 목적과 권한 자체가 다르다."
Master 권한을 그대로 넘겨받아 호출하는 방식은 보안상 위험하고 사이드 이펙트가 매우 크다. 따라서 시스템 간 통신을 위한 엔드포인트를 따로 분리하는 것이 정석이다.
- 용도 분리:
- 외부 API: 사용자 JWT 기반 인가 (관리자 권한 여부 체크)
- 내부 API: 서비스(M2M) 간 통신을 위한 목적 (유저의 권한과 무관)
- 보안 및 인프라 (Gateway):
- 내부 통신 전용 엔드포인트 프리픽스 정의 (예: /internal/api/v1/)
- API Gateway에서 /internal/** 경로로 들어오는 모든 외부 접근을 차단 (403 Forbidden 처리). 인프라 단에서 내부 망 통신만 허용하도록 격리
- 성능 및 장애 방어:
- 캐싱 (Caching): Feign 호출 횟수와 네트워크 Latency를 줄이기 위해 요청측(알림 서비스)과 응답측(사용자 서비스) 모두 적절한 캐싱 전략(Caffeine, Redis 등) 적용
- 서킷 브레이커 (Circuit Breaker): 사용자 서비스에 장애나 둔화가 발생했을 때 알림 서비스로 연쇄 장애(Cascading Failure)가 전파되지 않도록 Resilience4j 등으로 Circuit Breaker 구축 필수
이벤트 기반 로컬 DB 동기화 방식
"타 서비스의 데이터를 내 로컬 DB에 동기화하여 결합도를 극도로 낮춘다."
Feign을 통한 동기 호출 대신, 데이터 변경 이벤트를 수신하여 내 로컬 DB에 최소한의 필요한 정보(Slack ID)만 동기화해 두는 방식이다.
- 동작 흐름:
- 사용자 서비스에서 사용자 정보 수정 발생 시 UserUpdatedEvent 발행 (Kafka/RabbitMQ)
- 알림 서비스에서 해당 이벤트를 Listen하여 내 로컬 DB의 Slack ID 업데이트
- 장점:
- 장애 전파 최소화: 내 로컬 DB를 직접 참조하므로, 사용자 서비스에 장애가 나거나 점검 중이어도 알림 서비스는 100% 정상 작동 (강한 결합 제거).
- Eventually Consistency (최종 일관성): 사용자 서비스가 잠깐 다운되더라도 복구 후 메시지 큐를 통해 데이터 일관성이 보장됨.
- CQRS 패턴과의 궁합: 도메인의 Write DB와 Read DB를 분리할 때, Read DB에 타 서비스의 데이터를 미리 집어넣어 고속 조회를 구현하기 용이함.
- 단점:
- 메시지 브로커, 이벤트 리스너, 재시도/DLQ 처리 등 인프라 및 코드 구현 복잡도가 Feign 방식에 비해 훨씬 높음.
팀 컨벤션 및 인프라 적용 규칙
이번 회고를 통해 향후 서비스 간 통신 시 준수할 컨벤션을 아래와 같이 수립했다.
내부 API 명세 및 Naming Rule
- 서비스 간 내부 통신용 API는 반드시 별도로 명세하고 관리한다. (제공 측, 소비 측 명시)
- 엔드포인트 URL에는 반드시 /internal/api/v1/ 프리픽스를 사용한다.
API Gateway 외부 접근 차단
- 인프라(Gateway) 단에서 /internal/** 패턴의 요청은 외부망 진입 시 즉시 차단한다.
# Spring Cloud Gateway 설정 예시
spring:
cloud:
gateway:
routes:
- id: block_internal_route
uri: nohub://
predicates:
- Path=/internal/**
filters:
- SetStatus=403
'IT > 트러블슈팅' 카테고리의 다른 글
| [Trouble Shooting] 해시태그-카테고리 유사도 판단 파이프라인 (0) | 2026.09.23 |
|---|---|
| [Spring boot] 낙관락, 비관락 의사결정 기준에 대해 (0) | 2026.07.08 |
| [Spring boot] Soft-Delete와 Unique 제약조건 고찰 (0) | 2026.07.07 |
| [Spring boot] CSRF 토큰과 Session 생성 타이밍 불일치 (1) | 2026.04.14 |
| [Spring boot] 사용자의 리소스 접근 권한에 대해 (0) | 2025.04.10 |