관계형 데이터베이스만 쓰다가 처음 NoSQL을 접했을 때, 솔직히 멘붕이었다. "테이블 조인 없이 어떻게 데이터를 설계하지?" 싶었거든. 그런데 실무에서 MongoDB를 3년 넘게 쓰면서 깨달은 게 있다. NoSQL 데이터 모델링은 '다르게 생각하는 법'을 배우는 과정이라는 거죠. 오늘은 제가 실제로 프로젝트에서 써먹었던 NoSQL 데이터 모델링 패턴들을 정리해 정리하겠다.
NoSQL 데이터 모델링, 왜 RDBMS와 다른가
사고방식의 전환: 쿼리 중심 설계
RDBMS에서는 데이터의 구조(엔티티, 관계)를 먼저 정의하고, 그다음에 쿼리를 작성한다. 정규화를 열심히 하고, 필요하면 JOIN을 걸면 되니까. 하지만 NoSQL, 특히 MongoDB에서는 정반대다. "이 데이터를 어떻게 읽을 것인가?"를 먼저 결정하고, 그에 맞춰 데이터 구조를 설계한다. 이걸 Query-Driven Design이라고 부릅니다.
예를 들어, 전자상거래 서비스에서 주문 상세 페이지를 보여줘야 한다면, 주문 정보·상품 정보·배송 정보를 한 문서에 다 넣는 게 NoSQL에서는 자연스럽다. RDBMS라면 orders, products, shipping 세 테이블을 조인했겠지만요.
정규화 vs 비정규화의 트레이드오프
NoSQL 데이터 모델링에서 가장 핵심적인 판단은 "임베딩(내장)할 것이냐, 레퍼런스(참조)할 것이냐"다. 임베딩은 관련 데이터를 한 문서 안에 넣는 것이고, 레퍼런스는 별도 컬렉션에 두고 ID로 연결하는 것다. 정답은 없다. 읽기 성능이 중요하면 임베딩, 데이터 일관성이 중요하면 레퍼런스를 선택하면 된다.
스키마리스의 함정
MongoDB가 스키마리스(Schemaless)라고 해서 아무렇게나 데이터를 넣어도 된다는 뜻은 절대 아닙니다. 오히려 스키마 설계를 더 신중하게 해야 한다. 스키마 검증(Schema Validation)을 반드시 설정하시고, 애플리케이션 레벨에서도 Mongoose 같은 ODM으로 스키마를 잡아주는 게 좋다. 이걸 안 하면 나중에 데이터 정합성 문제로 밤새 디버깅하게 된다.
실무에서 자주 쓰는 핵심 모델링 패턴 6가지
임베디드 문서 패턴과 참조 패턴
임베디드 문서 패턴(Embedded Document Pattern)은 가장 기본이 되는 패턴다. 1:1 또는 1:소수 관계에서 자식 데이터를 부모 문서 안에 내장한다. 블로그 포스트와 댓글 관계가 대표적이다. 댓글이 수십 개 수준이라면 포스트 문서 안에 comments 배열로 넣는 게 효율적다. 한 번의 읽기로 포스트와 댓글을 모두 가져올 수 있으니까.
반면, 참조 패턴(Reference Pattern)은 1:다수 또는 다:다 관계에서 사용한다. 댓글이 수천, 수만 개가 될 수 있는 서비스라면 별도 컬렉션으로 분리하고 postId로 참조해야 한다. MongoDB 문서 크기 제한이 16MB이기 때문에, 무한정 임베딩할 수는 없다.
버킷 패턴과 서브셋 패턴
버킷 패턴(Bucket Pattern)은 시계열 데이터나 IoT 센서 데이터처럼 연속적으로 쌓이는 데이터에 최적화된 패턴다. 센서에서 1초마다 데이터가 들어온다고 가정하면, 매 초마다 새 문서를 만드는 대신 1시간 단위로 하나의 문서에 묶어서 저장한다. 이렇게 하면 문서 수가 3,600분의 1로 줄어들고, 인덱스 크기도 극적으로 감소한다. 실제로 제가 IoT 프로젝트에서 이 패턴을 적용했을 때 쿼리 성능이 약 15배 개선됐다.
서브셋 패턴(Subset Pattern)은 자주 접근하는 데이터만 메인 문서에 두고, 나머지는 별도 컬렉션에 보관하는 방식다. 상품 페이지에서 리뷰를 보여줄 때, 최신 리뷰 5개만 상품 문서에 임베딩하고 전체 리뷰는 reviews 컬렉션에 따로 두는 식이다. Working Set(자주 접근하는 데이터)을 메모리에 유지할 수 있어서 캐시 효율이 좋아집니다.
다형성 패턴과 속성 패턴
다형성 패턴(Polymorphic Pattern)은 비슷하지만 구조가 조금씩 다른 데이터를 하나의 컬렉션에 저장하는 패턴다. 예를 들어 콘텐츠 서비스에서 텍스트 글, 이미지, 동영상이 각각 다른 필드를 가지지만 하나의 posts 컬렉션에 type 필드로 구분하며 저장할 수 있다. RDBMS였다면 테이블을 세 개 만들거나 복잡한 상속 구조를 잡아야 했을 겁니다.
속성 패턴(Attribute Pattern)은 필드가 유동적으로 변하는 경우에 유용한다. 전자제품 쇼핑몰에서 노트북은 CPU, RAM, 저장공간 같은 스펙이 있고, 냉장고는 용량, 에너지 등급 같은 스펙이 있다. 이런 경우 specs라는 배열 안에 {key, value} 쌍으로 저장하면 모든 속성에 대해 하나의 인덱스만으로 검색이 가능해집니다.