NoSQL 데이터베이스는 어떻게 확장성과 유연성을 해결하기 위해 등장했나?
왜 NoSQL 데이터베이스가 등장했는지: 웹 규모, 유연한 데이터 요구, 관계형 시스템의 한계—주요 모델과 트레이드오프를 설명합니다.

NoSQL이 해결하려던 문제는 무엇이었나?
NoSQL은 많은 팀이 애플리케이션이 필요로 하는 것과 전통적인 관계형 데이터베이스(SQL 데이터베이스)가 최적화한 것 사이의 불일치에 직면했을 때 등장했습니다. SQL이 “실패”한 것은 아니지만, 웹 규모에서는 일부 팀이 다른 목표를 우선시하기 시작했습니다.
두 가지 압력: 확장과 변화
첫째, 확장(Scale). 인기 있는 소비자용 애플리케이션은 트래픽 급증, 지속적인 쓰기, 막대한 사용자 생성 데이터 볼륨을 겪기 시작했습니다. 이런 워크로드에서 "더 큰 서버를 사는 것"은 비용이 많이 들고 구현이 느리며 궁극적으로 운영할 수 있는 최대 머신에 제한되었습니다.
둘째, 변화(Change). 제품 기능은 빠르게 진화했고, 그 뒤의 데이터는 항상 고정된 테이블 집합에 깔끔하게 맞지 않았습니다. 사용자 프로필에 새로운 속성을 추가하거나 여러 이벤트 타입을 저장하거나 서로 다른 소스에서 반정형 JSON을 받아들이는 일은 반복적인 스키마 마이그레이션과 팀 간 조율을 의미하는 경우가 많았습니다.
관계형 데이터베이스가 특정 경우에 고전한 이유
관계형 데이터베이스는 구조를 강제하고 정규화된 테이블 간 복잡한 쿼리를 가능하게 하는 데 탁월합니다. 그러나 일부 고확장 워크로드에서는 이런 강점이 활용되기 어려웠습니다:
- 많은 테이블에 걸친 동시 쓰기가 경쟁을 일으킬 수 있음.
- 조인 기반의 무거운 쿼리는 데이터가 급격히 증가하면 비용이 커짐.
- 여러 머신으로 수평 확장하는 것은 가능하지만, 모든 곳에서 엄격한 정합성을 유지하면서 운영하는 것은 복잡할 수 있음.
결과적으로 일부 팀은 특정 보장과 기능을 포기하는 대신 더 단순한 스케일링과 빠른 반복을 얻는 시스템을 찾기 시작했습니다.
NoSQL: 하나의 기술이 아니라 접근 방식의 집합
NoSQL은 단일 데이터베이스나 설계가 아닙니다. 수평 확장, 유연한 데이터 모델, 특정 애플리케이션 요구에 맞춘 접근 패턴을 강조하는 시스템들에 대한 포괄적 용어입니다.
- 수평 확장(머신 추가)
- 유연한 데이터 모델
- 애플리케이션 접근 패턴에 맞춘 설계
기대치의 재설정
NoSQL은 결코 SQL의 보편적 대체물이 되려 한 것은 아닙니다. 이는 트레이드오프의 집합입니다: 확장성이나 스키마 유연성을 얻을 수 있지만, 약한 정합성 보장, 제한된 애드혹 쿼리 옵션, 애플리케이션 레벨의 데이터 모델링 책임 증가를 감수할 수 있습니다.
전통적 확장이 무너지기 시작한 이유
수년 동안 느린 데이터베이스에 대한 표준 답은 간단했습니다: 더 큰 서버를 사라. CPU를 늘리고, 메모리를 늘리고, 더 빠른 디스크를 넣고 동일한 스키마와 운영 모델을 유지하는 것. 이 "수직 확장" 접근법은 작동했지만, 더 이상 실용적이지 않을 때가 왔습니다.
수직 확장이 맞닥뜨린 한계
고급 머신은 빠르게 비싸지고 비용/성능 곡선은 결국 불리해집니다. 업그레이드는 종종 큰 예산 승인과 데이터 이동 및 전환을 위한 유지보수 창을 요구합니다. 설령 더 큰 하드웨어를 감당할 수 있다 해도, 단일 서버는 여전히 한계가 있습니다: 하나의 메모리 버스, 하나의 저장 서브시스템, 그리고 쓰기 부하를 흡수하는 하나의 주 노드.
성장으로 인해 워크로드의 형태가 바뀌었다
제품이 성장함에 따라 데이터베이스는 간헐적인 피크가 아닌 지속적인 읽기/쓰기 압력을 받았습니다. 트래픽은 24/7이 되었고, 특정 기능은 불균등한 접근 패턴을 만들었습니다. 소수의 자주 접근되는 행이나 파티션이 트래픽을 독점해 "핫 테이블(또는 핫 키)"을 만들어 다른 모든 것을 끌어내릴 수 있었습니다.
운영 병목은 흔해졌습니다:
- 새로운 기능이 요구하는 보조 인덱스의 팽창
- 동일 테이블로 집중되는 많은 동시 쓰기에서의 경쟁
- 부하 상태에서 예측 불가능해지는 지연을 만드는 락 대기
- 데이터셋 증가로 인한 복제 지연과 느린 페일오버
더 큰 서버가 전 세계 가용성을 해결하지는 못했다
많은 애플리케이션은 한 리전에서 빠르기만 해서는 안 되고 여러 지역에 걸쳐 가용해야 했습니다. 한 리전에 "메인" 데이터베이스가 있으면 먼 사용자에겐 지연이 발생하고 장애가 더 치명적입니다. 질문은 "더 큰 박스를 어떻게 사냐"에서 "데이터베이스를 여러 머신과 위치에 걸쳐 어떻게 운영하냐"로 바뀌었습니다.
유연한 데이터 모델의 필요성
관계형 데이터베이스는 데이터 형태가 안정적일 때 빛을 발합니다. 그러나 많은 현대 제품은 고정되어 있지 않습니다. 테이블 스키마는 의도적으로 엄격합니다: 모든 행이 동일한 컬럼, 타입, 제약을 따릅니다. 그 예측 가능성은 가치가 있지만, 빠르게 반복할 때는 문제입니다.
엄격한 스키마와 변화의 실제 비용
실무에서 빈번한 스키마 변경은 비용이 큽니다. 겉보기엔 작은 업데이트라도 마이그레이션, 백필, 인덱스 업데이트, 이전 코드 경로가 깨지지 않도록 하는 호환성 계획, 그리고 조정된 배포 시간을 요구할 수 있습니다. 대형 테이블에서는 컬럼을 추가하거나 타입을 바꾸는 것조차 시간이 많이 드는 작업이 되어 실제 운영 리스크를 수반할 수 있습니다.
그 마찰은 팀이 변경을 미루거나 임시방편을 쌓게 하거나 텍스트 필드에 지저분한 블롭을 저장하게 만듭니다—이들 모두 빠른 반복에 이상적이지 않습니다.
반정형 데이터는 제품이 진화하는 방식에 맞는다
많은 애플리케이션 데이터는 자연스럽게 반정형입니다: 중첩 객체, 선택적 필드, 시간이 지남에 따라 진화하는 속성들.
예를 들어, "사용자 프로필"은 이름과 이메일로 시작해 선호 설정, 연결된 계정, 배송 주소, 알림 설정, 실험 플래그 등으로 확장될 수 있습니다. 모든 사용자에게 모든 필드가 있는 것은 아니고, 새로운 필드는 점진적으로 추가됩니다. 도큐먼트 스타일 모델은 각 레코드가 동일한 엄격한 템플릿을 따르지 않아도 중첩되고 불균일한 형태를 직접 저장할 수 있습니다.
더 빠른 반복, 어색한 조인의 감소
유연성은 특정 데이터 형태에서 복잡한 조인의 필요를 줄이기도 합니다. 하나의 화면이 주문과 상품, 배송 정보, 상태 히스토리를 한 번에 필요로 할 때 관계형 설계는 여러 테이블과 조인을 요구합니다. ORM 계층이 그 복잡성을 숨기려 하지만 종종 마찰을 더합니다.
NoSQL 옵션은 애플리케이션이 읽고 쓰는 방식에 더 가깝게 데이터를 모델링하기 쉽게 만들어 팀이 더 빠르게 변경을 배포하도록 도왔습니다.
웹 규모 변화가 데이터베이스 요구를 어떻게 바꿨나
웹 애플리케이션은 단지 더 커진 것이 아니라 형태가 바뀌었습니다. 예전처럼 근무시간에 제한된 내부 사용자 수를 상대하는 대신, 제품은 전 세계 수백만 사용자를 24시간 제공하게 되었고 출시, 뉴스, 소셜 공유로 인해 갑작스러운 스파이크가 발생했습니다.
항상 가동되는 기대치는 기준을 올렸습니다: 다운타임은 불편함이 아니라 뉴스가 되었습니다. 동시에 팀은 더 빠르게 기능을 출시하라는 요구를 받았고—종종 "최종" 데이터 모델이 무엇인지 모르는 상태에서 기능을 먼저 내놓아야 했습니다.
분산은 성장을 위한 기본 경로가 되었다
따라서 단일 데이터베이스 서버를 수직 확장하는 것으로는 충분하지 않았습니다. 처리량이 늘어날수록 점진적으로 용량을 추가할 수 있기를 원했습니다—노드를 하나 더 추가하고, 부하를 분산하고, 장애를 격리하는 식으로.
이것은 아키텍처를 하나의 "메인" 박스가 아닌 머신 군집(fleet) 쪽으로 밀어냈고, 팀이 데이터베이스에 기대하는 것도 바꿨습니다: 단지 올바름뿐 아니라 높은 동시성에서의 예측 가능한 성능과 시스템 일부가 건강하지 않을 때의 우아한 동작.
데이터베이스가 따라오기 전 팀이 채택한 패턴
"NoSQL"이 주류 범주가 되기 전부터 많은 팀은 이미 웹 규모 현실에 맞춰 시스템을 구부려 왔습니다:
- 캐싱 계층(종종 인메모리)으로 반복 읽기 감소
- **비정규화(denormalization)**로 비용이 큰 조인 회피 및 왕복 수 감소
- 피드, 타임라인, 대시보드를 위한 사전 계산된 뷰와 머티리얼라이즈된 롤업
이 기법들은 작동했지만 복잡성을 애플리케이션 코드로 이동시켰습니다: 캐시 무효화, 중복 데이터의 일관성 유지, "서비스 가능한" 레코드를 위한 파이프라인 구축.
이것이 데이터베이스를 진화시키도록 강제한 방법
이러한 패턴이 표준이 되면서 데이터베이스는 데이터를 여러 머신에 분산하고 부분 실패를 견디며 높은 쓰기량을 처리하고 진화하는 데이터를 깔끔하게 표현할 수 있어야 했습니다. NoSQL 데이터베이스는 흔히 이러한 웹 규모 전략을 상시적 작업이 아니라 일급 시민으로 만들기 위해 등장했습니다.
분산의 트레이드오프와 CAP 정리
데이터가 한 머신에 있을 때 규칙은 단순해 보입니다: 단일 진실 소스가 있고 모든 읽기/쓰기는 즉시 확인될 수 있습니다. 데이터를 서버들에 분산하면 메시지가 지연될 수 있고 노드가 실패할 수 있으며 시스템 일부가 일시적으로 통신을 멈출 수 있다는 현실이 나타납니다.
핵심 분산 트레이드오프(쉽게 말하면)
분산 데이터베이스는 안전하게 조정할 수 없을 때 무엇을 할지 결정해야 합니다. 앱이 "가동" 상태를 유지하도록 요청을 계속 처리하고 결과가 약간 오래된 것을 허용할 것인가? 아니면 복제본이 합의할 때까지 일부 작업을 거부해 사용자에게는 다운타임처럼 보이는 동작을 선택할 것인가?
이런 상황은 라우터 장애, 네트워크 과부하, 롤링 배포, 방화벽 오작동, 크로스리전 복제 지연 등에서 발생합니다.
CAP을 한 컷으로: C, A, P
CAP 정리는 동시에 원할 수 있는 세 가지 속성을 간단히 요약합니다:
- 정합성(Consistency, C): 모든 읽기는 최신 쓰기(또는 오류)를 반환합니다. 실무적으로는 "모두가 지금 같은 답을 본다"는 의미입니다.
- 가용성(Availability, A): 모든 요청이 응답을 받습니다(반드시 최신 데이터는 아님).
- 파티션 허용성(Partition Tolerance, P): 네트워크가 분리되어도 시스템이 동작을 계속합니다.
핵심은 "항상 둘을 골라라"가 아니라: 네트워크 분할이 발생하면 정합성과 가용성 중 하나를 선택해야 한다는 점입니다. 웹 규모 시스템에서는 파티션이 불가피하다고 간주됩니다—특히 다중 리전 설정에서는 더욱 더.
파티션은 실제 장애와 직접적으로 연결된다
예를 들어 애플리케이션이 내구성을 위해 두 리전에서 동작한다고 가정합시다. 광케이블 절단이나 라우팅 문제로 동기화가 막힐 수 있습니다.
- 가용성 우선이면 두 리전 모두 쓰기를 계속 받아들이고 데이터는 일시적으로 분기될 수 있습니다.
- 정합성 우선이면 한 리전은 복제본 합의가 될 때까지 쓰기(또는 읽기)를 거부할 수 있습니다.
서로 다른 NoSQL 시스템(혹은 같은 시스템의 서로 다른 설정)은 무엇이 가장 중요한지에 따라 서로 다른 타협을 합니다: 장애 시 사용자 경험, 정합성 보장, 운영 단순성, 회복 동작 등.
스케일 아웃: 샤딩과 복제는 핵심 아이디어
스케일 아웃(수평 확장)은 더 큰 서버를 사는 대신 더 많은 머신(노드)을 추가해 용량을 늘리는 것을 의미합니다. 많은 팀에게 이는 재정적·운영적 전환이었습니다: 범용 노드를 점진적으로 추가할 수 있고, 실패는 예상되며, 성장은 위험한 "빅 박스" 마이그레이션을 요구하지 않습니다.
샤딩(파티셔닝): 작업을 분산하기
여러 노드를 유용하게 만들려면 NoSQL 시스템은 샤딩(파티셔닝)에 의존했습니다. 모든 요청을 하나의 데이터베이스가 처리하는 대신 데이터를 파티션으로 나누어 노드에 분산합니다.
간단한 예로 키(예: user_id)로 파티셔닝하면:
- 노드 A는 사용자 1–1,000,000을 저장
- 노드 B는 사용자 1,000,001–2,000,000을 저장
읽기와 쓰기가 분산되어 핫스팟을 줄이고 노드를 추가함에 따라 처리량이 증가합니다. 파티션 키는 설계상의 결정이 됩니다: 쿼리 패턴에 맞는 키를 고르지 않으면 트래픽이 한 샤드로 쏠릴 수 있습니다.
복제: 가용성과 읽기 확장
복제는 동일한 데이터를 여러 노드에 유지하는 것을 의미합니다. 이것은 다음을 개선합니다:
- 가용성: 한 노드가 실패하면 다른 복제본이 요청을 처리할 수 있음.
- 읽기 용량: 여러 복제본에서 읽기를 분산할 수 있음.
복제는 또한 랙이나 리전에 데이터를 분산해 국지적 장애를 버티도록 합니다.
숨겨진 비용: 리밸런싱과 운영
샤딩과 복제는 지속적인 운영 작업을 초래합니다. 데이터가 증가하거나 노드가 변하면 시스템은 리밸런싱을 수행해야 합니다—파티션을 이동하면서 온라인 상태를 유지해야 합니다. 잘못 처리되면 리밸런싱은 지연 스파이크, 불균형한 부하, 일시적 용량 부족을 초래할 수 있습니다.
이것이 핵심 트레이드오프입니다: 더 많은 노드로 저렴하게 확장하는 대신 분산, 모니터링, 장애 처리의 복잡성이 늘어납니다.
정합성 모델: 엄격함에서 최종으로
데이터가 분산되면 데이터베이스는 업데이트가 동시에 발생하거나 네트워크가 느려지거나 노드가 통신할 수 없을 때 무엇이 "올바른"지 정의해야 합니다.
엄격한(강한) 정합성
강한 정합성에서는 쓰기가 확인되면 모든 읽기가 즉시 그 쓰기를 보여야 합니다. 이는 많은 사람들이 관계형 데이터베이스와 연관지어 생각하는 "단일 진실의 원천" 경험과 일치합니다.
문제는 조정입니다: 노드 간에 엄격한 보장을 하려면 여러 메시지를 주고받고 충분한 응답을 기다려야 하며, 중간 실패를 처리해야 합니다. 노드가 멀리 떨어져 있거나 바쁠수록(또는 둘 다) 쓰기마다 지연을 도입할 수 있습니다.
최종(이벤트얼) 정합성
최종 정합성은 그 보장을 완화합니다: 쓰기 후 서로 다른 노드가 잠시 다른 답을 반환할 수 있지만 시간이 지나면 시스템은 수렴합니다.
예시:
- 어떤 복제본에서는 좋아요가 101로 보이는데 다른 곳은 100으로 몇 초 동안 다를 수 있습니다.
- 새로운 게시물이 일부 사용자에게는 먼저 보이고 다른 사용자에게는 지연될 수 있습니다(특히 리전 간).
많은 사용자 경험에서 그런 일시적 불일치는 시스템이 빠르고 사용 가능성을 유지한다면 허용됩니다.
충돌과 해결 방법
두 복제본이 거의 동시에 업데이트를 수용하면 데이터베이스는 병합 규칙이 필요합니다.
일반적 접근법:
- 타임스탬프(마지막 쓰기 승리): 최신 타임스탬프의 업데이트를 채택. 단순하지만 시계 차이나 "최신"이 의미하는 바가 항상 옳지 않을 수 있음.
- 버전 벡터(개념적): 어떤 복제본이 어떤 업데이트를 봤는지 추적해 동시성을 감지하고 병합하거나 충돌을 노출.
강한 정합성이 여전히 중요한 곳
강한 정합성은 일반적으로 금전 이체, 재고 한도, 고유 사용자명, 권한 등 "잠깐의 두 진실"이 실제 피해를 초래할 수 있는 워크플로우에 대해 비용을 들일 가치가 있습니다.
주요 NoSQL 데이터베이스 계열(그리고 무엇을 최적화했나)
NoSQL은 확장성, 지연, 데이터 형태에 대해 서로 다른 트레이드오프를 하는 모델들의 집합입니다. "계열"을 이해하면 무엇이 빠르고 무엇이 고통스러울지, 그리고 그 이유를 예측하는 데 도움이 됩니다.
키-값 스토어: 단순성으로 인한 속도
키-값 데이터베이스는 값을 고유 키 뒤에 저장하는 거대한 분산 해시맵처럼 동작합니다. 접근 패턴이 주로 "키로 가져오기(get) / 키로 설정(set)"이므로 매우 빠르고 수평 확장이 용이합니다.
세션, 캐싱, 피처 플래그 등 이미 조회 키를 알고 있는 경우에 적합하지만, 여러 필드에 걸친 필터링 같은 애드혹 쿼리에는 적합하지 않습니다.
도큐먼트 데이터베이스: 유연한 레코드, JSON 유사 형태
도큐먼트 데이터베이스는 JSON 유사 문서를(종종 컬렉션으로 그룹화) 저장합니다. 각 문서는 약간 다른 구조를 가질 수 있어 제품이 진화할 때 스키마 유연성을 지원합니다.
이들은 전체 문서의 읽기/쓰기에 최적화되어 있고 문서 내부의 필드로 쿼리할 수 있게 해줍니다—강제된 엄격한 테이블 구조를 요구하지 않습니다. 트레이드오프는 관계 모델링이 까다로워지고(있다면) 조인이 관계형 시스템만큼 강력하지 않을 수 있다는 점입니다.
와이드-컬럼 스토어: 거대한 규모에서의 높은 쓰기 처리량
와이드-컬럼 데이터베이스(빅테이블의 영감을 받은)는 행 키별로 데이터를 조직하고 행마다 달라질 수 있는 많은 컬럼을 가집니다. 이들은 막대한 쓰기율과 분산 저장에 강해 시계열, 이벤트, 로그 작업에 적합합니다.
일반적으로 접근 패턴에 따라 신중한 설계를 요구합니다: 기본 키와 클러스터링 규칙으로 효율적으로 쿼리하되 임의의 필터링은 피하는 식입니다.
그래프 데이터베이스: 관계 중심 쿼리
그래프 데이터베이스는 관계를 일급 시민으로 취급합니다. 반복적으로 테이블을 조인하는 대신 노드 사이의 엣지를 따라 탐색하므로 "이것들이 어떻게 연결되어 있나?" 같은 쿼리가 자연스럽고 빠릅니다(사기 고리, 추천, 의존성 그래프).
간단 가이드: 각 모델이 가장 적합한 경우
- 키-값: ID로 가장 빠른 조회가 필요할 때; 캐싱, 세션, 카운터
- 도큐먼트: 진화하는 제품 데이터; 프로필, 카탈로그, 콘텐츠
- 와이드-컬럼: 대규모 수집 및 쓰기; 텔레메트리, 로그, 시계열
- 그래프: 깊은 관계 탐색; 소셜 그래프, 라우팅, 사기 분석
데이터 모델링 변화: 조인 축소, 더 의도적인 설계
관계형 데이터베이스는 정규화를 장려합니다: 데이터를 여러 테이블로 분리하고 쿼리 시 조인으로 재조립합니다. 많은 NoSQL 시스템은 핵심 접근 패턴을 중심으로 설계하도록 요구합니다—때로는 중복을 대가로 하여 레이턴시를 예측 가능하게 만듭니다.
왜 비정규화가 흔한가
분산 데이터베이스에서는 조인이 여러 파티션이나 머신에서 데이터를 가져와야 할 수 있습니다. 이는 네트워크 왕복, 조정, 예측 불가능한 지연을 추가합니다. 비정규화(관련 데이터를 함께 저장)는 왕복을 줄이고 읽기를 가능한 한 로컬로 유지합니다.
실용적 결과: customers에도 고객 이름이 있는데도 orders 레코드에 고객 이름을 저장할 수 있습니다. "최근 20건 주문"이 핵심 쿼리라면 이것이 한 번의 빠른 읽기로 해결되기 때문입니다.
쿼리 제약: 조인 감소, 앱 내부의 더 많은 모델링
많은 NoSQL 데이터베이스는 제한된 조인(또는 없음)을 지원하므로 애플리케이션이 더 많은 책임을 집니다:
- 키로 문서/행을 가져와 바로 렌더링
- 두 데이터셋을 별도로 읽어 코드에서 병합
- 비용이 큰 스캔을 피하기 위해 사전 계산된 "뷰" 데이터(카운트, 요약)를 유지
그래서 NoSQL 모델링은 종종 "우리가 어떤 화면을 로드해야 하나?"와 "빠르게 만들어야 할 상위 쿼리는 무엇인가?"로 시작합니다.
보조 인덱스—숨겨진 비용
보조 인덱스는 새로운 쿼리(예: 이메일로 사용자 찾기)를 가능하게 하지만 공짜가 아닙니다. 분산 시스템에서 각 쓰기는 여러 인덱스 구조를 업데이트할 수 있어 다음을 초래합니다:
- 쓰기 증폭(write amplification): 하나의 논리적 쓰기가 여러 물리적 쓰기로 변함
- 추가 저장소 비용: 인덱스 항목이 데이터 크기와 맞먹을 수 있음
- 운영 복잡성: 인덱스 지연이나 세심한 튜닝 필요
성능을 개선하는 모델링 선택 예시
- 참조보다 포함(Embed): 주문 문서 안에 주문 항목을 넣어 한 요청으로 주문 전체를 읽게 함
- 시계열 데이터 버킷화: 장기적 파티션을 피하기 위해 장치별/일별 이벤트를 유지
- 읽기 모델 물리화(Materialize read models): 프로필 페이지를 서빙하기 위해
user_profile_summary레코드를 유지
팀들이 수용한 이득과 트레이드오프
NoSQL이 채택된 이유는 모든 면에서 "더 낫기" 때문이 아닙니다. 웹 규모 압박에서 속도, 스케일, 유연성을 얻기 위해 관계형 데이터베이스의 몇몇 편의성을 기꺼이 포기했기 때문입니다.
팀들이 얻은 것
기본 설계로서의 스케일 아웃. 많은 NoSQL 시스템은 머신 추가로 확장하는 것을 실용적으로 만들었습니다. 샤딩과 복제는 사후 추가 기능이 아니라 핵심 기능이었습니다.
유연한 스키마. 도큐먼트와 키-값 시스템은 애플리케이션이 매주 요구가 바뀌어도 모든 필드 변경을 엄격한 테이블 정의를 통해 라우팅하지 않아도 되게 했습니다.
고가용성 패턴. 노드와 리전 간 복제는 하드웨어 장애나 유지보수 동안 서비스를 계속 운영하기 쉽게 했습니다.
팀들이 지불한 대가
데이터 중복과 비정규화. 조인을 피하려면 데이터 중복이 필요합니다. 이는 읽기 성능을 개선하지만 저장소를 늘리고 "모든 곳에서 업데이트"해야 하는 복잡성을 도입합니다.
정합성의 놀라움. 최종 정합성은 받아들일 수 있지만—허용되지 않을 때는 문제가 됩니다. 사용자는 오래된 데이터를 보거나 혼란스러운 엣지 케이스를 볼 수 있으며 애플리케이션은 이를 견디거나 해결하도록 설계되어야 합니다.
복잡한 분석이 더 어려움(때로는). 일부 NoSQL 저장소는 운영 읽기/쓰기에 탁월하지만 애드혹 쿼리, 리포팅, 복잡한 집계를 SQL 중심 시스템보다 더 번거롭게 만들 수 있습니다.
운영과 툴링이 중요한 이유
초기 NoSQL 도입은 종종 데이터베이스 기능에서 엔지니어링 규율로의 노력을 이동시켰습니다: 복제 모니터링, 파티션 관리, 컴팩션 운영, 백업/복구 계획, 장애 시나리오 부하 테스트. 운영 성숙도가 높은 팀이 가장 큰 혜택을 보았습니다.
트레이드오프를 평가하는 방법
예상 지연, 최고 처리량, 지배적 쿼리 패턴, 오래된 읽기에 대한 관용도, 복구 요구(RPO/RTO)에 따라 선택하세요. "올바른" NoSQL 선택은 보통 애플리케이션이 어떻게 실패하고 확장하며 쿼리되어야 하는지를 잘 맞추는 도구입니다—가장 멋진 체크리스트가 있는 도구가 아닙니다.
오늘날 NoSQL이 적합한지 결정하는 방법
NoSQL 선택은 데이터베이스 브랜드나 과대광고로 시작해서는 안 됩니다—애플리케이션이 무엇을 해야 하고 어떻게 성장할지, 사용자에게 "정확함"이 무엇을 의미하는지에서 시작해야 합니다.
요구사항과 접근 패턴으로 시작하라
데이터 저장소를 고르기 전에 다음을 적어보세요:
- 지원해야 할 상위 5–10개 쿼리/연산(읽기, 쓰기, 검색, 집계)
- 현재와 12–24개월 후의 예상 트래픽
- 오래된 데이터에 대한 관용(밀리초, 초, 절대 불허)
- 장애 시 기대 동작(노드 또는 리전이 다운되면 어떻게 할 것인가?)
접근 패턴을 명확히 설명할 수 없으면 어떤 선택이라도 추측이 됩니다—특히 NoSQL에서는 모델링이 읽기/쓰기 방식에 의해 형성되기 때문입니다.
간단한 결정 체크리스트(SQL vs NoSQL vs 하이브리드)
빠른 필터로 사용하세요:
- SQL 선택: 기본적으로 강한 정합성이 필요하고, 복잡한 애드혹 쿼리와 조인이 많은 경우.
- NoSQL 선택: 특정 접근 패턴에 대해 쉬운 수평 확장이 필요하고, 그 패턴에 맞춰 데이터를 설계할 수 있으며 일부 워크플로우에서 완화된 정합성을 수용할 수 있을 때.
- 하이브리드 선택: 앱의 다른 부분이 서로 다른 요구를 가질 때(실제 제품에서 흔함).
실용적 신호: 핵심 진실(주문, 결제, 재고)이 항상 정확해야 한다면 그 부분은 SQL이나 다른 강한 정합성 저장소에 두세요. 고부하 콘텐츠, 세션, 캐시, 활동 피드, 유연한 사용자 생성 데이터는 NoSQL이 잘 맞습니다.
의도적 폴리글롯 지속성 고려하기
많은 팀은 여러 저장소를 함께 사용해 성공합니다: 예를 들어 트랜잭션용 SQL, 프로필/콘텐츠용 도큐먼트 DB, 세션용 키-값 스토어. 목적은 복잡성 자체가 아니라—각 워크로드를 깔끔하게 처리하는 도구에 맞추는 것입니다.
이것이 개발자 워크플로가 중요한 지점이기도 합니다. 아키텍처(SQL vs NoSQL vs 하이브리드)를 실험할 때 작업하는 프로토타입(API, 데이터 모델, UI)을 빠르게 띄울 수 있으면 결정 위험을 줄일 수 있습니다. Koder.ai 같은 플랫폼은 채팅으로 풀스택 앱을 생성해 React 프런트엔드와 Go + PostgreSQL 백엔드를 제공하고 소스 코드를 내보낼 수 있게 해 실험을 더 안전하고 빠르게 합니다. 나중에 특정 워크로드에 NoSQL 저장소를 도입하더라도 강한 SQL "진실의 시스템"과 빠른 프로토타이핑, 스냅샷, 롤백은 실험을 더 안전하게 만듭니다.
의견이 아니라 테스트로 검증하라
무엇을 택하든 증명하세요:
- 현실적인 쿼리와 데이터 크기로 부하 테스트를 수행하세요.
- **장애 연습(failure drills)**을 하세요(노드 종료, 네트워크 분할 시뮬레이션, 복구 테스트).
- 스키마 진화 계획을 만드세요: 필드 추가, 레코드 마이그레이션, 롤아웃 동안 구버전/신버전 호환성 유지 방법.
이 시나리오를 테스트할 수 없다면 데이터베이스 결정은 이론에 머물고 실제 운영 환경이 대신 테스트를 수행하게 될 것입니다.
자주 묻는 질문
NoSQL은 본래 무엇을 해결하려 했나?
NoSQL은 두 가지 공통 압박을 해결하려고 등장했습니다:
- 확장(Scale): 높은 쓰기량, 트래픽 급증, 단일 "더 큰 서버"로는 감당하기 어려운 데이터셋.
- 변화(Change): 제품 요구사항이 빠르게 바뀌어 관계형 스키마 마이그레이션이 자주 발생하고 비용·위험이 큼.
SQL이 본질적으로 잘못됐기 때문이 아니라, 다른 워크로드가 다른 트레이드오프를 우선시했기 때문입니다.
단일 관계형 데이터베이스 서버의 스케일링이 왜 무너지기 시작했나?
전통적인 "스케일 업" 방식이 맞닥뜨린 현실적 한계:
- 고성능 하드웨어는 급격히 비싸고 업그레이드는 중단을 동반할 수 있음.
- 단일 머신은 쓰기·디스크·페일오버의 병목이 됨.
- 데이터베이스 주 노드가 한 리전에 있으면 전 세계 사용자에게 지연이 발생.
NoSQL은 노드 추가로 스케일 아웃하는 방식을 채택해 이러한 문제를 완화했습니다.
현대 애플리케이션에 있어 엄격한 스키마가 왜 문제였나?
관계형 스키마는 의도적으로 엄격해서 안정성에는 유리하지만 빠른 반복에선 문제를 일으킵니다. 대형 테이블에서의 작은 변경도 다음을 요구할 수 있습니다:
- 마이그레이션과 백필(backfill)
- 인덱스 업데이트
- 팀 간 조정된 배포
- 다운타임 위험 또는 긴 유지보수 창
도큐먼트형 모델은 선택적 필드와 점진적 확장을 허용해 이런 마찰을 줄입니다.
NoSQL은 단지 수평 확장(스케일 아웃)만을 의미하나?
반드시 그런 것은 아닙니다. 많은 SQL 데이터베이스도 수평 확장이 가능하지만 운영적으로 복잡할 수 있습니다(샤딩 전략, 샤드 간 조인, 분산 트랜잭션 등).
NoSQL 시스템은 분할(partitioning)과 복제(replication)를 설계의 핵심으로 삼아 특정 접근 패턴에 대해 더 단순하고 예측 가능한 분산 동작을 제공하는 경우가 많습니다.
NoSQL 설계에서 왜 중복 저장과 조인 감소를 자주 사용하나?
데이터를 읽는 형태로 데이터를 저장해 조인을 피하기 위해 중복 저장(denormalization)을 자주 사용합니다.
예: orders 레코드 안에 고객 이름을 넣어 "최근 20건 주문" 쿼리를 단번에 해결하도록 하는 방식.
대신 업데이트 복잡성이 증가합니다. 중복된 데이터를 일관되게 유지하려면 애플리케이션 로직이나 파이프라인이 필요합니다.
CAP 정리는 NoSQL에 대해 실제로 무엇을 의미하나?
분산 환경에서 네트워크 분할(파티션)이 발생하면 데이터베이스는 다음 중 하나를 선택해야 합니다:
- 가용성 우선: 요청을 계속 처리하지만 오래된(또는 일시적으로 다른) 데이터를 반환할 수 있음.
- 정합성 우선: 복제본들이 합의할 때까지 일부 작업을 거부해 사용자 입장에선 가용성이 떨어지는 것처럼 보일 수 있음.
CAP은 파티션 발생 시 완전한 정합성과 완전한 가용성을 동시에 보장할 수 없다는 점을 상기시켜 줍니다.
강한 정합성과 최종 정합성의 차이는 무엇인가?
**강한 정합성(Strong consistency)**은 쓰기가 확인된 후 모든 리더가 즉시 그 쓰기를 보게 함—분산 환경에선 여러 노드 간 조정이 필요합니다.
**점진적(최종) 정합성(Eventual consistency)**은 쓰기 후 일시적으로 복제본 간 불일치가 발생할 수 있지만 시간이 지나면 수렴합니다. 피드, 카운터 등에서 잠깐의 비동기성이 허용될 때 유용합니다.
NoSQL 데이터베이스는 충돌 쓰기를 어떻게 처리하나?
충돌은 서로 다른 복제본이 거의 동시에 업데이트를 수용할 때 발생합니다. 일반적 처리법은:
- 최신 쓰기 승리(타임스탬프 기반): 최신 타임스탬프를 가진 업데이트를 채택(단순하지만 데이터 손실 우려).
- 버전 벡터 등 버전 관리: 동시성을 감지해 병합하거나 충돌을 노출.
어떤 전략을 쓸지는 중간 업데이트 손실이 허용되는지 여부에 따라 달라집니다.
키-값, 도큐먼트, 와이드-컬럼, 그래프 데이터베이스 중 어떻게 선택하나?
간단한 적합 안내:
- 키-값(Key-value): 키로 빠르게 조회해야 할 때(세션, 캐시, 피처 플래그).
- 도큐먼트(Document): 진화하는 JSON 형태의 레코드(프로필, 카탈로그, 콘텐츠).
- 와이드-컬럼(Wide-column): 대규모 쓰기 처리(이벤트, 로그, 시계열).
- 그래프(Graph): 관계 탐색 쿼리(추천, 사기 분석, 의존성 그래프).
우선순위는 지배적인 접근 패턴(주요 쿼리)에 맞추세요.
지금 내 시스템에 NoSQL이 적합한지 어떻게 알 수 있나?
요구사항에서 출발해 테스트로 검증하세요:
- 상위 5–10개의 주요 연산을 적으세요(읽기, 쓰기, 검색, 집계).
- 12–24개월 내 예상 트래픽을 정의하세요.
- 데이터의 오래된 읽기(stale) 허용 범위를 정하세요(밀리초, 초, 절대 불허).
- 노드/리전 손실 시 동작(복구 목표 RPO/RTO)을 정하세요.
많은 시스템은 하이브리드를 택합니다: 트랜잭션과 핵심 진실은 SQL에, 대용량·유연한 데이터는 NoSQL에 맡기는 식입니다.