- 들어가며 — "둘을 고르세요"의 문제
- 세 글자의 엄밀한 정의
- 왜 "셋 중 둘"이 틀렸는가
- PACELC — 더 완전한 그림
- 실제 시스템은 어디에 서 있나
- CAP의 C와 ACID의 C는 다르다
- 일관성은 스펙트럼이다
- 흔한 오해들
- 설계자의 실전 지침
- 마치며
- 참고 자료
들어가며 — "둘을 고르세요"의 문제
분산 시스템을 처음 배우면 거의 반드시 CAP 정리를 만납니다. 그리고 거의 반드시 이렇게 요약된 버전을 만납니다. "일관성(Consistency), 가용성(Availability), 분할 내성(Partition tolerance) 중 둘만 고를 수 있다." 삼각형 그림이 따라 나오고, 각 데이터베이스를 세 변 중 하나에 붙입니다.
이 설명은 기억하기 쉽지만 틀렸습니다. 정확히는, 너무 뭉개서 오해를 부릅니다. 실제 CAP 정리가 말하는 것은 훨씬 좁고 정밀합니다. 이 글의 목표는 그 뭉갬을 걷어내는 것입니다. 세 글자가 각각 무엇을 엄밀히 뜻하는지, 왜 "셋 중 둘"이 오해인지, 그리고 이 정리가 실제 시스템 설계에서 어떤 의미를 갖는지를 손짓 없이 짚겠습니다.
세 글자의 엄밀한 정의
먼저 용어를 정확히 못 박아야 합니다. CAP의 세 글자는 일상어처럼 들리지만, 정리 안에서는 매우 구체적인 뜻을 가집니다.
C — 일관성(Consistency). 여기서의 일관성은 **선형화 가능성(linearizability)**을 뜻합니다. 쉽게 말하면, 모든 클라이언트가 마치 데이터의 복사본이 하나뿐인 것처럼 본다는 것입니다. 어떤 쓰기가 성공적으로 완료되면, 그 이후에 시작하는 모든 읽기는 그 쓰기(또는 더 최신의 쓰기)를 반드시 봐야 합니다. 낡은 값을 돌려주면 안 됩니다. 이것은 뒤에서 다룰 ACID의 C와는 완전히 다른 개념입니다.
A — 가용성(Availability). 여기서의 가용성은 "죽지 않은 모든 노드가 모든 요청에 대해 (에러가 아닌) 응답을 반드시 돌려준다"는 것입니다. 핵심은 "죽지 않은 모든 노드"입니다. 일부 노드만 응답하면 되는 것이 아니라, 살아 있는 노드라면 전부 답해야 합니다. 그리고 그 답은 합리적인 시간 안에 와야 합니다. 무한정 기다리게 하는 것은 가용하다고 치지 않습니다.
P — 분할 내성(Partition tolerance). 파티션(partition)은 네트워크가 쪼개져서 노드 그룹들 사이에 메시지가 오가지 못하는 상황입니다. 분할 내성이란 "이런 파티션이 발생해도 시스템이 계속 동작한다"는 성질입니다.
이 세 정의를 나란히 놓고 보면, 흔한 삼각형 그림이 왜 오해인지 실마리가 보이기 시작합니다.
왜 "셋 중 둘"이 틀렸는가
핵심은 이것입니다. 분할 내성(P)은 선택 사항이 아닙니다. 네트워크 파티션은 여러분이 고르든 말든 현실에서 발생합니다. 스위치가 죽고, 케이블이 끊기고, 데이터센터 사이 링크가 순간적으로 막힙니다. 여러 대의 기계가 네트워크로 통신하는 한, 파티션은 "일어날 수도 있는 일"이 아니라 "언젠가 반드시 일어나는 일"입니다.
그러니 "P를 버린다"는 선택지는 사실상 존재하지 않습니다. 파티션을 견디지 못하는 시스템은 파티션이 나는 순간 잘못된 답을 주거나 데이터가 깨집니다. 진지한 분산 시스템에서 P는 협상 대상이 아닙니다.
그럼 실제 선택은 무엇일까요? CAP 정리가 진짜로 말하는 것은 이것입니다.
네트워크 파티션이 발생한 그 순간에, 여러분은 일관성(C)과 가용성(A) 중 하나를 포기해야 한다.
파티션이 나면 노드 그룹들이 서로 통신하지 못합니다. 이때 한쪽 그룹에 쓰기 요청이 들어오면 딱 두 가지 선택뿐입니다.
- 가용성을 택한다(AP): 그냥 쓰기를 받아들이고 응답한다. 대신 다른 그룹은 이 변경을 모르므로, 그쪽에서 읽으면 낡은 값이 나온다. 일관성을 포기한 것이다.
- 일관성을 택한다(CP): 다른 그룹과 합의할 수 없으니 이 요청을 거부하거나 막는다. 응답을 못 주므로, 가용성을 포기한 것이다.
즉 CAP는 "평소에 셋 중 둘을 고르는" 문제가 아니라, "파티션이 났을 때 C냐 A냐"를 고르는 문제입니다. 파티션이 없는 평상시에는 C와 A를 둘 다 누릴 수 있습니다. 선택은 오직 파티션이라는 예외 상황에서만 강제됩니다.
파티션 없음(정상) 파티션 발생
┌─────────────┐ ┌─────────────┐
│ C 와 A 둘 다 │ --> │ C 또는 A 중 │
│ 누릴 수 있음 │ │ 하나만 선택 │
└─────────────┘ └─────────────┘
PACELC — 더 완전한 그림
CAP는 중요한 절반을 빠뜨립니다. 바로 "파티션이 없을 때는 어떤가"입니다. CAP는 파티션 상황만 이야기하고, 정상 상황에 대해서는 침묵합니다. 하지만 시스템의 대부분의 시간은 파티션이 없는 정상 상태입니다. 그 시간의 트레이드오프를 설명하는 것이 PACELC입니다.
PACELC은 이렇게 읽습니다.
파티션(P)이 나면, 가용성(A)과 일관성(C) 사이에서 선택한다. 그렇지 않으면(Else, E), 지연 시간(L)과 일관성(C) 사이에서 선택한다.
앞의 절반(PAC)은 CAP와 같습니다. 새로운 것은 뒤의 절반(ELC)입니다. 파티션이 없는 정상 상황에서도 여전히 트레이드오프가 있다는 것입니다. 강한 일관성을 유지하려면 여러 복제본이 합의해야 하고, 그 합의에는 시간이 걸립니다. 즉 일관성을 높이면 지연 시간이 늘어납니다. 반대로 지연을 줄이려면 합의를 느슨하게 하거나 생략해야 하고, 그러면 일관성이 약해집니다.
이 프레임으로 보면 시스템을 두 축으로 분류할 수 있습니다.
| 분류 | 파티션 시(PAC) | 정상 시(ELC) | 뜻 |
|---|---|---|---|
| PA/EL | 가용성 우선 | 지연 우선 | 항상 빠름과 가용성을 택함 |
| PC/EC | 일관성 우선 | 일관성 우선 | 항상 일관성을 택함 |
| PA/EC | 가용성 우선 | 일관성 우선 | 파티션 때만 가용성 양보 안 함 |
| PC/EL | 일관성 우선 | 지연 우선 | 파티션 때만 일관성 사수 |
PACELC이 CAP보다 나은 이유는, 우리가 실제로 매일 마주하는 트레이드오프를 담기 때문입니다. 파티션은 드문 사건이지만, "일관성이냐 지연이냐"는 정상 운영 내내 계속되는 선택입니다.
실제 시스템은 어디에 서 있나
이론을 실제 시스템에 대보면 훨씬 선명해집니다.
Amazon Dynamo 계열 — AP (그리고 EL). Dynamo와 그 후예들(예: Cassandra, Riak, DynamoDB의 일부 설정)은 가용성을 최우선으로 설계되었습니다. 파티션이 나도 쓰기를 계속 받아들이고, 나중에 복제본들이 다시 만나면 충돌을 해소합니다. 이 계열은 **결과적 일관성(eventual consistency)**을 제공합니다. 지금 당장은 노드마다 값이 다를 수 있지만, 시간이 지나면 수렴합니다. 정상 상황에서도 합의를 기다리지 않고 낮은 지연을 택하므로 EL에 가깝습니다. "장바구니는 절대 멈추면 안 된다"는 요구가 이 선택을 낳았습니다. 두 기기에서 담은 물건이 잠깐 어긋나도, 나중에 합치면 되니까요.
Google Spanner — CP (그리고 EC). Spanner는 정반대 극단입니다. 전 지구에 흩어진 데이터베이스이면서도 강한 일관성(외부 일관성, 사실상 선형화 가능성)을 제공합니다. 파티션이 나면 합의(Paxos)를 이룰 수 없는 쪽은 쓰기를 멈춥니다. 즉 일관성을 위해 가용성을 양보합니다. 정상 상황에서도 복제본 간 합의와 정교한 시간 동기화를 거치므로 지연이 늘어납니다. 즉 EC입니다. Spanner가 유명한 이유는, 원자시계와 GPS로 만든 TrueTime이라는 장치로 이 강한 일관성을 지구 규모에서 실용적인 성능으로 해냈기 때문입니다. 하지만 CAP를 우회한 것은 아닙니다. 파티션이 나면 여전히 가용성을 포기합니다.
전통적 단일 노드 RDBMS. 한 대의 PostgreSQL이나 MySQL 인스턴스는 어떨까요? 여기엔 네트워크로 나뉜 복제본이 없으므로 파티션이라는 개념 자체가 흐릿합니다. CAP는 본질적으로 여러 노드에 데이터가 복제된 상황을 다루는 정리입니다. 단일 노드는 그 무대 밖에 있습니다. 물론 복제(replication)를 붙이는 순간 다시 이 트레이드오프의 무대로 들어옵니다.
정리하면 이렇습니다. 어떤 시스템도 "CAP를 깼다"거나 "셋 다 가진다"고 말할 수 없습니다. 파티션이 나면 모두가 C와 A 사이에서 선택해야 합니다. 차이는 어느 쪽을 택하도록 설계했는가일 뿐입니다.
CAP의 C와 ACID의 C는 다르다
여기서 아주 흔한 혼동을 반드시 짚어야 합니다. CAP의 C와 ACID의 C는 철자만 같고 뜻이 완전히 다릅니다.
- CAP의 C = 선형화 가능성. 여러 복제본에 걸쳐, 모든 클라이언트가 최신 값을 본다는 것. 이것은 분산 복제에 관한 성질입니다.
- ACID의 C = 일관성(consistency)이지만 여기서는 "제약 조건 보존". 트랜잭션이 데이터베이스의 불변식(외래 키, 유니크 제약, 사용자가 정의한 규칙 등)을 깨지 않고, 유효한 상태에서 유효한 상태로만 옮긴다는 것. 이것은 한 트랜잭션의 정합성에 관한 성질입니다.
둘은 다른 층위의 이야기입니다. ACID의 C는 애플리케이션이 정의한 규칙을 데이터베이스가 지켜준다는 약속이고, CAP의 C는 여러 복제본이 같은 값을 보여준다는 약속입니다. 이 둘을 뭉뚱그리면 "CP 시스템은 ACID를 보장한다" 같은 잘못된 문장을 쓰게 됩니다. 실제로는 서로 독립적인 개념입니다.
한 걸음 더 들어가면, 분산 트랜잭션에서 흔히 말하는 격리 수준의 최상단인 **직렬화 가능성(serializability)**과 CAP의 **선형화 가능성(linearizability)**도 다릅니다. 직렬화 가능성은 여러 트랜잭션이 어떤 순차 실행과 동등한 결과를 낸다는 것이고, 선형화 가능성은 단일 객체 연산이 실시간 순서를 지킨다는 것입니다. 이 둘을 합친 강력한 보장을 **엄격 직렬화 가능성(strict serializability)**이라 부르며, Spanner가 지향하는 지점이 대략 여기입니다. 용어가 비슷해 헷갈리기 쉬우니, "무엇에 대한 순서 보장인가"를 늘 되물어야 합니다.
일관성은 스펙트럼이다
CAP는 일관성을 "선형화 가능성이냐 아니냐"의 이분법으로 다루지만, 현실의 일관성은 넓은 스펙트럼입니다. 강한 쪽에서 약한 쪽으로 몇 단계를 짚어보면 감이 잡힙니다.
- 선형화 가능성(linearizable): 가장 강함. 모든 읽기가 최신 쓰기를 본다.
- 순차 일관성(sequential): 모든 클라이언트가 같은 순서로 연산을 보지만, 그 순서가 실시간과 일치할 필요는 없다.
- 인과 일관성(causal): 인과 관계가 있는 연산들만 순서를 지킨다. 서로 무관한 연산은 순서가 뒤바뀌어 보여도 된다.
- 결과적 일관성(eventual): 새 쓰기가 없다면 언젠가는 모든 복제본이 같은 값으로 수렴한다. 그전까지는 낡은 값을 볼 수 있다.
AP 시스템이 "일관성을 포기한다"고 할 때, 대개 완전한 무질서로 떨어지는 것이 아니라 이 스펙트럼의 약한 쪽(예: 결과적 일관성이나 인과 일관성)을 택하는 것입니다. 그래서 "AP냐 CP냐"라는 이분법보다, "어느 수준의 일관성을 어떤 대가로 사는가"를 묻는 편이 실무에 가깝습니다.
흔한 오해들
지금까지의 내용을 오해 목록으로 다시 정리하면 이렇습니다.
- "평소에 셋 중 둘을 고른다" — 아니다. 파티션이 없으면 C와 A를 둘 다 누린다. 선택은 파티션 순간에만 강제된다.
- "P는 버릴 수 있는 선택지다" — 아니다. 네트워크 파티션은 현실에서 반드시 일어나므로, 분산 시스템에서 P는 사실상 필수다.
- "CA 시스템이 존재한다" — 여러 노드에 복제된 시스템에서 파티션을 무시하는 CA는 실질적으로 성립하지 않는다. 파티션이 나면 결국 C나 A 중 하나를 잃는다.
- "CP 시스템은 항상 사용 불가 상태가 된다" — 아니다. CP 시스템은 파티션이 난 그 순간, 그 영향받는 부분에서만 가용성을 양보한다. 평소에는 멀쩡히 응답한다.
- "CAP의 C가 곧 ACID의 C다" — 아니다. 앞 절에서 보았듯 완전히 다른 개념이다.
- "결과적 일관성은 곧 데이터가 뒤죽박죽이라는 뜻이다" — 아니다. 수렴을 보장하며, 인과 관계 유지 같은 더 강한 형태도 있다.
설계자의 실전 지침
이 이론을 실제 결정으로 옮길 때 유용한 질문들을 정리합니다.
먼저 이 데이터에 낡은 값을 잠깐 보여줘도 되는가를 물어야 합니다. 소셜 피드의 좋아요 수, 상품 조회수 같은 것은 잠깐 어긋나도 큰일이 아닙니다. 여기선 AP와 낮은 지연이 합리적입니다. 반대로 은행 잔고, 재고 수량, 좌석 예약처럼 두 번 팔면 안 되는 데이터는 강한 일관성이 필요하니 CP 쪽으로 기웁니다.
다음으로 파티션이 났을 때의 동작을 명시적으로 설계했는가를 물어야 합니다. 많은 사고가 "파티션이 안 날 것"이라는 암묵적 가정에서 옵니다. 파티션은 반드시 오므로, "그때 쓰기를 막을 것인가, 받아들이고 나중에 화해시킬 것인가"를 미리 정해두어야 합니다. 후자를 택했다면 충돌 해소 전략(최신 타임스탬프 우선, 버전 벡터, CRDT 등)도 함께 정해야 합니다.
마지막으로 정상 상황의 지연 예산을 아는가를 물어야 합니다. PACELC의 ELC가 말하듯, 강한 일관성은 정상 상황에서도 지연을 부릅니다. 지구 반대편 복제본까지 합의를 기다리는 비용을 감당할 수 있는지, 아니면 지역별로 약한 일관성을 허용하고 지연을 줄일지 결정해야 합니다.
마치며
CAP 정리는 분산 시스템에서 가장 자주 인용되면서도 가장 자주 오해받는 결과입니다. "셋 중 둘을 고르라"는 손짓 섞인 요약은 기억하긴 쉽지만, 정작 중요한 진실을 가립니다. 진짜 이야기는 훨씬 정밀합니다. 파티션 내성은 사실상 필수이고, 진짜 선택은 오직 파티션이 발생한 순간에 일관성과 가용성 사이에서 이루어집니다. 그리고 PACELC이 상기시키듯, 파티션이 없는 평상시에도 일관성과 지연 사이의 조용한 트레이드오프가 늘 흐르고 있습니다.
Dynamo가 가용성을, Spanner가 일관성을 택한 것은 어느 한쪽이 옳아서가 아니라, 각자가 풀려는 문제가 달랐기 때문입니다. 좋은 설계자는 "CAP를 이겼다"고 주장하지 않습니다. 대신 "이 데이터에 대해, 파티션이 났을 때 나는 무엇을 포기할 것인가"라는 질문에 또렷하게 답합니다. 손짓을 걷어내고 나면, CAP는 마법의 삼각형이 아니라 그 또렷한 질문을 던지게 해 주는 정직한 도구가 됩니다.
참고 자료
- Eric Brewer, "CAP Twelve Years Later: How the 'Rules' Have Changed": https://www.infoq.com/articles/cap-twelve-years-later-how-the-rules-have-changed/
- Gilbert & Lynch, "Brewer's Conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services": https://groups.csail.mit.edu/tds/papers/Gilbert/Brewer2.pdf
- Daniel Abadi, "Consistency Tradeoffs in Modern Distributed Database System Design (PACELC)": https://www.cs.umd.edu/~abadi/papers/abadi-pacelc.pdf
- Google Spanner 논문: https://research.google/pubs/pub39966/
- Amazon Dynamo 논문: https://www.allthingsdistributed.com/files/amazon-dynamo-sosp2007.pdf
- Jepsen: Consistency Models: https://jepsen.io/consistency
현재 단락 (1/65)
분산 시스템을 처음 배우면 거의 반드시 CAP 정리를 만납니다. 그리고 거의 반드시 이렇게 요약된 버전을 만납니다. "일관성(Consistency), 가용성(Availability...