데이터베이스는 어떻게 할 것인가?
코드뿐만 아니라 데이터베이스 스키마와 테이블 접근 권한을 기능별로 분리하여 데이터 결합도를 낮추는 전략을 다룹니다.
코드를 도메인별로 완벽하게 나누었다고 생각하시나요? 많은 개발자가 소스 코드는 아름답게 분리해 놓고도, 정작 뒷단의 데이터베이스에서는 끔찍한 실수를 저지릅니다. 여러 모듈의 코드가 하나의 데이터베이스 테이블에 동시에 쓰기(Write) 작업을 수행하거나 복잡하게 조인(Join)되는 순간, 그동안 노력했던 코드 분리는 무용지물이 됩니다. 이번 시간에는 데이터베이스 관점에서 결합도를 끊어내는 방법을 배웁니다.
가장 끊어내기 힘든 사슬: 데이터베이스 결합
애플리케이션 코드를 분리하는 것보다 수십 배 어려운 작업이 바로 데이터베이스를 분리하는 것입니다. 관계형 데이터베이스(RDBMS)의 강력한 특징인 `JOIN`과 `외래 키(Foreign Key)`는 역설적으로 모듈 간의 결합도를 극한으로 높이는 주범이 됩니다.
예를 들어 '주문(Order)' 모듈에서 어떤 정보를 화면에 보여주기 위해, 하나의 거대한 쿼리로 `Orders`, `Users`, `Payments`, `Products` 테이블을 조인해서 데이터를 가져온다고 가정해봅시다.
이 경우 '회원(User)' 테이블의 구조가 아주 조금만 변경되어도, 회원이 아닌 주문 모듈의 코드가 덩달아 망가져 버립니다. 데이터를 통해 모듈들이 강하게 묶여버린 전형적인 데이터 통합(Data Integration) 안티 패턴입니다.
데이터베이스도 나누어야 모듈이다
진정한 모듈러 모놀리스는 소스 코드뿐만 아니라 데이터의 접근 권한까지 명확하게 분리해야 합니다. 기본 원칙은 다음과 같습니다.
> "특정 데이터베이스 테이블의 소유권은 단 하나의 모듈만 가진다."
`Users` 테이블은 회원 모듈만이 쓰고 읽을 수 있어야 합니다. 결제 모듈이나 주문 모듈은 절대로 `Users` 테이블에 직접 SQL 쿼리를 날리거나 ORM 객체를 통해 접근해서는 안 됩니다.
데이터베이스 분리 원칙
- 1테이블 소유권 독점: 각 테이블은 오직 담당 모듈만 직접 읽고 쓴다.
- 2외래 키(Foreign Key) 제약 최소화: 모듈의 경계를 넘나드는 외래 키 제약 조건은 데이터 강결합을 유발하므로 피한다.
- 3데이터 조회는 인터페이스를 통해: 다른 모듈의 데이터가 필요하다면, 직접 조회하지 않고 해당 모듈의 API(함수)를 호출하여 전달받는다.
모듈 간 조인(Join) 피하기
테이블에 직접 접근할 수 없다면, 다른 모듈의 데이터를 조합해야 할 때는 어떻게 할까요? 크게 두 가지 접근법이 있습니다.
1. 애플리케이션 레벨에서의 조합 (In-Memory Join)
과거에는 DB에서 한 번에 조인해서 가져오는 것이 당연했습니다. 모듈러 모놀리스에서는 조인 대신 모듈별로 데이터를 각각 가져온 뒤, 코드에서(메모리 상에서) 조합하는 방식을 권장합니다.
- 주문 모듈: 데이터베이스에서 주문 목록을 조회합니다. (주문자 ID 포함)
- 주문 모듈: 회원 모듈의 인터페이스를 호출해 회원 ID에 해당하는 '회원 이름'들을 달라고 요청합니다.
- 데이터 합치기: 주문 모듈은 두 데이터를 코드에서 합쳐 사용자에게 반환합니다.
이 방식은 쿼리가 여러 번 실행된다는 성능상의 단점이 있어 보이지만, 최적화가 가능하며 무엇보다 모듈 간의 독립성을 완벽하게 보장한다는 엄청난 이점이 있습니다.
2. 논리적 스키마(Schema) 분리
물리적으로 데이터베이스 서버를 쪼개지 않더라도, PostgreSQL의 Schema 기능 등을 활용해 데이터베이스 내부에 벽을 세울 수 있습니다.
- `inventory` 스키마 안에는 상품, 재고 테이블 위치
- `billing` 스키마 안에는 결제, 영수증 테이블 위치
그리고 애플리케이션의 데이터베이스 계정(Role) 접근 권한을 분리하여, 서로 다른 스키마를 직접 조회하지 못하도록 물리적으로 강제할 수도 있습니다.
중복을 두려워하지 말자
"상품 가격이 변경되면 주문 내역의 상품 가격도 바뀌어야 하지 않나요?"<br/> 많은 초보 설계자들이 범하는 실수입니다. 결제가 완료된 주문은 당시의 가격을 스냅샷으로 저장해야 합니다. 즉, '카탈로그 모듈'의 상품 정보와 '주문 모듈'의 주문 상품 정보는 전혀 다른 데이터입니다. 데이터를 정규화하여 한 곳에 모으려다 보면 결합도가 치솟습니다. 도메인의 경계를 넘는 데이터는 적절히 중복(복제) 저장하는 것이 훨씬 안전한 설계입니다.
언젠가 DB가 쪼개질 날을 위하여
이렇게 지독하게 데이터베이스까지 결합도를 낮추는 이유는 무엇일까요?
미래에 트래픽 폭주로 결제 시스템만 별도의 마이크로서비스로 독립해야 한다고 상상해 보십시오. 만약 결제 테이블이 주문 테이블과 거미줄처럼 조인되어 있다면 시스템을 쪼개는 것은 사실상 불가능합니다.
하지만 모듈러 모놀리스의 원칙에 따라 데이터베이스 접근까지 완벽히 분리해 두었다면? 결제 모듈의 코드와 결제 테이블만 그대로 새로운 서버로 옮기면 작업은 끝납니다. 모듈러 모놀리스는 훗날의 마이크로서비스 도입을 위한 완벽한 리허설 무대입니다.
개념 퀴즈
퀴즈 답을 맞춰야 학습 완료가 됩니다.모듈러 모놀리스 환경에서 다른 도메인(모듈)에 속한 테이블의 데이터가 필요할 때 가장 올바른 접근 방법은 무엇입니까?
3줄 요약
- 1모듈러 모놀리스의 완성은 코드가 아닌 데이터베이스의 의존성을 끊어내는 데 있습니다.
- 2모든 데이터베이스 테이블은 반드시 하나의 모듈만이 소유하고 관리해야 하며, 타 모듈의 테이블에 직접 접근해서는 안 됩니다.
- 3관계형 데이터베이스의 조인(Join) 대신 애플리케이션 메모리 상에서 데이터를 조합하는 방식을 통해 결합도를 낮춰야 합니다.