ByteByteGo의 영상을 기반으로 Claude, Gemini 와 같이 추가 공부한 내용을 정리했습니다.
키-값 저장소는 어떻게 작동할까?
쇼핑카트에서 시작된 복잡한 분산 시스템 이야기
온라인 쇼핑을 할 때 장바구니에 물건을 담으면, 그 정보는 어디에 저장될까요? 채팅 메시지를 보내면 어떻게 상대방에게 정확히 전달될까요? 이런 일상적인 기능들 뒤에는 키-값 저장소(Key-Value Store)라는 데이터베이스가 숨어있습니다.

키-값 저장소란?
키-값 저장소는 거대한 사전과 같습니다. 사전에서 단어(키)를 찾으면 그 뜻(값)을 얻는 것처럼, 키-값 저장소에서는 특정 키로 데이터를 저장하고 찾을 수 있습니다.
예를 들어보겠습니다:
- 키: user12345_cart
- 값: 사용자의 장바구니에 담긴 모든 상품 정보
이렇게 간단해 보이는 개념이 실제로는 어떤 복잡한 문제들을 만들어낼까요?
규모의 문제: 한 대의 컴퓨터로는 부족하다
아마존 같은 대형 서비스를 생각해보세요. 전 세계 수억 명의 사용자가 동시에 쇼핑을 하고, 각 지역마다 테라바이트 단위의 데이터가 저장됩니다. 수십억 개의 키-값 쌍이 초당 수백만 번씩 접근됩니다.
당연히 한 대의 컴퓨터로는 감당할 수 없습니다. 수천 대의 서버에 데이터를 분산해서 저장해야 합니다. 하지만 여기서 새로운 문제가 생깁니다.
첫 번째 도전: 어느 서버에 데이터가 있을까?
사용자가 user12345_cart 정보를 요청했을 때, 수천 대의 서버 중 어느 서버에 그 데이터가 있는지 어떻게 알 수 있을까요?
단순한 해시 방법의 한계
처음에는 간단한 방법을 생각할 수 있습니다:
- 키를 해시 함수에 넣어서 숫자로 변환
- 서버 개수로 나눈 나머지를 구함
- 그 나머지에 해당하는 서버에 저장
예를 들어, 서버가 3대라면:
- user12345_cart의 해시값이 157이라면
- 157 ÷ 3 = 52 나머지 1
- 1번 서버에 저장

하지만 서버를 하나 추가하면 어떻게 될까요? 갑자기 모든 계산이 바뀝니다. 4로 나누게 되면서 거의 모든 데이터가 다른 서버로 이동해야 합니다. 이는 엄청난 비용과 시간이 듭니다.

일관된 해싱
일관된 해싱(Consistent Hashing)은 이 문제를 해결하는 영리한 방법입니다. 시계를 상상해보세요. 12시간 대신 수백만 개의 위치가 있는 거대한 원형 시계입니다.
이 원 위에 서버들을 무작위 위치에 배치합니다:
- 서버 A: 위치 100
- 서버 B: 위치 1,000
- 서버 C: 위치 5,000
이제 user12345_cart를 저장하려면:
- 키를 해시해서 원 위의 위치를 구합니다 (예: 750)
- 그 위치에서 시계방향으로 돌면서 첫 번째 서버를 찾습니다
- 서버 B(위치 1,000)에 데이터를 저장합니다
새 서버 D를 위치 500에 추가하면 어떻게 될까요? 위치 101부터 500까지의 키들만 서버 D로 이동하면 됩니다. 나머지 데이터는 그대로 유지됩니다. 이렇게 데이터 이동을 최소화할 수 있습니다.

여기서 한 걸음 더 나아가, 실제 시스템에서는 하나의 서버가 원 위의 여러 위치를 담당하는 '가상 노드' 개념을 사용하기도 합니다. 이렇게 하면 소수의 서버만으로도 데이터가 전체 원에 훨씬 더 균일하게 분포되어 특정 서버에 부하가 몰리는 것을 막아줍니다.
두 번째 도전: 서버가 고장나면?
서버 B가 갑자기 고장나면 해당 데이터에 접근할 수 없게 됩니다. 이를 해결하기 위해 복제(Replication)를 사용합니다.
각 데이터를 여러 서버에 복사해서 저장합니다. user12345_cart를 서버 B에 저장할 때, 시계방향으로 다음 두 서버(서버 C, 서버 A)에도 복사본을 저장합니다. 이제 서버 B가 고장나도 데이터에 접근할 수 있습니다.

세 번째 도전: 일관성 문제
복제는 새로운 문제를 만듭니다. 가족 공용 계정으로 두 사람이 동시에 장바구니에 물건을 담는다고 상상해보세요. 두 요청이 서로 다른 서버에 도착하면, 같은 장바구니의 서로 다른 버전이 생깁니다. 어느 것이 정확한 버전일까요?
CAP 정리: 완벽함의 불가능성
분산 시스템에서는 다음 세 가지를 모두 완벽하게 가질 수 없습니다:
- 일관성(Consistency): 모든 서버가 항상 최신 데이터를 가짐
- 가용성(Availability): 시스템이 항상 응답함
- 분할 내성(Partition Tolerance): 네트워크 문제가 있어도 작동함
더 정확히 말하면, 분산 시스템은 여러 서버로 이루어져 있기에 서버 간 통신이 끊어지는 네트워크 분할(Partition Tolerance) 상황을 피할 수 없습니다.
따라서 엔지니어는 네트워크 분할이 발생했을 때 일관성(Consistency)과 가용성(Availability) 중 무엇을 우선할지 선택해야만 합니다.
은행은 일관성을 선택합니다. 잘못된 계좌 잔고를 보여주는 것은 용납할 수 없으니까요. 서버 간 통신에 문제가 있으면 요청을 거부하더라도 정확한 정보만 제공합니다.
대부분의 웹 애플리케이션은 가용성을 선택합니다. 오래된 장바구니 정보를 보여주는 것이 아무것도 보여주지 않는 것보다 낫기 때문입니다.
최종적 일관성: 현실적 타협
대부분의 대규모 시스템은 최종적 일관성(Eventual Consistency)을 사용합니다. "충분한 시간이 지나면 모든 복사본이 일치하게 된다"는 개념입니다. 지금 당장은 약간 다를 수 있지만, 결국에는 같아집니다.
네 번째 도전: 서버 실패 감지
수천 대의 서버가 있는 환경에서는 항상 어딘가에서 고장이 납니다. 어떻게 서버가 다운되었는지 알 수 있을까요?
모든 서버가 다른 모든 서버를 확인하는 것은 비현실적입니다. 1,000대 서버라면 거의 100만 개의 연결이 필요합니다.

가십 프로토콜
가십 프로토콜(Gossip Protocol)은 마치 학교에서 소문이 퍼지는 것과 같습니다. 각 서버는 다른 서버들의 목록을 가지고 있고, 가끔씩 몇 개의 무작위 이웃과 그 정보를 공유합니다.
서버 X가 응답하지 않으면, 그 소문이 전체 클러스터로 퍼집니다. 모든 서버가 모든 서버와 대화할 필요 없이도 효과적으로 작동합니다.

결론: 단순함 뒤의 복잡성
"장바구니 기억하기"라는 단순한 요구사항이 분산 시스템 엔지니어링의 마스터클래스가 되었습니다. 그리고 이것은 시작에 불과합니다.
우리가 아직 다루지 않은 주제들이 더 있습니다:

- 스토리지 엔진 최적화
- 데이터센터 전체 장애 대응
- 수천 대 머신에서의 성능 최적화
- 데이터 압축과 캐싱 전략
다음에 온라인 쇼핑에서 장바구니에 물건을 담았을 때 아무런 문제없이 잘 작동한다면, 그 순간 전 세계 데이터센터에서 벌어지고 있는 복잡하고 정교한 협업을 떠올려보세요. 이 모든 기술적 노력은 단 하나의 목표를 위한 것입니다. 바로 당신의 데이터가 언제나 정확한 곳에서 안전하게 기다리고 있도록 하는 것이죠.
키-값 저장소는 현대 인터넷의 숨은 영웅입니다. 단순해 보이지만 그 안에는 수십 년간 축적된 분산 시스템의 지혜가 담겨있습니다.