# Fan-Out On Write

## Fan-Out on Write: 쓰기 시점의 팬아웃

"Fan-Out on Write"는 Fan-Out 구조를 구체적으로 <mark style="color:red;">**`쓰기 시점`**</mark>에 적용한 방식입니다.

이는 Fan-Out 패턴의 확장 또는 특수화된 구현으로 볼 수 있으며, 데이터가 생성되는 시점(쓰기 시점)에 미리 여러 대상에 데이터를 분배하는 것을 의미합니다.

대표적인 예로 소셜 미디어를 들 수 있습니다. 사용자가 글을 작성하면(Write), 그 글이 즉시 모든 팔로워의 타임라인(피드)에 복제되어 저장됩니다. 즉, 글을 쓸 때마다 팔로워 수만큼 "fan-out"이 발생하는 것입니다.

(참고로 이와 반대되는 방식인 "Fan-Out on Read"는 읽기 시점에 여러 곳에서 데이터를 종합해 보여주는 방식입니다.)

## 트위터에서 적용되는 방식

트위터에서 한 사용자가 새 트윗을 작성하면, 다음과 같은 과정이 일어납니다.

1. 트윗 저장: 작성된 트윗은 DB에 저장됩니다.
2. 팔로워 목록 조회: 해당 트윗 작성자를 팔로우하는 모든 사용자의 목록을 조회합니다.
3. 타임라인 업데이트: 조회된 팔로워들의 "타임라인" DB에 해당 트윗을 추가(쓰기)합니다.

* 특정 사용자가 피드를 작성하면 해당 사용자를 팔로우하는 팔로워들의 타임라인에 피드가 복제되어 저장됩니다.

즉, 사용자가 피드를 작성할 때마다 팔로워 수만큼 쓰기(fan-out)이 발생하게 됩니다.

### 왜 팔로워 수만큼 쓰기가 발생하는가?

결론부터 말씀드리면, 트위터 같은 SNS에서 팔로워들이 최신 트윗을 즉각적으로 자신의 타임라인에서 보려면,&#x20;

단순히 "읽기"만으로는 현재와 같은 사용자 경험을 제공하기 어렵기 때문에 추가적인 <mark style="color:red;">**`쓰기`**</mark> 작업이 필요합니다.

**🤔 왜 "읽기"만으로는 부족할까? (Fan-Out on Read 방식의 문제)**

만약 '쓰기' 작업 없이 '읽기'로만 타임라인을 구현한다면, 다음 상황을 가정해 볼 수 있습니다.

```
1. 사용자가 트윗을 작성하면, 그 트윗은 '트윗 테이블' 하나에 시간 순서대로 저장됩니다.

2. 팔로워가 자기 타임라인을 보려고 할 때마다(Read), 시스템은 다음 작업을 실시간으로 수행합니다.
- 팔로우 목록 조회: 먼저 사용자가 팔로우하는 사람들의 목록을 가져옵니다. (예: 1,000명)
- 모든 트윗 조회: 팔로우 목록에 있는 1,000명이 작성한 모든 트윗을 '트윗 테이블'에서 조회합니다.
- 정렬 및 조합: 이 모든 트윗을 가져와 최신순으로 정렬하고, 사용자에게 보여줄 타임라인을 구성합니다.
```

이 방식은 단순해 보이지만, 트위터와 같은 거대 규모 서비스에서는 다음과 같은 심각한 문제가 발생할 수 있습니다.

{% hint style="warning" %}

* **성능 문제**:&#x20;
  * 사용자가 타임라인을 열 때마다 팔로우하는 수백, 수천 명의 트윗을 실시간으로 검색, 조회, 정렬해야 합니다.&#x20;
  * 만약 팔로워 중 인기 계정(예: 수백만 개의 트윗을 작성)이 있다면, 해당 사용자의 트윗을 가져오는 데만도 많은 시간이 소요될 수 있습니다.&#x20;
  * 수억 명의 사용자가 동시에 타임라인을 열려고 한다면, DB는 엄청난 쿼리 부하로 마비되거나 사용자들은 트윗을 보는 데 몇 분씩 기다려야 할 수도 있습니다.
* **복잡한 쿼리**:
  * 일반적인 RDB에서 여러 사용자의 트윗을 조인(join)하고 정렬하는 쿼리는 매우 복잡하고 리소스를 많이 소모합니다.
* **확장성 문제**:&#x20;
  * 사용자 수와 트윗 수가 기하급수적으로 늘어날수록 시스템을 확장하기 어렵습니다.
    {% endhint %}

## 쓰기가 필요한 이유 (Fan-out on Write의 본질)

"Fan-Out on Write" 방식을 사용하는 이유는, 사용자가 타임라인을 <mark style="color:red;">**`읽을 때의 속도`**</mark>**를 극대화하기 위해서입니다.**&#x20;

즉, 읽기 작업을 간단하고 빠르게 만들기 위해 쓰기 시점에 미리 모든 준비 작업을 해두는 것입니다.

비유를 들어보겠습니다.

* 읽기 방식 (Fan-out on Read)
  * 친구에게 전화를 걸 때마다, 그 친구가 다른 모든 친구에게 일일이 전화해서 정보를 물어본 다음, 그 정보를 종합해서 나에게 말해주는 것과 같습니다. (매번 실시간으로 정보를 수집하는 방식)
* 쓰기 방식 (Fan-out on Write)
  * 친구들이 중요한 일이 생길 때마다 나의 개인 비서에게 그 내용을 미리 전달해 두고, 내가 궁금할 때마다 비서가 이미 정리된 보고서를 즉시 건네주는 것과 같습니다. (정보를 미리 개인화된 공간에 푸시해두는 방식)

여기서 '개인 비서' 역할이 각 팔로워의 '개인 타임라인 저장소'이며, 여기에 트윗을 미리 전달해두는 행위가 바로 팔로워 수만큼 발생하는 쓰기 작업입니다.

이렇게 구성하면, 사용자가 타임라인을 열 때 단순히 자신의 개인 타임라인 저장소에서 데이터를 가져오기만 하면 되므로, 매우 빠르게 정보를 볼 수 있습니다.

결론적으로, 개별 저장소에 트윗을 미리 '쓰기' 해두는 것이 훨씬 효율적이라고 판단한 것입니다. 이는 <mark style="color:red;">**`읽기 성능을 위해 쓰기 부하를 감수하는 전략`**</mark>입니다.

## Fan-Out on Write 시스템 구성 예시

#### 1. 트윗 작성 서비스 (Tweet Write Service)

* 역할: 사용자로부터 트윗 작성 요청을 받아 처리합니다.
* 기술: 애플리케이션 서버로 API 엔드포인트(예: `POST /tweets`)를 구현합니다.
* 작동 방식:

  1. 애플리케이션 서버가 사용자로부터 트윗 작성 요청을 받습니다.
  2. 트윗 내용을 DB에 저장합니다.
  3. 저장 성공 후, 트윗 관련 정보(트윗 ID, 작성자 ID 등)를 메시지 브로커의 특정 토픽(Topic)으로 발행(publish)합니다. (이 과정이 Fan-out on Write를 시작하는 핵심 트리거입니다.)
  4. 사용자에게 트윗 작성 성공 응답을 반환합니다.

#### 2. 메시지 브로커 (Message Broker)

* 역할:&#x20;
  * 트윗 작성 이벤트(메시지)를 수집하고, 이를 필요로 하는 다른 서비스(subscriber)에 전달합니다.&#x20;
  * 분산 시스템에서 서비스 간의 비동기 통신을 담당합니다.
* 기술: 아파치 카프카(Apache Kafka), RabbitMQ 등
* 작동 방식:&#x20;
  * 트윗 서비스로부터 받은 트윗 이벤트를 Topic(또는 Queue)에 저장하고, 이를 구독하는 컨슈머들이 가져갈 수 있도록 대기시킵니다.

#### 3. 팔로워 서비스 / 타임라인 생성 서비스 (Timeline Generation Service)

* 역할:&#x20;
  * 메시지 브로커로부터 트윗 이벤트를 구독(subscribe)합니다.&#x20;
  * 트윗 작성자의 팔로워들을 찾아 각 팔로워의 개인 타임라인을 업데이트합니다. (이 서비스가 Fan-out on Write의 핵심적인 "쓰기" 부분을 담당합니다.)
* 기술: 애플리케이션 서버(컨슈머)에서 메시지 브로커의 이벤트를 소비(consume)할 수 있게 구성합니다.
* 작동 방식:
  1. 메시지 브로커로부터 새로운 트윗 이벤트 메시지를 수신합니다.
  2. 메시지에 포함된 사용자 ID를 활용하여 '팔로우 DB'(예: Redis, RDB 등)에서 해당 작성자를 팔로우하는 모든 사용자의 목록을 조회합니다. (이 DB는 팔로우/팔로잉 관계를 효율적으로 조회할 수 있도록 최적화되어 있어야 합니다.)
  3. 조회된 각 팔로워에 대해, 트윗 정보를 팔로워의 '개인 타임라인 DB'(주로 NoSQL)에 저장(쓰기)합니다. (Redis의 Sorted Set, Cassandra의 Wide Column Store 같은 구조가 이 용도에 효율적입니다.)
  4. 이 과정은 팔로워 수가 많을 경우(예: 수백만 명) 부하가 클 수 있으므로, 동시성 및 부하 관리가 중요합니다. 병렬 처리 또는 비동기 처리(예: Spring의 `@Async`, WebFlux)를 적극적으로 활용해야 합니다.

#### 4. 타임라인 조회 서비스 (Timeline Read Service)

* 역할: 사용자가 자신의 타임라인 조회를 요청할 때, 미리 만들어진 개인 타임라인 데이터를 조회하여 반환합니다.
* 기술: 스프링 부트 애플리케이션 서버에서 API 엔드포인트(예: `GET /timeline`)를 구현합니다.
* 작동 방식:
  1. 애플리케이션 서버가 사용자로부터 타임라인 조회 요청을 받습니다.
  2. 사용자 ID를 기반으로 '개인 타임라인 DB'(NoSQL)에서 해당 사용자의 미리 구성된 트윗 목록을 조회합니다.
  3. 조회된 트윗 목록을 사용자에게 반환합니다.
  4. 이 시점에는 복잡한 조인이나 정렬 없이 단순한 '읽기'만 발생하므로 매우 빠른 응답 속도를 보장할 수 있습니다.
