테이블은 어디에 둘 것인가 - 객체의 책임 분리와 DB 배치 기준
들어가며
새로운 도메인 개념이 등장했을 때, 개발자가 가장 먼저 마주하는 질문이 있다.
"이 테이블, 어느 DB에 만들지?"
얼핏 단순해 보이는 이 질문은, 사실 객체의 책임을 어디까지로 볼 것인가라는 설계 판단 그 자체다. 그리고 이 판단의 근거는 놀랍게도 코드나 ERD가 아니라, 비즈니스가 그 개념을 어떤 맥락에서 변경하고 싶어 하는가에 있다.
상황: "단백질 초코바"는 어디에 속하는가
기존 시스템에 두 개의 데이터베이스가 있다고 가정하자.
| DB | 관리 대상 | 핵심 관심사 |
|---|---|---|
| 식료품 DB | 과자, 음료, 초콜릿 등 | 맛, 용량, 유통기한, 원재료 |
| 영양제 DB | 비타민, 유산균, 오메가3 등 | 성분 함량, 일일 권장량, 인증 |
어느 날, PM이 말한다.
"단백질 초코바 상품을 새로 관리해야 해요."
단백질 초코바. 이름을 뜯어보면 두 세계의 언어가 섞여 있다.
- "단백질" → 영양제의 언어
- "초코바" → 식료품의 언어
이 테이블을 식료품 DB에 넣을 수도 있고, 영양제 DB에 넣을 수도 있다. 둘 다 틀린 선택이 아니다. 그렇다면 기준은 무엇인가?
첫 번째 힌트는 대부분 이름에 있다
사실 많은 경우, PM이 그 개념을 부르는 이름 자체가 가장 강력한 힌트다.
PM이 이 제품을 뭐라고 부르는지 유심히 들어보자. 대화 속에서 어떤 단어가 수식어이고 어떤 단어가 핵심 명사인지를 구분하면, 그 사람이 이 개념을 어느 세계에 놓고 생각하는지가 드러난다.
"저희 단백질 초코바 쪽 보충되는 영양소 비타민 C도 추가하고 싶어요."
이 문장에서 PM의 머릿속 구조를 읽어보자. "단백질"이 수식어가 아니라 핵심이다. "초코바"는 형태일 뿐, 관심사는 "단백질", "영양소", "비타민 C"에 있다. 이 PM에게 단백질 초코바란 바 형태를 한 영양제다.
"저희 초코바 쪽 새로운 맛으로 화이트 초콜릿맛 넣고 싶어요."
반면 이 문장에서는 "초코바"가 핵심 명사다. "단백질"이라는 단어는 아예 등장하지도 않는다. 관심사는 "맛", "화이트 초콜릿"에 있다. 이 PM에게 단백질 초코바란 단백질이 들어간 과자다.
같은 제품이지만, 부르는 이름의 무게중심이 다르다. 이름의 무게중심은 곧 관심사의 무게중심이고, 관심사의 무게중심은 곧 변경의 축이다.
물론 이름만으로 최종 결정을 내릴 수는 없다. PM이 습관적으로 쓰는 호칭과 실제 변경 요청이 다를 수도 있다. 하지만 이름은 가장 빠르고 직관적인 첫 번째 신호다. 이 신호를 포착한 뒤, 실제 요구사항으로 검증하면 된다.
정답은 코드에 없다. 요구사항에 있다.
객체지향 설계에서 "책임"이란, 변경의 이유를 의미한다. 어떤 객체가 존재하는 이유는 그 객체가 담당하는 변경의 축이 있기 때문이다. DB도 마찬가지다. 하나의 DB(혹은 스키마, 바운디드 컨텍스트)는 하나의 변경 축을 중심으로 응집되어야 한다.
그러면 "단백질 초코바"의 변경 축은 어디에 있을까? 이건 개발자가 정하는 게 아니다. PM에게 물어봐야 한다.
시나리오 A: 영양 성분 중심으로 변경이 일어나는 경우
PM의 요청이 주로 이런 방향이라고 해보자.
"단백질 초코바에 보충되는 영양소로 비타민 C도 추가하고 싶어요."
"단백질 함량을 20g에서 25g으로 올린 신제품이 나왔어요."
"식약처 인증 마크를 표시해야 해요."
이 경우, 단백질 초코바의 변경 이유는 영양 성분, 함량, 인증 — 즉 영양제 DB가 이미 관리하고 있는 관심사와 동일하다. 단백질 초코바 테이블은 영양제 DB에 두는 것이 자연스럽다.
왜냐하면, 영양소를 추가하거나 함량을 변경할 때 영양제 DB의 기존 구조(성분 테이블, 함량 컬럼, 인증 정보 등)를 그대로 활용할 수 있기 때문이다. 이미 존재하는 변경 패턴 위에 새 테이블이 자연스럽게 올라간다.
시나리오 B: 맛·제품 라인업 중심으로 변경이 일어나는 경우
반대로, PM의 요청이 이런 방향이라면 어떨까.
"초코바에 화이트 초콜릿맛을 새로 넣고 싶어요."
"미니 사이즈 출시할 건데 용량 옵션을 추가해주세요."
"유통기한 표기 방식을 바꿔야 해요."
이 경우의 변경 이유는 맛, 용량, 유통 — 식료품 DB가 다루는 관심사다. 단백질 초코바 테이블은 식료품 DB에 두는 것이 맞다.
식료품 DB에는 이미 맛 변형(flavor variant), 용량 옵션(size option), 유통 정보를 다루는 구조가 갖추어져 있을 것이다. 여기에 단백질 초코바를 추가하면, 새로운 맛이 출시될 때마다 기존 패턴을 그대로 따를 수 있다.
핵심 원칙: "함께 변경되는 것은 함께 둔다"
이것은 단순히 "비슷한 것끼리 모은다"는 분류학적 사고가 아니다.
"함께 변경되는 것을 함께 두고, 다른 이유로 변경되는 것은 분리한다."
이것이 단일 책임 원칙(SRP)의 DB 배치 버전이다. Robert C. Martin이 SRP를 설명할 때 사용한 표현을 빌리자면, 책임이란 "변경을 요청하는 사람(actor)"에 의해 정의된다. DB 배치도 동일하다.
- 영양 성분 변경을 요청하는 사람(영양 담당 PM)의 요구가 주된 변경 축이면 → 영양제 DB
- 맛·라인업 변경을 요청하는 사람(제품 기획 PM)의 요구가 주된 변경 축이면 → 식료품 DB
결국 "누가, 무엇을, 얼마나 자주 바꾸고 싶어 하는가"가 테이블의 주소를 결정한다.
잘못된 배치의 대가
만약 이 판단을 거꾸로 했다면 어떻게 될까?
영양 성분 변경이 주된 요구사항인데 식료품 DB에 테이블을 뒀다고 가정하자. PM이 "비타민 C 성분을 추가해주세요"라고 요청할 때마다, 개발자는 다음과 같은 상황에 놓인다.
- 식료품 DB에는 영양 성분을 관리하는 구조가 없다.
- 성분 컬럼을 억지로 추가하거나, 영양제 DB를 조회하는 크로스 DB 참조가 생긴다.
- 하나의 요구사항을 반영하기 위해 두 DB를 동시에 수정해야 한다.
- 배포 단위가 엮이고, 장애 반경이 넓어진다.
작은 테이블 하나의 위치가 틀어졌을 뿐인데, 변경 비용이 기하급수적으로 올라간다.
실무에서의 판단 체크리스트
새로운 테이블의 위치를 결정해야 할 때, 다음 질문을 순서대로 던져보자.
첫째, 이 데이터의 주된 변경 요청자는 누구인가?
같은 "단백질 초코바"라도, 영양 담당 PM이 주 요청자인지 제품 기획 PM이 주 요청자인지에 따라 답이 달라진다.
둘째, 향후 들어올 변경 요청의 패턴은 기존 어느 DB의 패턴과 닮아 있는가?
"성분 추가" 요청이 주로 올 예정이라면 영양제 DB의 변경 패턴과 동일하다. "맛 추가" 요청이 주로 올 예정이라면 식료품 DB의 변경 패턴과 동일하다.
셋째, 이 테이블이 변경될 때 함께 변경되어야 하는 기존 테이블은 어디에 있는가?
단백질 초코바의 성분을 바꿀 때 성분 마스터 테이블도 함께 수정된다면, 그 마스터 테이블이 있는 DB에 함께 두는 것이 트랜잭션 경계를 단순하게 유지하는 길이다.
마치며
"단백질 초코바는 식료품인가, 영양제인가?"
이 질문에 보편적으로 옳은 답은 없다. 있는 것은 맥락적으로 옳은 답뿐이다. 그리고 그 맥락은 기술이 아니라 비즈니스가 제공한다.
테이블의 위치를 정하는 일은 곧 책임의 경계를 긋는 일이다. 그 경계는 "이 데이터가 무엇인가"가 아니라 "이 데이터가 왜, 어떻게 바뀌는가"에 의해 결정된다. PM의 요구사항을 경청하는 것이 곧 좋은 설계의 출발점인 이유다.