# Raft Consensus 알고리즘 완벽 가이드 – 분산 시스템에서 리더를 뽑는 가장 쉬운 방법 (50~60자)

---

# # 서론 – “왜 우리는 리더 선출 문제에 고민해야 할까?”

현대의 클라우드 서비스, 데이터베이스, 캐시 시스템 등 **분산 시스템**은 수많은 서버가 협업해 하나의 서비스를 제공하도록 설계됩니다.

그런데 서버 중 하나가 장애가 나면 어떻게 할까요?

- 데이터 일관성을 유지해야 한다.

- 클라이언트 요청을 지속적으로 처리해야 한다.

- 시스템 전체가 멈추지 않아야 한다.

이 모든 요구를 만족하려면 **‘누가 현재 시스템을 이끌어야 하는가(리더)’**를 명확히 정하고, 그 리더가 장애가 발생했을 때 빠르게 새로운 리더를 선출할 메커니즘이 필요합니다.

분산 시스템에서 리더를 선출하는 대표적인 방법이 바로 **Raft Consensus 알고리즘**입니다. 이 글에서는 Raft가 어떻게 동작하는지, 왜 이해하기 쉬운지, 실제 프로젝트에 어떻게 적용되는지를 초보자도 쉽게 따라올 수 있도록 2,000 단어 안팎으로 정리합니다.

> **핵심 키워드**: Raft 알고리즘, 분산 시스템, 리더 선출

---

# # 1. Raft란 무엇인가? – “쉽게 말해 ‘팀장 뽑기’ 게임”

Raft는 2013년 Diego Ongaro와 John Ousterhout가 발표한 **분산 합의(Consensus) 알고리즘**입니다. 기존에 복잡하고 이해하기 어려운 Paxos와 달리, Raft는 다음 세 가지 기본 목표를 내세워 설계되었습니다.

| 목표 | 설명 |

|------|------|

| **리더 선출** | 시스템에 하나의 리더만 존재하도록 보장 |

| **로그 복제** | 리더가 클라이언트 요청을 받아 로그에 기록하고, 이를 팔로워에게 복제 |

| **안전성 보장** | 어떤 상황에서도 일관된 상태(state)를 유지 |

즉, Raft는 “팀장이 정해지면 팀장이 모든 결정을 내리고, 팀원은 팀장의 명령을 그대로 따라한다”는 단순한 메커니즘을 구현합니다. 이 단순함이 바로 **‘초보자도 이해하기 쉬운’** 이유입니다.

---

# # 2. Raft의 기본 구성 요소

## # 2.1 서버 역할 – Leader, Follower, Candidate

1. **Follower**

   - 기본 상태이며, 리더의 하트비트(heartbeat) 신호를 기다립니다.

   - 일정 시간 동안 하트비트를 받지 못하면 **Candidate**가 됩니다.

2. **Candidate**

   - 선거를 시작하고, 다른 서버에게 투표를 요청합니다.

   - 과반수(> 50 %)의 표를 얻으면 **Leader**가 됩니다.

3. **Leader**

   - 클라이언트 요청을 받아 **로그(entry)** 를 생성하고, 이를 모든 Follower에게 복제합니다.

   - 일정 주기로 하트비트를 전송해 자신이 살아 있음을 알립니다.

## # 2.2 로그와 상태 머신

- **로그(entry)** : 클라이언트가 보낸 명령(예: “키‑값 저장”)을 순서대로 저장한 리스트.

- **상태 머신** : 로그를 적용해 실제 데이터베이스나 파일 시스템을 업데이트하는 역할.

Raft는 **‘로그가 동일한 순서대로 적용될 때만 상태 머신이 동일해진다’**는 원칙을 따릅니다. 따라서 로그 복제와 커밋(commit) 과정이 핵심입니다.

---

# # 3. Raft 동작 흐름 – 단계별 설명

## # 3.1 초기화와 하트비트

시스템이 시작되면 모든 서버는 **Follower** 상태이며, 임의의 **타임아웃**(election timeout)을 설정합니다. Leader가 존재하면 일정 주기(heartbeat interval)마다 `AppendEntries` RPC를 통해 하트비트를 전송합니다.

- **하트비트 수신** → 타임아웃 재설정 → 그대로 Follower 유지

- **하트비트 미수신** → 타임아웃 초과 → Candidate 전환

## # 3.2 선거(Election) 과정

1. **Candidate 전환**

   - 타임아웃이 끝나면 현재 term(선거 번호)를 증가시키고, 자신을 후보로 선언합니다.

   - `RequestVote` RPC를 모든 다른 서버에 전송합니다.

2. **투표 규칙**

   - 각 서버는 아직 투표하지 않았고, 후보의 로그가 **자신의 로그보다 최신**이면 투표합니다.

   - 최신성 판단은 `(lastLogTerm, lastLogIndex)` 쌍으로 비교합니다.

3. **과반수 확보**

   - Candidate가 과반수 표를 받으면 **Leader**가 됩니다.

   - 새로운 Leader는 즉시 `AppendEntries`(하트비트)와 함께 **빈 로그**를 전송해 자신이 Leader임을 알립니다.

> **Tip**: 선거는 **분산 시스템**에서 가장 빠르게 수렴하도록 설계되었습니다. 대부분의 경우 1~2 라운드만에 Leader가 선출됩니다.

## # 3.3 로그 복제와 커밋

1. **클라이언트 요청** → Leader가 로그에 새로운 entry 추가

2. **AppendEntries RPC** → Leader가 모든 Follower에게 새 entry 전송

3. **Follower 응답** → 성공 시 `matchIndex` 업데이트, 실패 시 `nextIndex` 감소 후 재시도

4. **커밋**

   - Leader는 **대다수(majority) 서버가 해당 entry를 저장**했는지 확인합니다.

   - 확인되면 해당 entry를 **커밋**하고, 상태 머신에 적용합니다.

   - 커밋된 entry는 `CommitIndex`에 기록됩니다.

## # 3.4 장애 복구와 로그 일관성 보장

- **Follower가 뒤처졌을 때**: Leader는 `prevLogIndex`와 `prevLogTerm`을 검증해 일치하지 않으면 **log truncation**(잘라내기) 후 재전송합니다.

- **Leader 장애**: 하트비트가 사라지면 새로운 선거가 시작되고, 기존 로그를 보존한 **최신 서버**가 새로운 Leader가 됩니다.

이러한 메커니즘 덕분에 **‘어떤 서버가 죽어도 일관된 상태를 유지한다’**는 안전성을 확보합니다.

---

# # 4. Raft를 실제 서비스에 적용한 사례

| 프로젝트/툴 | 적용된 Raft 모듈 | 주요 활용 |

|------------|----------------|----------|

| **etcd** | Core Raft 구현 | 키‑값 저장소의 메타데이터 복제 |

| **Consul** | HashiCorp Raft | 서비스 디스커버리와 KV 스토어 |

| **RethinkDB** | 자체 Raft | 샤드 복제와 장애 복구 |

| **TiKV** | Rust‑based Raft | 분산 트랜잭션 스토리지 |

| **CockroachDB** | Go‑Raft | SQL 레이어 위에 일관된 트랜잭션 제공 |

이처럼 Raft는 **데이터베이스, 키‑값 스토어, 서비스 레지스트리 등** 다양한 분야에서 핵심 합의 메커니즘으로 활용됩니다. 특히 **Go 언어**와 **Rust** 같은 현대 언어에 최적화된 구현체가 풍부해, 개발자는 직접 Raft를 내 서비스에 끼워 넣는 것이 비교적 쉬워졌습니다.

---

# # 5. Raft 구현 시 흔히 마주치는 고민과 해결책

## # 5.1 타임아웃 설정

- **Election timeout**은 보통 150 ms ~ 300 ms 사이로 랜덤하게 설정합니다.

- **Heartbeat interval**은 50 ms 이하가 일반적이며, 네트워크 상황에 맞게 조정합니다.

> **팁**: 타임아웃을 너무 짧게 하면 불필요한 선거가 빈번히 발생하고, 너무 길게 하면 장애 복구가 지연됩니다.

## # 5.2 로그 압축(Snapshot)

로그가 무한히 커지는 것을 방지하기 위해 **스냅샷**을 주기적으로 생성합니다.

1. Leader가 `InstallSnapshot` RPC를 통해 최신 상태를 전송

2. Follower는 스냅샷을 적용하고, 이후부터는 로그 복제만 진행

스냅샷은 **디스크 I/O**와 **네트워크 대역폭**을 절약해 대규모 클러스터에서도 효율적으로 동작합니다.

## # 5.3 멤버십 변화(Cluster Reconfiguration)

Raft는 **동적 멤버십**을 지원합니다.

- **Joint consensus** 방식을 사용해 기존 구성과 새 구성을 동시에 유지하면서 안전하게 서버를 추가/제거합니다.

- 이 과정에서도 **다수결** 원칙을 유지해 일관성을 보장합니다.

---

# # 6. Raft와 다른 합의 알고리즘 비교

| 항목 | Raft | Paxos | ZAB (ZooKeeper) |

|------|------|-------|-----------------|

| 이해 난이도 | ★★★★★ (가장 쉬움) | ★★☆☆☆ | ★★★☆☆ |

| 리더 선출 | 명시적 선거 과정 | 암묵적(프로포저) | ZAB 자체 선출 |

| 로그 복제 | 단순 `AppendEntries` | 복잡한 프로포저/아코머 | ZAB 프로토콜 |

| 장애 복구 | 빠른 선거 | 복구 속도 느림 | 빠른 복구 |

| 구현 난이도 | 중간 | 높음 | 중간 |

Raft는 **‘쉽게 배우고 구현할 수 있는’** 장점 덕분에 초보자부터 대규모 서비스까지 폭넓게 채택되고 있습니다.

---

# # 7. 직접 Raft를 구현해 보는 작은 프로젝트

## # 7.1 목표

- 3대 노드(서버)로 구성된 클러스터 만들기

- Go 언어와 `etcd/raft` 패키지를 활용해 Leader 선출, 로그 복제 구현

## # 7.2 주요 단계

1. **프로젝트 초기화**

   
```
bash

   go mod init raft-demo

   go get go.etcd.io/etcd/raft/v3

   
```

2. **Node 구조 정의**

   
```
go

   type Node struct {

       id        uint64

       raftNode  raft.Node

       storage   *raft.MemoryStorage

       transport Transport // 네트워크 전송 추상화

   }

   
```

3. **Raft 초기화**

   
```
go

   cfg := &raft.Config{

       ID:              nodeID,

       ElectionTick:    10,

       HeartbeatTick:   1,

       Storage:         storage,

       MaxSizePerMsg:   1024 * 1024,

       MaxInflightMsgs: 256,

   }

   node.raftNode = raft.StartNode(cfg, peers) // peers는 초기 클러스터 멤버

   
```

4. **메시지 처리 루프**

   
```
go

   go func() {

       for {

           select {

           case rd := <-node.raftNode.Ready():

               // 로그 저장, 스냅샷 적용, 메시지 전송 등

               node.storage.Append(rd.Entries)

               node.transport.Send(rd.Messages)

               node.raftNode.Advance()

           }

       }

   }()

   
```

5. **클라이언트 요청**

   - Leader에게 `Propose` 호출 → 로그에 entry 추가 → 복제 진행

위와 같은 최소 구현으로 **리더 선출**과 **로그 복제**를 체험할 수 있습니다. 실제 서비스에 적용하려면 **스냅샷, 영속성, 보안** 등을 추가해야 하지만, 초보자 입장에서는 충분히 학습용 프로젝트가 됩니다.

---

# # 결론 – “Raft, 이제 직접 써볼 차례입니다”

- **Raft Consensus 알고리즘**은 복잡한 분산 합의를 **‘리더 선출 → 로그 복제 → 커밋’**이라는 세 단계로 단순화했습니다.

- 이해하기 쉬운 구조와 풍부한 구현체 덕분에 **초보자도 금방 적용 가능**합니다.

- 실제 서비스(예: etcd, Consul, CockroachDB)에서도 핵심 역할을 수행하고 있어, **분산 시스템을 설계하거나 운영하는 모든 개발자**에게 필수 지식입니다.

> **다음 행동 권장**

> 1. 위에서 소개한 **간단 Raft 프로젝트**를 직접 구현해 보세요.

> 2. GitHub에서 `etcd/raft` 혹은 `rust-raft` 같은 오픈소스 구현을 살펴보며 코드를 분석해 보세요.

> 3. 현재 운영 중인 서비스에 **Raft 기반** 키‑값 스토어(예: etcd)를 도입해 **고가용성**을 검증해 보세요.

분산 시스템의 핵심은 **‘일관성과 가용성을 동시에 보장하는 메커니즘’**에 있습니다. Raft는 그 메커니즘을 가장 직관적으로 제공하므로, 이제 여러분 차례입니다. **Raft를 직접 다뤄보고, 여러분만의 견고한 분산 서비스를 구축해 보세요!**

---

**관련 링크**: [Raft Consensus in 2000 words (Reddit)](https://reddit.com/r/programming/comments/1ow0czt/raft_consensus_in_2000_words/)

**SEO 키워드**: Raft 알고리즘, 분산 시스템, 리더 선출.
