NoSQL은 관계형 데이터베이스의 반대말이 아니다
NoSQL은 SQL을 쓰지 않는 데이터베이스라는 뜻으로만 이해하면 부족하다. 더 정확히는 관계형 모델 하나로 모든 문제를 풀지 않고, 데이터의 모양과 조회 방식에 맞는 저장 구조를 선택하자는 흐름에 가깝다.
관계형 데이터베이스는 여전히 강력한 기본 선택지다. 트랜잭션, 조인, 제약 조건, SQL, 운영 도구가 성숙하다. 주문, 결제, 재고, 정산처럼 정합성이 중요한 업무에는 대부분 관계형 데이터베이스가 잘 맞는다.
하지만 모든 데이터를 같은 방식으로 저장할 필요는 없다. 사용자 세션을 매번 조인해서 읽을 필요는 없고, 추천 후보를 찾기 위해 수십 단계의 관계를 반복 조인하는 것도 비효율적일 수 있다. 로그성 이벤트를 초당 수만 건씩 받아야 한다면 전통적인 행 중심 모델보다 쓰기 확장에 강한 저장소가 더 적합할 수 있다.
핵심은 NoSQL이 더 새롭거나 더 빠르기 때문이 아니라, 특정 문제를 더 단순하게 풀 수 있는 경우가 있다는 점이다.
AI 생성 이미지
저장소는 데이터 모양보다 조회 패턴으로 고른다
NoSQL 저장소는 보통 문서형, 키-값, 컬럼 패밀리, 그래프 데이터베이스로 나눈다. 이 분류는 제품 이름을 외우기 위한 것이 아니라 어떤 질문에 빠르게 답할 수 있는지를 구분하기 위한 것이다.1
| 종류 | 대표 제품 | 잘 맞는 데이터 | 강한 조회 패턴 | 주의할 점 |
|---|---|---|---|---|
| 문서형 | MongoDB, Couchbase | JSON과 비슷한 계층형 데이터 | 한 객체와 관련된 정보를 한 번에 읽기 | 중복 데이터 갱신, 문서 크기 증가 |
| 키-값 | Redis, DynamoDB 일부 사용 방식 | 키로 바로 찾는 단순 값 | 정확한 키 기반 초저지연 조회 | 값 내부 조건 검색과 복잡한 질의에 약함 |
| 컬럼 패밀리 | Cassandra, HBase | 대량 이벤트, 시계열성 데이터 | 특정 파티션 키 기준의 대량 쓰기와 조회 | 쿼리 유연성이 낮고 모델링을 미리 잘해야 함 |
| 그래프 | Neo4j, Amazon Neptune | 사람, 조직, 상품, 권한처럼 관계가 핵심인 데이터 | 여러 단계 관계 탐색 | 단순 CRUD에는 과하고 운영 경험이 별도로 필요 |
예를 들어 블로그 게시글, 작성자 요약, 태그, 최근 댓글 일부를 항상 함께 보여준다면 문서형 저장소가 자연스럽다. 반대로 user:123:session 같은 키로 세션을 즉시 찾아야 한다면 키-값 저장소가 더 단순하다. 친구의 친구, 비슷한 취향의 사용자, 조직 상위 권한처럼 관계를 따라가야 한다면 그래프 데이터베이스가 강점을 가진다.
중요한 질문은 이 데이터가 어떻게 생겼는가가 아니다. 이 데이터를 어떤 조건으로, 얼마나 자주, 어느 정도의 지연 안에 읽고 쓰는가다.
문서형 저장소: 함께 읽는 데이터는 함께 둔다
문서형 데이터베이스는 하나의 레코드 안에 중첩 객체와 배열을 담을 수 있다. MongoDB의 문서는 JSON과 비슷한 형태로 사용자, 주소, 설정, 최근 활동 같은 관련 값을 한 덩어리로 저장할 수 있다.
관계형 모델에서는 사용자 테이블, 주소 테이블, 설정 테이블을 나누고 조인으로 다시 합친다. 문서형 모델에서는 애플리케이션이 자주 읽는 단위를 하나의 문서로 저장한다. 그래서 상품 상세 화면처럼 하나의 집합을 통째로 읽는 기능에서는 쿼리가 단순해질 수 있다.
다만 중첩이 항상 좋은 것은 아니다. 판단 기준은 다음과 같다.2
부모와 자식 데이터를 거의 항상 함께 읽는다면 중첩이 유리하다.
일대소수 관계처럼 배열 크기가 예측 가능하면 중첩이 안전하다.
자식 데이터가 독립적으로 자주 변경되거나 여러 부모가 공유한다면 참조가 낫다.
댓글, 주문 이력, 활동 로그처럼 계속 늘어나는 목록을 한 문서에 무한히 넣는 것은 위험하다.
문서 하나가 너무 커지면 읽기, 쓰기, 복제, 백업 비용이 모두 커진다.
예를 들어 상품 문서에 상품명, 가격, 대표 이미지, 옵션 요약을 넣는 것은 자연스럽다. 상세 페이지에서 항상 같이 읽기 때문이다. 하지만 모든 주문 내역을 사용자 문서 안에 배열로 넣는 것은 위험하다. 오래 활동한 사용자의 문서만 비정상적으로 커지고, 주문 조회나 환불 처리도 어려워진다.
문서형 저장소의 설계 원칙은 정규화를 버리는 것이 아니다. 읽기 단위에 맞춰 의도적으로 묶고, 공유되거나 독립적으로 변하는 데이터는 참조로 분리하는 것이다.
Redis는 빠르지만 역할마다 위험이 다르다
Redis는 키-값 저장소로 시작했지만 실무에서는 캐시, 세션 저장소, 분산 락, 큐, 카운터 등 다양한 용도로 쓰인다. 빠르고 기능이 많기 때문에 오히려 역할을 명확히 정해야 한다.
캐시로 사용할 때는 원본 데이터베이스가 따로 있다. Redis의 값은 없어져도 다시 만들 수 있어야 한다. 그래서 TTL을 두고, 캐시 미스가 발생하면 원본에서 읽어 다시 채우는 방식이 흔하다. 주의할 점은 오래된 값이다. 상품 가격, 재고, 권한처럼 틀리면 문제가 큰 데이터는 캐시 만료 시간과 무효화 시점을 신중히 잡아야 한다.
세션 저장소로 쓸 때는 사용자 로그인 상태가 Redis에 의존한다. Redis 장애가 곧 대규모 로그아웃이나 인증 실패로 이어질 수 있다. 만료 시간, 복제, 장애 조치, 메모리 초과 시 eviction 정책을 확인해야 한다. 세션이 사라져도 다시 로그인하면 되는 서비스와, 결제 진행 상태처럼 중간 손실이 치명적인 서비스는 다르게 설계해야 한다.
분산 락으로 쓸 때는 더 조심해야 한다. Redis 공식 문서는 단일 인스턴스 락에서 SET key value NX PX timeout 형태를 사용하고, 해제할 때는 저장된 고유 값이 자신이 넣은 값과 같을 때만 삭제하라고 설명한다.3 그래야 락 시간이 만료된 뒤 다른 요청이 새 락을 잡았는데, 이전 요청이 그 락을 지워 버리는 사고를 막을 수 있다.
하지만 Redis 락은 만능 정합성 장치가 아니다. 락 TTL보다 작업이 오래 걸리거나, 네트워크 지연이 발생하거나, Redis 장애와 재시작이 겹치면 예상과 다른 상황이 생길 수 있다. 돈, 재고, 쿠폰처럼 반드시 한 번만 처리되어야 하는 작업은 데이터베이스의 유니크 제약, 조건부 업데이트, 트랜잭션과 함께 설계해야 한다.
큐로 사용할 때도 마찬가지다. 단순 비동기 작업에는 Redis List나 Stream이 유용하다. 그러나 작업 유실, 중복 실행, 재시도, 실패 큐, 처리 확인이 필요하다면 메시지 브로커처럼 운영해야 한다. 이메일 발송은 중복되어도 큰 문제가 아닐 수 있지만, 포인트 지급 작업은 멱등성 키와 처리 이력이 반드시 필요하다.
그래프 데이터베이스는 관계를 따라가는 문제에 강하다
그래프 데이터베이스는 데이터를 노드와 엣지로 표현한다. 노드는 사용자, 상품, 조직, 문서 같은 대상이고, 엣지는 친구다, 구매했다, 소속된다, 권한을 가진다 같은 관계다.
관계형 데이터베이스에서도 관계를 표현할 수 있다. 외래 키와 조인 테이블을 만들면 된다. 문제는 관계 탐색이 여러 단계로 깊어질 때다. 친구의 친구 중 특정 상품을 산 사람, 어떤 사용자가 접근 가능한 모든 문서, 한 부서의 하위 조직 전체에 전파되는 권한을 계산하려면 반복 조인이나 재귀 쿼리가 필요하다.
그래프 데이터베이스는 이런 탐색을 기본 작업으로 다룬다. 특정 노드에서 시작해 엣지를 따라 이동하고, 경로 길이, 관계 종류, 중간 노드 조건을 기준으로 결과를 찾는다. 추천 시스템, 사기 거래 탐지, 지식 그래프, 조직 권한 전파에 유리한 이유가 여기에 있다.
예를 들어 사용자가 직접 구매한 상품만 추천한다면 관계형 데이터베이스로 충분하다. 하지만 나와 비슷한 사용자, 그 사용자가 본 상품, 그 상품과 함께 구매된 상품, 내가 이미 본 상품 제외 같은 관계를 계속 따라가야 한다면 그래프 모델이 더 읽기 쉬운 설계를 제공할 수 있다.
물론 그래프 데이터베이스도 모든 추천 문제의 정답은 아니다. 단순 인기 상품, 카테고리 기반 추천, 최근 조회 기반 추천은 Redis나 관계형 데이터베이스, 검색 엔진으로도 충분하다. 그래프를 도입할 이유는 관계 자체가 핵심 데이터이고, 관계 탐색이 주요 쿼리일 때다.
NoSQL 도입의 비용을 먼저 계산한다
NoSQL을 도입하면 얻는 것이 있지만 잃는 것도 있다. 가장 큰 비용은 익숙한 관계형 데이터베이스의 기본 기능을 일부 포기하거나 애플리케이션에서 직접 책임져야 한다는 점이다.
첫째, 트랜잭션 범위가 달라진다. 문서 하나 안의 변경은 원자적으로 처리하기 쉬워도, 여러 문서와 여러 컬렉션을 함께 바꾸는 작업은 복잡해질 수 있다. 제품마다 다중 문서 트랜잭션을 지원하더라도 성능과 모델링상의 제약을 확인해야 한다.
둘째, 조인이 약하거나 비싸다. 관계형 데이터베이스에서는 정규화된 테이블을 조인해 필요한 형태로 읽는다. NoSQL에서는 자주 읽는 모양으로 미리 저장하거나, 애플리케이션에서 여러 번 조회해 합치는 경우가 많다. 이 선택은 중복 저장과 정합성 관리 비용으로 돌아온다.
셋째, 일관성 모델을 이해해야 한다. 어떤 저장소는 쓰기 확장을 위해 즉시 일관성보다 최종 일관성을 선택한다. 최종 일관성은 시간이 지나면 같은 상태로 수렴한다는 뜻이다. 피드, 조회수, 추천 후보에는 괜찮을 수 있지만 결제 승인, 재고 차감, 권한 변경에는 위험할 수 있다.
넷째, 운영 복잡도가 증가한다. 백업, 복구, 모니터링, 장애 조치, 보안 설정, 버전 업그레이드를 저장소마다 따로 알아야 한다. PostgreSQL 하나를 잘 운영하는 것보다 PostgreSQL, MongoDB, Redis, Neo4j를 함께 운영하는 것이 더 어렵다.
따라서 NoSQL 선택은 유행이 아니라 책임의 추가다. 특정 저장소가 문제를 단순하게 만들 때만 도입해야 한다.
선택 기준: 먼저 실패 모드를 상상한다
목적별 저장소를 고를 때는 다음 질문을 먼저 던져 보자.
이 데이터는 키 하나로 읽는가, 조건 검색이 필요한가, 관계를 따라가야 하는가?
읽기와 쓰기 중 무엇이 더 많고, 어느 쪽 지연 시간이 더 중요한가?
데이터가 틀렸을 때 사용자 불편으로 끝나는가, 금전·권한·법적 문제로 이어지는가?
중복 저장을 허용한다면 누가, 언제, 어떻게 동기화하고 검증할 것인가?
장애가 났을 때 이 저장소 없이도 서비스가 축소 동작할 수 있는가?
팀이 이 저장소의 백업, 복구, 모니터링, 성능 튜닝을 운영할 수 있는가?
관계형 데이터베이스 하나에 모든 데이터를 넣을 수는 있다. 반대로 여러 저장소를 섞어 모든 문제를 나눌 수도 있다. 좋은 설계는 그 중간에 있다. 정합성이 중요한 핵심 데이터는 관계형 데이터베이스에 두고, 읽기 속도, 관계 탐색, 대량 쓰기처럼 분명한 목적이 있을 때 별도 저장소를 붙인다.
모든 문제를 같은 방식으로 풀 필요는 없다. 그러나 저장소가 늘어날수록 시스템이 지켜야 할 약속도 늘어난다. NoSQL을 잘 쓰는 개발자는 새로운 데이터베이스를 많이 아는 사람이 아니라, 어떤 책임을 데이터베이스에 맡기고 어떤 책임을 애플리케이션과 운영 절차로 감당할지 분명히 정하는 사람이다.
참고
1NoSQL Databases and Their Use Cases - KDnuggets
2Choosing the Right MongoDB Data Model: Embedded vs. Referenced Documents
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.