노트

CRDT

Conflict-free Replicated Data Type

CS#algorithm · 연결된 개념 3개

쉽게 말하면

CRDT는 각자 고친 사본을 어떤 순서로 합쳐도 같은 결과가 나오게 만든 자료구조예요. 사람마다 자기 칸에만 찍는 도장판을 칸별 최댓값으로 합치면, 누가 먼저 합치든 같은 판이 되는 식이죠.

비유가 깨지는 곳 도장판처럼 늘기만 하면 쉽지만, 지운 것도 툼스톤으로 남겨야 해서 메타데이터가 계속 쌓여요. '재고가 음수면 안 된다' 같은 업무 규칙은 자동 병합으로 표현하기 어려워요.

여러 사본이 각자 독립적으로 수정돼도, 변경이 어떤 순서로 도착하든 병합하면 항상 같은 상태로 수렴하도록 수학적으로 설계한 자료구조. 이름 그대로 "충돌 없는 복제 데이터 타입"이다. 중앙 서버가 순서를 정해 주지 않아도 된다.

두 가지 방식

  • 상태 기반(State-based, CvRDT): 상태 전체를 주고받고 병합 함수로 합친다. 병합은 순서와 중복에 상관없이 같은 결과를 내야 한다
  • 연산 기반(Operation-based, CmRDT): 연산을 주고받되, 동시에 일어난 연산끼리는 순서를 바꿔 적용해도 결과가 같도록 설계한다. 대신 전달 계층이 연산을 빠짐없이 한 번씩, 인과 순서대로 전달해야 한다
// G-Counter: 증가만 하는 카운터. 노드마다 자기 칸만 올리고, 병합은 칸별 최댓값
type GCounter = Record<string, number>
const merge = (a: GCounter, b: GCounter) => {
  const out = { ...a }
  for (const [node, n] of Object.entries(b)) out[node] = Math.max(out[node] ?? 0, n)
  return out
}
const value = (c: GCounter) => Object.values(c).reduce((s, n) => s + n, 0)

주요 타입

타입용도방식
G-Counter / PN-Counter카운터노드별 칸, 증가·감소 칸 쌍
LWW-Register(Last-Writer-Wins)단일 값타임스탬프가 늦은 쪽이 이김
OR-Set(Observed-Remove Set)집합추가마다 고유 태그를 붙여 추적
RGA(Replicated Growable Array), YATA 계열텍스트·리스트문자마다 고유 ID

텍스트 편집에서는 문자에 고유 ID를 붙이고 "어느 문자 뒤에 넣는다"를 인덱스가 아닌 ID로 표현해, 동시에 넣어도 위치가 꼬이지 않는다.

한계

  • 삭제된 요소도 표시(툼스톤, Tombstone)로 남겨야 해서 메타데이터가 계속 쌓인다. 정리 전략이 필요하다
  • "재고가 음수가 되면 안 된다" 같은 업무 규칙이나 의도 기반 연산은 자동 병합으로 표현하기 어렵다

오프라인에서 각자 고친 뒤 나중에 합치는 로컬 퍼스트와 잘 맞고, 서버가 연산을 변환하는 OT와 자주 비교된다. 각 사본이 언젠가 같아진다는 점에서 최종 일관성의 한 구현이다. 라이브러리로는 Yjs, Automerge가 대표적이다(2026 기준).

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • 그래프

    정점(vertex, 노드)과 정점들을 잇는 간선(edge)으로 이루어진 자료구조. 트리도 그래프의 한 종류다. SNS 친구 관계, 지도와 경로, 웹 페이지 링크, 추천 시스템, 패키지 의존성처럼 "무엇과 무엇이 연결돼 있다"는 모든 것을 표현한다. 이 지식 맵도 노트를 정점, 링크를 간선으로 한 그래프다.

  • DB 인덱스와 트레이드오프

    DB 인덱스는 특정 컬럼 값으로 행을 빨리 찾도록 테이블 옆에 따로 유지하는 보조 자료구조(auxiliary data structure)다. 관계형 DB의 기본 인덱스는 정렬된 균형 트리(B-tree 계열)라서, 전체를 훑지 않고 트리를 따라 내려가 원하는 행에 닿는다.

  • 데이터베이스 다중화

    같은 데이터를 여러 DB 서버에 복제해 두는 것. 가장 흔한 형태는 원본을 가진 주(Primary) 서버가 쓰기를 받고, 사본을 받는 부(Replica) 서버들이 읽기를 나눠 맡는 구조다.

  • 결합도

    한 요소를 바꿀 때 다른 요소도 바꿔야 하는 관계. 결합도는 언제나 "어떤 변경에 대해" 결합되어 있는지를 함께 말해야 의미가 있다. 같은 두 모듈도 어떤 변경에는 묶여 있고 어떤 변경에는 독립적일 수 있다.

  • 커맨드 패턴

    요청이나 작업을 실행 가능한 객체로 포장하는 패턴. 작업을 값처럼 저장·전달·대기열에 넣을 수 있게 되고, 되돌리기 같은 부가 연산을 붙일 수 있다.

보기 옵션