본문으로 건너뛰기
개발

여러 예약에 붙는 메모의 연결 설계와 리뷰 기준

여러 예약에 붙는 메모의 연결 설계와 리뷰 기준

오늘 주니어 개발자에게 들려준 이야기를 정리한다.

복잡성을 해결할 은탄환 같은 건 없다. 기술 스택을 바꾸거나 새로운 아키텍처를 도입하거나 특정 패턴을 적용해도 복잡성은 갑자기 사라지지 않는다.

대신 시간이 지날수록 늘어나는 복잡성이 관리 가능한 형태로 남도록 설계할 수는 있다.

설계가 팀 안에서 오래 유지되려면 왜 이렇게 설계했는지 설명할 수 있어야 한다. 이 설명은 코드 자체만큼 중요하다.

사례: 여러 예약에 연결되는 파트너 메모

관계가 깊은 도메인에서는 하위 데이터가 상위 관계를 전부 가지게 하는 선택이 자주 나온다.

오늘 주니어 개발자와 이야기한 사례는 파트너 메모가 예약에 붙는 구조였다. 예약은 한 종류로 고정되지 않았다. 비즈니스 흐름상 메모는 카드 예약, 무통장 예약, 오프라인(자체) 예약 중 어느 것에든 연결될 수 있었다.

이런 상황에서 구현하다 보면 다음 생각이 자연스럽게 든다.

"메모가 예약에 붙는 거라면, 메모가 연결 키를 다 가지고 있으면 조회가 편하지 않을까?"

이 생각을 따르면 메모의 "예약 연결"을 하나로 추상화하지 않고, 예약 유형별로 연결 필드를 따로 두게 된다.

이 선택에는 분명 실용적인 편의가 있지만 부작용도 함께 따라온다. 그래서 코드 리뷰를 요청할 때는 편하다는 이유 외에, 이 방식이 지금 도메인에서 왜 합리적인지도 설명할 수 있어야 한다.

설계 이유는 트레이드오프로 설명한다

조회가 쉽다는 것도 이유가 될 수 있지만 그것만으로는 부족하다. 설계에는 언제나 비용이 따르므로, 리뷰에서 설득력 있는 설명은 보통 얻는 것과 감수하는 것을 함께 놓는다.

얻는 것(Pros)

  • 자주 쓰는 조회가 단순해지고 빨라질 수 있다(조인 감소, 쿼리 단순화).
  • 특정 화면/운영 기능 요구사항에 최적화된다.
  • 데이터 접근 패턴이 명확해져서 구현 속도가 올라간다.

감수하는 것(Cons)

  • 규칙이 흐려질 수 있다. "정확히 하나만 연결"이 보장되지 않으면 모델 의미가 애매해진다.
  • 정합성 관리 비용이 생긴다. 연결 규칙을 DB/코드/운영 정책 중 어디에서 어떻게 강제할지 결정해야 한다.
  • 확장 비용이 올라간다. 예약 유형이 늘어날수록 컬럼/제약/인덱스가 계속 추가될 수 있다.
  • 삭제/이력 정책과 충돌할 수 있다. 하드 삭제, 소프트 삭제, 이력 보존 등과 함께 설계가 맞물려야 한다.

이 트레이드오프가 정리되지 않으면 리뷰어는 그 설계를 "이유 없는 편의"로 보게 된다. 반대로 트레이드오프가 정리되어 있으면 리뷰는 "맞냐/틀리냐" 대신 "위험을 어떻게 관리하냐"를 묻게 된다.

‘정확히 하나만 연결한다’를 리뷰 가능한 규칙으로 바꾸기

앞의 메모 사례에서 예약 유형별 연결 필드를 선택한다면, 나는 최소한 아래 표의 질문에는 답을 남기겠다. 표는 원래 대화의 설계 문제를 구체화한 검토 예시이며, 실제 운영 스키마를 공개한 것은 아니다.

입력 또는 사건선택한 규칙이 설명해야 할 결과
어떤 예약도 연결하지 않음독립 메모를 허용하는가? 허용하지 않으면 저장을 거절한다
카드 예약과 무통장 예약을 동시에 연결하나의 예약에 속하는 메모라면 거절한다
존재하지 않는 예약 식별자를 입력연결 개수만 검사해서는 잡을 수 없으므로 참조 무결성도 확인한다
예약을 삭제메모도 지울지, 삭제를 막을지, 이력과 함께 보존할지 정한다

PostgreSQL에서는 두 제약으로 책임을 나눌 수 있다. 같은 행의 연결 개수는 CHECK로 검사하고, 연결 대상의 존재는 FOREIGN KEY로 보장한다.

CHECK가 다른 테이블의 상태까지 보장한다고 생각하면 안 된다. PostgreSQL 제약 조건 문서는 이 경계와 삭제 시 참조 동작을 설명한다.

어떤 구조를 검토할지는 전제에 따라 달라진다. 예를 들어 예약 유형이 당분간 세 개로 고정되고, 메모가 반드시 그중 하나에 속한다고 가정한다. 이 전제라면 여러 외래 키와 연결 개수 제약을 검토할 수 있다.

반면 예약 유형이 계속 늘고 여러 기능에서 같은 예약을 참조한다면, 공통 예약 모델을 검토할 이유가 커진다.

유형과 식별자만 저장하는 방식도 있다. 이 방식은 컬럼 추가를 줄이지만, 서로 다른 대상 테이블의 존재를 일반 외래 키 하나로 보장하기 어렵다.

리뷰에는 선택이 유효한 조건과 다시 볼 시점까지 적는 편이 좋겠다. 예를 들면 "지금은 예약 유형이 세 개로 고정된다는 전제가 맞아서 이 구조를 선택했다. 예약 유형이 추가되거나 공통 조회가 늘면 재검토한다."처럼 쓴다.

조회가 빨라진다는 주장은 실제 쿼리와 실행 계획을 비교한 뒤에 붙일 수 있다.

코드 리뷰 요청에 남길 설계 설명

리뷰 요청에 아래 4가지만 적어도 리뷰어가 설계를 판단하기 쉬워진다.

  1. 문제 정의: 무엇이 어려워서 이 설계를 고민했는지
  2. 선택한 설계: 어떤 구조를 택했고, 어떤 규칙을 강제하는지
  3. 대안: 다른 방법(정규화 유지, 공통 예약 모델, 뷰/읽기 모델 등)을 왜 택하지 않았는지
  4. 트레이드오프와 방어책: 정합성/확장/삭제 정책을 어떻게 관리할지(제약/트리거/서비스 로직/배치 등)

정리

복잡성은 계속 늘어나고 설계로 없앨 수도 없다. 좋은 설계는 그 복잡성을 관리 가능한 형태로 정리하고, 팀이 납득할 수 있는 이유를 남긴다.

누군가 메모가 상위 관계를 전부 들고 있게 했다면, 질문은 그 선택이 맞는지에서 무엇을 얻었고 감수한 비용을 어떻게 관리할지로 바뀌어야 한다.

2026-09-14 보완: 연결 제약과 설계 재검토 조건을 구체화했다.