173. 데이터베이스 퍼 서비스 (Database per Service)
핵심 인사이트: 주문 서버가 결제 DB를 몰래 열어보고 데이터를 읽어간다면, 그건 무늬만 MSA일 뿐 속은 썩은 모놀리식이다. 진정한 MSA는 "내 DB는 내 꺼야! 네가 내 데이터 필요하면, DB 훔쳐보지 말고 정정당당하게 내 API(게이트웨이)를 호출해서 물어봐!"라고 강제로 철벽을 치는 데서 완성된다.
Ⅰ. Database per Service의 개념
마이크로서비스 아키텍처(MSA)에서 가장 핵심적이고 지키기 어려운 원칙입니다. "각각의 마이크로서비스는 반드시 자신만의 독립적인 데이터베이스(또는 독립적인 스키마/테이블 공간)를 가져야 하며, 다른 서비스는 이 DB에 절대 직접 접근할 수 없다" 는 강제적 아키텍처 패턴입니다.
Ⅱ. 통합 DB(Shared Database)의 문제점과 분리의 이유
MSA를 한다며 서버(App)는 3개로 쪼갰는데, 뒤에 있는 오라클 DB는 1개를 그대로 쓴다고 가정해 봅시다.
- 강결합(Tight Coupling)의 악몽: '결제 서비스' 개발자가
PAYMENT테이블의 컬럼 이름을status에서state로 무심코 바꿉니다. 그런데 몰래 이 테이블을 직접 조회(JOIN)해서 쓰고 있던 '주문 서비스' 서버가 갑자기Column Not Found에러를 뿜으며 뻗어버립니다. 서버를 아무리 쪼개놔도 DB가 묶여있으면 결국 연쇄 장애가 터집니다. - 의존적 배포: 결제 DB를 튜닝하려 해도, 주문팀과 배송팀에게 다 연락해서 허락을 받아야 배포가 가능해집니다. MSA의 목적(자율적 배포)이 박살 납니다.
Ⅲ. Database per Service 적용 후의 통신 방식
DB 퍼 서비스가 적용되면 타 서비스의 DB를 절대 SELECT 할 수 없습니다.
[ 데이터가 필요할 때는 오직 API 통신뿐! ]
❌ 금지된 방식 (강결합)
[주문 서비스] ───(몰래 직접 DB Query)──▶ [(결제 DB)]
✅ 완벽한 방식 (캡슐화 및 정보 은닉)
[주문 서비스] ───(HTTP GET: 결제 내역 줘)──▶ [결제 서비스 (API)] ──▶ [(결제 DB)]
Ⅳ. 데이터 분리가 가져오는 2가지 치명적 과제
이렇게 완벽하게 남남으로 찢어놓고 나면, 실무에서 피눈물 나는 2가지 과제가 생깁니다.
- 분산 조인(Join) 불가: 주문 내역과 결제 내역을 묶어서 "오늘의 결제 완료 주문 목록"을 웹 화면에 한 번에 띄워줘야 하는데(JOIN 쿼리 1방이면 끝날 일), DB가 떨어져 있으니 주문 API를 부르고, 결제 API를 부른 다음 애플리케이션(서버 메모리)에서 직접 for문을 돌리며 데이터를 조립(API Composition) 해야 합니다.
- 분산 트랜잭션의 한계: 주문 DB에는 주문 성공을 썼는데, 결제 API를 불렀더니 돈이 부족해서 실패했습니다. 기존엔
ROLLBACK한 방이면 다 취소됐지만, 이제는 "주문 취소 API"를 역으로 쏴서 로컬 트랜잭션을 되돌려야 합니다. 이를 해결하기 위해 Saga(사가) 패턴이 필수적으로 도입됩니다.
📢 섹션 요약 비유: 3형제가 돈을 한 통장에 모아놓고 같이 쓰면(통합 DB) 누가 얼마 썼는지 꼬이고 싸움이 납니다. 그래서 각자 독립된 3개의 통장(Database per Service)을 파서 완벽히 쪼개놓았습니다. 이제 첫째가 둘째의 통장을 몰래 볼 수 없고, 돈이 필요하면 정중하게 "카카오톡(API)으로 5만 원만 송금해 줘"라고 요청해야만 하는 깔끔하고 완벽한 회계 시스템이 되었습니다.