# 프로토콜

> 양방향 동기화에서 충돌 사본이 안 생기는 이유는 폴더마다 쓰는 쪽이 하나이기 때문입니다.

- 수치: writer 1
- 사람이 보는 화면: https://aitryed.com/ko/built/claude-bus/protocol/
- 반대 언어: https://aitryed.com/en/built/claude-bus/protocol/index.md
- 이 사이트 안내(AI용): https://aitryed.com/ko/llms.txt

---

**양쪽이 같은 폴더를 쓰는데 왜 충돌이 안 나는가. 답은 «폴더마다 쓰는 쪽을 하나로» 정했기 때문입니다.**

## 충돌 사본이 왜 생기는가

자동 동기화 폴더를 써 보신 분은 **「어떤 파일 (충돌본)」** 같은 게 생기는 걸 보셨을 겁니다.

원인은 하나입니다. **두 컴퓨터가 «같은 파일»을 «동시에» 고친 것**입니다.

| A 컴퓨터 | B 컴퓨터 | 결과 |
|---|---|---|
| 파일을 고침 | 같은 파일을 고침 | **어느 쪽이 맞는지 동기화 도구가 모름** → 둘 다 남김 |

그러면 **어느 게 최신인지 사람이 골라야** 합니다. 자동화가 안 됩니다.

## 그래서 그 상황이 «생길 수 없게» 만들었습니다

고치는 게 아니라 **애초에 안 생기게** 했습니다. 폴더를 갈라서 **각 폴더에 쓰는 쪽을 하나로** 정했습니다.

| 폴더 | **쓰는 쪽** | 읽는 쪽 |
|---|---|---|
| 받은 편지함 | **보내는 사람들** | 주인 |
| 보낸 편지함 | **주인만** | 보냈던 사람들 |
| 기록 | **주인만** | 누구나 |

**쓰는 쪽이 하나면 「동시에 고침」이 성립할 수가 없습니다.**

읽는 건 여럿이 해도 충돌이 안 납니다. 읽기는 아무것도 안 바꾸니까요.

> **충돌을 «잘 처리하는» 것보다 «생길 수 없게 만드는» 편이 훨씬 쌉니다.**

## 반쯤 쓰다 만 파일이 건너가면 안 됩니다

두 번째 문제입니다.

파일을 쓰는 데는 시간이 걸립니다. 그런데 동기화 도구는 **쓰는 도중에도 파일을 발견**할 수 있습니다. 그러면 **반쯤 쓰인 파일이 건너가고**, 받는 쪽이 그걸 읽다 깨집니다.

해법은 간단합니다.

1. **임시 이름**으로 다 씁니다 — 동기화 도구가 무시하는 이름으로
2. 다 쓰고 나서 **이름을 바꿉니다**

이름 바꾸기는 **한순간에 끝납니다.** 그래서 동기화 도구가 그 파일을 발견하는 시점에는 **이미 완성된 파일**입니다.

## 식별 번호가 파일 이름과 같습니다

작은데 편한 설계입니다.

요청의 식별 번호를 **파일 이름과 똑같이** 만들었고, 그 안에 **보낸 사람 이름**도 들어 있습니다.

그래서 **폴더를 눈으로 보기만 해도** 누가 뭘 보냈는지 압니다. 파일을 열어 볼 필요가 없습니다.

## 「침묵」과 「일하는 중」을 구분합니다

이게 이 방식의 제일 어려운 문제였습니다.

메신저는 **오류가 나면 오류가 보입니다.** 이 방식은 다릅니다.

> **비동기 채널은 오류가 아니라 «침묵»으로 고장 납니다.**

그리고 **침묵은 「아직 일하는 중」과 구분이 안 됩니다.** 둘 다 **아무 일도 안 일어나는 것으로 보입니다.**

| 실제 | 보이는 것 |
|---|---|
| 열심히 일하는 중 | **아무것도 안 옴** |
| **감시자가 죽음** | **아무것도 안 옴** |
| 파일을 못 읽음 | **아무것도 안 옴** |

그래서 **「받았다」를 먼저 알리게** 했습니다. 일이 끝나기 전에 <strong>「접수했고 시작한다」</strong>를 보냅니다.

그러면 침묵이 두 종류로 갈립니다 — **접수 표시가 온 침묵**(일하는 중)과 **접수 표시도 안 온 침묵**(뭔가 고장).

## 찾은 버그 — 「앞줄이 막아서 뒷줄이 영원히 못 들어감」

여기가 값을 치른 자리입니다.

한 번에 처리할 개수를 제한하는 코드가 있었습니다. 「한 번에 5개까지만」 같은 식입니다.

그런데 그 제한이 **이미 답한 요청을 걸러 내기 «전»에** 걸려 있었습니다.

무슨 일이 생기냐면 —

1. 폴더에서 요청을 읽음
2. **먼저 5개로 자름** ← 여기가 문제
3. 그중 이미 답한 것을 걸러 냄
4. **남은 게 없음** → 아무것도 처리 안 함

이미 답한 요청 5개가 앞에 있으면, **그 뒤에 있는 새 요청은 영원히 차례가 안 옵니다.** 5개를 자르고 나면 전부 걸러지니까요.

그리고 **아무 오류도 안 납니다.** 그냥 조용히 아무것도 안 합니다.

고치는 건 **순서를 바꾸는 것뿐**이었습니다 — **거르고 나서 자릅니다.**

→ **같은 두 동작인데 순서가 바뀌면 결과가 완전히 달라지고, 그 차이가 화면에 안 나타납니다.**

## 여기부터는 자세한 기록입니다

파일 두 개가 전부다. 요청 하나, 응답 하나. 그 위에 규약이 몇 개 얹혀 있다.

## 폴더마다 쓰는 쪽이 하나다

양방향 동기화는 **두 기계가 같은 파일을 고칠 때** 충돌 사본을 만든다. 그래서 그 상황이 애초에 안 생기게 폴더를 갈랐다.

| 폴더 | 쓰는 쪽 | 읽는 쪽 |
|---|---|---|
| `<상대>/inbox` | 보내는 사람들 | 상대 |
| `<상대>/outbox` | 상대만 | 보낸 사람들 |
| `<상대>/log` | 상대만 | 누구나 |

**어느 파일도 두 기계가 같이 고치지 않는다.** 그래서 충돌 해결 로직이 필요 없다 — 충돌이 안 나는 게 아니라 **날 수 없는 구조**다.

폴더 이름은 **실행하는 쪽**을 가리킨다. `home/inbox`는 집 PC가 처리할 것들이다. 처음엔 "보낸 사람 기준"으로 이름을 붙였다가 헷갈려서 뒤집었다 — 파일을 보는 순간 **"누가 이걸 실행하나"가 궁금하지 "누가 보냈나"가 궁금한 게 아니었다.**

## 반쯤 쓰인 파일은 복제되지 않는다

파일을 임시 이름으로 다 쓴 뒤 이름을 바꾼다. 동기화 도구가 잡아가는 시점에 **이미 완성된 파일**이라는 뜻이다.

이걸 안 하면 반쯤 쓰인 JSON이 건너가고, 반대편은 그걸 파싱하다 실패한다. 그리고 그 실패는 **재현이 거의 안 된다** — 파일 크기와 동기화 타이밍이 맞아떨어질 때만 나기 때문이다.

## 식별자가 파일명과 같다

요청 식별자는 항상 파일명과 같고, 그 안에 **보낸 사람 이름이 들어 있다.**

그래서 두 기계가 같은 inbox에 써도 이름이 부딪히지 않는다. 그리고 파일 목록만 봐도 누가 뭘 언제 보냈는지 읽힌다 — **디렉터리 목록 자체가 로그**가 된다.

식별자를 따로 만들고 파일명을 순번으로 두는 방법도 있었는데, 그러면 파일을 열어 봐야 뭐가 뭔지 안다.

## 침묵과 작업 중을 구분한다

비동기 채널은 **에러가 아니라 침묵으로** 고장 난다. 그리고 침묵은 "아직 일하는 중"과 구분되지 않는다.

그래서 감시자는 잡을 집는 **그 순간에** 상태를 `running`으로 써서 내보낸다. 끝나면 덮어쓴다.

이 한 줄 때문에 **"아직 못 봤다"와 "지금 하는 중"이 갈린다.** 답이 안 왔을 때 물어볼 질문이 달라진다 — 전자면 동기화를 의심하고, 후자면 그냥 기다리면 된다.

## 찾은 버그 — head-of-line starvation

한 번에 처리할 개수를 자르는 코드가 **이미 답한 요청을 걸러내기 전에** 실행되고 있었다.

그래서 처리는 끝났지만 안 치운 요청 3건이 큐 앞에 있으면 **새 작업은 영원히 집히지 않는다.** 에러 없음. 감시자는 계속 돈다. 로그도 정상이다.

스모크 테스트에서 네 번째 작업이 처리되지 않는 걸로 잡았다. 세 개까지는 전부 통과했으니, **세 개만 던졌으면 못 봤다.**

상한이 3이라 네 번째에서 걸린 건데, 이건 운이었다. 상한이 10이었으면 열한 번째에서 걸렸을 테고, **테스트를 열한 개까지 던져 볼 이유가 없다.**
