# Automerge로 멀티플레이어 팟캐스트 에디터 만들기

> Clean Markdown view of GeekNews topic #30490. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=30490](https://news.hada.io/topic?id=30490)
- GeekNews Markdown: [https://news.hada.io/topic/30490.md](https://news.hada.io/topic/30490.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2026-06-15T09:31:02+09:00
- Updated: 2026-06-15T09:31:02+09:00
- Original source: [adamsolove.com](https://www.adamsolove.com/ui/ducking/2026/06/10/podcast-multiplayer.html)
- Points: 3
- Comments: 0

## Topic Body

- 몇년 전만 해도 실시간 멀티플레이어 데이터 동기화는 전문 인력과 기업 수준의 투자가 필요한 가장 어려운 문제였으나, 이제는 **`npm install` 한 번**으로 취미 프로젝트에도 멀티플레이어 UI 구현 가능  
- **Automerge**는 로컬 우선·멀티플레이어 안전·버전 관리를 갖춘 데이터 모델 구축 도구로, React의 `useState` 패턴과 유사한 방식으로 데이터 영속화·이력 관리·협업자 브로드캐스트·충돌 해소를 UI가 신경 쓰지 않아도 자동 처리  
- 브라우저 기반 멀티플레이어 오디오 에디터 **Ducking** 사례에서, 데이터 모델을 **CRDT 연산에 자연스럽게 매핑**되도록 설계하는 것이 핵심  
- 리스트 재정렬처럼 Automerge가 보장하지 않는 경우에는 **애플리케이션 계층 코드**로 더 강한 불변식 직접 구현  
- 과거 산업용 수준의 마법이었던 실시간 협업 편집이 이제 소수를 위한 작은 앱에도 자유롭게 적용 가능해진 점이 핵심 의미  
  
---  
  
### 배경 — Ducking 프로젝트  
  
- 지난 몇 달간 파트너의 팟캐스트를 위해 **브라우저 기반 멀티플레이어 오디오 에디터** Ducking 제작  
- 오디오 편집이 20년 된 단일 사용자 데스크톱 앱과 파일을 주고받는 방식에 머물러 있는 현실이 이상했음  
  - 한 명이 클립을 편집하는 동안 다른 한 명은 트랜스크립트를 고치거나 EQ 설정을 조정하는, **Google Docs나 Figma 같은 협업 워크플로우** 필요  
  - 댓글, 이력, 변경 추적 같은 현대적 협업 도구도 함께 요구됨  
- 이전 글에서 다룬 독특한 UI 디자인과 오디오 레이아웃 모델은 단일 편집기를 더 효과적으로 만들었으나, 진짜 원했던 것은 **더 협업적인 워크플로우**  
  
### Automerge 작업 방식  
  
- 오디오 blob을 제외한 Ducking의 모든 데이터는 **Automerge 문서**에 저장  
- 핵심 패턴은 React 개발자에게 익숙한 형태로, 훅으로 데이터를 가져와 렌더링하고 비동기 변경 요청을 디스패치하면 데이터 변경 후 훅이 리렌더 트리거  
  - `useDocument` 훅 사용 예시: `const [doc, changeDoc] = useDocument&lt;Episode&gt;(docUrl)` 형태로 문서를 받아 입력값 변경 시 `changeDoc((d) => { d.title = e.target.value })`로 갱신  
- 데이터 갱신 연산은 명령형처럼 보이지만 네이티브 JS 객체·배열과 다름  
  - 메서드가 더 적고 즉시 변형(mutate)하지 않으며, 자체적으로 변형을 가로채 **문서 이력의 changelist 항목**으로 전환  
- Automerge는 단순 용도에서는 필요한 것을 처리하지만 마법은 아니며, 불변식이 원하는 의미와 항상 일치하지는 않아 **신중한 데이터 모델 설계** 중요  
  - 대부분의 의미 단위 사용자 동작이 Automerge가 제공하는 단일 연산에 대응되도록 함  
  - 관련 데이터에 대한 별개의 사용자 동작이 해당 Automerge 연산의 불변식 관점에서 자연스럽게 해소되도록 함  
  - 저장되는 정규 데이터(canonical data)와 계산으로 도출되는 파생 데이터(derived data)를 명확히 분리  
  
#### 멀티플레이어를 위한 데이터 모델링  
  
- Ducking의 데이터 모델에서 **clip(클립)** 은 변경 불가능한 기저 오디오 소스의 일부를 재생하는 창(window)으로, 재생 구간·효과 적용·타임라인 공간 점유 담당  
  - 가장 흔한 효과는 클립이 기저 오디오의 볼륨을 시간에 따라 조절해 크로스페이드하거나 잡음을 제거하는 것  
- 초기에는 각 클립이 **클립 시작 기준의 시간 인덱스 볼륨 레벨** 목록을 가졌으나, 대부분의 볼륨 변경은 클립이 아닌 기저 오디오에 관한 것이라 문제 발생  
  - 클립 시작 시간을 조금 앞당기면 모든 볼륨 변경이 오디오의 다른 부분에 적용되는 현상  
  - 클립 시작 시간이 바뀔 때 모든 볼륨 타임스탬프를 갱신하는 코드를 작성하는 방식은 **나쁜 선택**  
- 두 협업자가 동시에 클립 시작 시간을 편집하면, 각 편집이 시작 시간과 모든 볼륨 자동화 타임스탬프 변경을 함께 묶음  
  - Automerge는 그 변경들 사이의 **인과 관계(causal relationship)** 를 알 수 없어 병합 시 뒤죽박죽으로 해소될 수 있음  
  - 하나의 의미 단위 동작이 CRDT가 이해하지 못하는 인과적 방식으로 여러 영속 데이터를 갱신하려 할 때 발생하는 전형적 문제  
- 해결책은 오디오 효과 데이터를 클립이 아닌 **기저 오디오의 타임프레임 기준**으로 이전(migrate)하는 것  
  - 클립 시작·길이 변경 시 갱신이 불필요해져, 여러 편집자가 시작 시간·볼륨 자동화·기타 효과를 바꿔도 서로 독립적이라 올바르게 병합될 가능성 높음  
- 단일 사용자 UI와 멀티플레이어 UI의 차이  
  - 단일 사용자 UI에서는 기존 데이터 모델을 두고 쓰기 시점에 추가 계산을 더해 동작시키기도 함  
  - 멀티플레이어 UI에서는 모든 영속 데이터를 **직교(orthogonal)** 상태로 유지하기 위해 데이터 모델 이전이 훨씬 흔함  
- **쓰기 시점 단순화와 읽기 시점 계산을 강하게 선호**하게 됨으로써 Automerge의 자동 병합 활용 극대화  
- 데이터 형태 이전(migration)에 대한 조언  
  - 빌드 과정에서 데이터 형태를 이전해야 함을 받아들이고, 첫 큰 이전을 두려워하지 않도록 초반에 시간을 들여 미리 연습  
  - 클라이언트의 읽기 시점 처리, 서버의 일괄 업그레이드 등 다양한 패턴 존재  
  - 이전 전후가 동일한지 확인할 편리한 불변식을 찾으면 작업이 훨씬 수월  
  - Ducking에서는 이전 전후 모든 프로젝트의 오디오를 내보내 **오디오 지문(audio fingerprint)** 으로 변경 여부를 확인, 큰 스키마 변경도 두렵지 않게 배포  
  
#### 리스트 재정렬 구현  
  
- 때로는 Automerge가 제공하지 않는 보장을 위해 **애플리케이션 계층 코드**로 더 강한 불변식 직접 작성 필요  
- Ducking의 **magnetic timeline**(재생할 클립의 정렬된 목록) 구현 시 문제 발생  
  - Automerge는 인덱스로 항목을 삭제·삽입하는 배열 연산은 제공하나, 기존 항목을 **원자적으로 재정렬(atomic re-order)** 하는 연산은 미제공  
- 알려진 해법 존재  
  - Martin Kleppmann이 원자적 리스트 재정렬 연산에 관한 논문 발표  
  - Liangrun Da와 함께 "Extending JSON CRDTs with Move Operations" 논문도 발표  
  - Automerge에 추가하는 **draft PR**도 있으나 아직 병합되지 않음  
- 단순한 재정렬 방식의 문제  
  - 현재 인덱스에서 객체를 삭제하고 목적지 인덱스에 다시 추가하는 방식  
  - 이 두 연산의 불변식을 결합해도 "동시 재정렬이 많을 때 객체가 목록에 정확히 한 번만 존재한다"는 원하는 불변식이 보장되지 않음  
  - 동시 삭제·추가가 여러 개면 객체가 목록 여러 위치에 존재할 수 있음 (Alice와 Bob이 각각 B를 delete+insert로 이동하면 두 삭제는 하나의 tombstone으로 합쳐지나 두 삽입은 각기 새 요소를 만들어 둘 다 살아남아 **B가 두 번 등장**)  
- "정확히 한 번" 불변식을 애플리케이션 계층에서 제공하는 직접 구현  
  - 클립이 타임라인에 삽입될 때 **semantic id** 부여  
  - 재정렬 시 위와 같이 삭제·삽입 연산 트리거  
  - 읽기 시점에 애플리케이션이 동일 semantic id의 중복을 스캔해, 삭제되지 않은 첫 항목을 임의로 선택하고 나머지는 무시  
  - 이로써 객체가 목록에 한 번만 존재하고 여러 독자가 항상 동일한 최종 상태에 도달  
- 리스트 재정렬은 Ducking에서 Automerge가 제공하지 않은 유일한 연산으로, PR 병합 시 애플리케이션 레벨 로직 불필요해질 전망  
  
#### 문서 이력 (Document history)  
  
- 좋은 멀티플레이어 UI는 **이력 관리 도구** 필요로, 협업자는 떠난 사이의 변경 확인·diff 댓글·구버전 비교 및 롤백 원함  
- Automerge는 문서 버전 이력을 추적하고 이력·비교를 다루는 훌륭한 기본 요소(primitive) 제공  
  - 단, 그 정보를 어떻게 노출하고 어떤 개념을 사용자에게 제시할지는 애플리케이션 개발자가 결정  
- Ink & Switch의 **Patchwork lab notes** 권장  
  - 사용자에게 브랜치를 노출하는 작업과 universal comments 작업이 특히 흥미로움  
- Ducking이 정착한 비교적 단순한 협업·이력 모델  
  - 사용자 정의 이름의 **checkpoint**가 있는 선형 버전 이력으로, checkpoint가 변경의 그룹화 단위이자 논의·diff·롤백의 단위  
  - 오디오의 특정 지점, 트랜스크립트의 영역, 버전 checkpoint에 연결 가능한 **댓글 스레드(comment thread)**  
- 아직 브랜치 도입의 충분한 이유는 없었으나 향후 유용할 가능성 언급  
  
#### 텍스트와 marks  
  
- 리치 텍스트 작업은 편집 가능한 텍스트 위에 커스텀 로직을 얹으려 할 때 특히 까다로운 문제  
  - 리치 텍스트와 멀티플레이어 소프트웨어 전반의 난점을 설명하는 **Peritext 논문** 권장  
- Automerge의 리치 텍스트 스키마는 **marks**(텍스트 범위에 적용되며 텍스트 편집 중에도 일관성 유지되는 주석) 포함  
  - 가장 흔히 굵게·기울임 같은 서식에 사용되나, 애플리케이션 고유의 커스텀 mark 생성도 가능  
- Ducking의 커스텀 mark 활용 두 가지  
  - 댓글 스레드의 대상이 된 트랜스크립트 영역 추적  
  - 트랜스크립트 단어의 타임스탬프 추적, 편집은 그대로 허용  
    - 트랜스크립션 서비스가 각 단어에 타이밍 정보 mark를 단 richtext 객체로 트랜스크립트를 Automerge에 저장  
    - 작은 오타로 단어 하나만 고치면 mark가 유지돼 모든 타이밍 정보 보존  
    - 문장 전체를 고치면 중간 mark 일부는 제거되나 문장 시작·끝의 mark는 유지돼 최소한의 대략적 타이밍 정보 확보  
- marks의 한 가지 제약은 datum이 단순 값(일반적으로 문자열)이어야 하고 멀티플레이어 병합이 되지 않는다는 점  
  - 트랜스크립트 타이밍 정보처럼 작고 불변인 데이터는 **JSON을 문자열로 직렬화**  
  - 댓글 스레드처럼 더 복잡하거나 가변적인 데이터는 mark에 **id만 저장**하고 실제 데이터는 문서 내 다른 곳에 보관  
- marks는 멀티플레이어 리치 텍스트 위에 애플리케이션 기능을 쌓는 훌륭한 토대 제공  
  
### 다음 글 — 시리즈 구성  
  
- 본 글은 Ducking 제작에 관한 3부작 중 2부  
  - 1부: 소프트웨어의 독특한 UI 디자인 설명  
  - 2부(본 글): Automerge 검토를 권하고 취미용 멀티플레이어 프로젝트 구축 가능성 제시  
  - 최종 3부 예정: Ducking 제작 경험 회고  
- 최종 3부 관련 언급  
  - **LLM 지원**을 작업 강화가 아닌 더 많은 스케치·해먹 시간 확보 목적으로 사용  
  - 소수만을 만족시키면 되는 **narrowcast 소프트웨어** 제작의 즐거움  
  
### 예상 질문  
  
#### 오디오 데이터는?  
  
- 모든 멀티플레이어 데이터는 Automerge에 저장되나, 기저 **오디오 blob**은 빠른 재생을 위해 Automerge에 두지 않고 별도 처리 필요  
- 목표는 새 협업자가 페이지 로드 후 **4초 이내** 청취·편집 시작으로, 데스크톱 앱 실행보다 빠르고 전체 프로젝트 파일 다운로드보다 훨씬 빠름  
  - 1시간 분량 에피소드는 고품질 스튜디오 녹음 4시간에 효과·배경음악을 더해 **약 1기가바이트** 오디오에 의존할 수 있음  
- 빠른 콜드 스타트를 위한 업로드 시 오디오 서비스 작업  
  - 원본 오디오 백업  
  - 음성을 트랜스크립트 뷰용으로 전사(transcribe)  
  - 타임라인 뷰용 파형(waveform) 생성  
  - 40분 녹음 중 1분만 사용하면 대부분의 클라이언트가 한두 개 작은 조각만 받도록 짧은 창(window)으로 분할(slice)  
  - 조각을 압축 포맷으로 트랜스코딩해, 고품질 오디오가 백그라운드에서 다운로드되는 중에도 즉시 재생 가능한 lossy 버전 제공  
- UI 데이터 계층은 사용자 의도를 따라 즉시 필요한 데이터의 빠른 버전과 실제 사용된 전체 오디오의 고품질 버전 로딩 관리  
  - 브라우저의 **IndexedDB API**가 다단계 캐싱과 content-addressable 저장에 유용하며, 자동 eviction 관리로 사용하면 남고 안 쓰면 사라짐  
- 이 모든 처리와 로컬 캐싱이 끝나면 나머지 UI는 오디오에 대한 빠른 무작위 접근을 가정하고 편집 워크플로우에 집중 가능  
  
#### 로컬 우선 앱이 아닌 서버+브라우저 UI를 만든 이유  
  
- 서버 없이 완전히 동작하는 Obsidian 같은 **local-first 앱** 선호, 특히 신뢰할 만한 탈출 경로를 제공하면서 클라우드 기반 유료 경험을 함께 갖춘 형태 선호  
- 초기에는 로컬 파일시스템 저장과 선택적 서버 동기화를 갖춘 **Tauri 앱** 옵션으로 시작  
  - 서버나 로컬 앱 어느 쪽이든 공급 가능한 데이터 인터페이스 기준으로 UI 구축  
  - 향후 어떤 자금도 lock-in으로 앱을 더 수익화하도록 유혹하지 못하게 하는 안전장치  
- 이후 이것이 SaaS가 아니라 파트너 및 소수 친구와 쓰고 싶은 것이라 판단  
  - 잘못 다룰 유인이 사라지고 영구 운영 비용이 낮아져 가장 쉬운 방식으로 제작 결정  
- 약 3초 콜드 스타트를 달성하자 누구도 네이티브 앱 다운로드·설치에 시간 낭비하길 원치 않게 됨  
- 오디오 앱이 현재의 데스크톱 전용 세계에서 동기화 옵션을 갖춘 **local-first 세계**로 곧장 건너뛰어, 중간의 10~20년 SaaS lock-in을 피할 수 있기를 희망  
  
#### Automerge는 안전하고 web-scale인가? 스타트업에 써야 하나?  
  
- 모른다고 **즐겁게** 답할수 있음, 이는 거부가 아니라 말 그대로 알 수 없다는 의미  
- 입사 당시 충돌 없는 실시간 멀티플레이어 편집은 마법이었고, 10년 전에는 특정 문제의 알려진 해법이 있었으나 자금 지원 팀과 여러 분야 전문성 필요  
  - 오늘날에는 의존성 하나를 받아 대체로 직관적으로 UI를 만들고 친구들과 실시간 협업 가능  
- 보안 측면에서 현재 Ducking은 제한된 네트워크 접근과 Automerge 서버 websocket 연결 생성 시 **인가(authorization) 단계**로 보호  
  - 사용자는 초대받지 않은 프로젝트를 발견하거나 편집할 수 없음  
  - 편집·댓글의 사용자 귀속은 일부만 안전하며 친구들이 못된 짓을 안 한다는 전제에 의존  
  - comment만 가능/edit 불가, 프로젝트 일부만 편집, 발견 가능성 제어 같은 **세분화된 권한**은 신중한 설계 작업 필요  
- Ink & Switch가 개발 중인 **Keyhive**는 암호학적으로 안전한 capability 기반 접근 제어 모델 제공  
  - 신뢰할 수 없는 사용자에게도 Automerge 앱을 공개 공유하기 쉽게 하나 아직 준비되지 않음  
  
#### Automerge가 더 나은가?  
  
- 이 분야의 다른 해법으로 **Yjs**가 존재하나, 무엇이 적합한지는 대신 평가해 줄 수 없음  
- 변치 않는 조언  
  - 문제를 깊이 고민하고, 마주칠 한계에 대해 대략적 계산(back-of-the-napkin)을 해 보며, 여러 대안으로 프로토타입을 만들어 보고, 어쩌면 자신의 문제가 그리 어렵지 않아 최신·최고급 해법이 필요 없을 수도 있음을 정직하게 인정  
- Ducking의 경우 빠른 프로토타입과 문서 탐색으로 Automerge가 해당 용도에 **충분히 성숙하고 성능 좋음** 확인  
- 더 중요하게는 **Ink & Switch 생태계**가 미학적으로 끌림  
  - Automerge가 단순 동기화·버전 관리 엔진이 아니라 소프트웨어를 더 안전·협업적·유연·즐겁고 개인적으로 만드는 더 큰 비전의 일부라는 점  
  - Keyhive 등의 성공을 바라며, 소수만을 위한 작지만 마법 같은 소프트웨어의 확산을 기대

## Comments



_No public comments on this page._
