operational-transformation

3 개의 포스트

figma

피그마의 멀티플레이어 (새 탭에서 열림)

Figma는 Google Docs처럼 복잡한 OT(Operational Transformation)를 적용하는 대신, 디자인 문서의 구조에 맞춘 단순한 자체 멀티플레이어 시스템을 구축했다. 클라이언트와 서버는 WebSocket으로 변경 사항을 동기화하고, 서버가 문서별 단일 조정자로 동작해 충돌을 관리한다. 이 방식은 실시간 협업뿐 아니라 오프라인 편집과 재접속까지 지원하면서도 Figma의 데이터 모델에 맞는 구현 단순성을 유지했다. ## 웹 기반 협업을 선택한 이유 - 멀티플레이어 기능이 있으면 파일을 내보내거나 복사본을 이메일로 주고받고, 변경 사항을 수동으로 동기화할 필요가 없다. - 링크 하나만으로 여러 사람이 현재 디자인 상태를 확인할 수 있다. - 디자이너뿐 아니라 카피라이터, 개발자 등도 같은 문서에 참여할 수 있다. - 초기에는 실시간 협업이 “디자인을 망치는 기능”으로 여겨졌지만, 웹 생산성 도구에는 자연스러운 기본 기능이 되었다. ## Figma의 클라이언트·서버 구조 - 웹 클라이언트는 WebSocket을 통해 멀티플레이어 서버 클러스터와 통신한다. - 문서마다 별도의 서버 프로세스를 두고, 해당 문서를 편집하는 사용자들이 같은 프로세스에 연결된다. - 문서를 열 때 클라이언트가 먼저 파일 전체를 내려받는다. - 이후 변경 사항은 양방향 WebSocket 연결을 통해 실시간으로 전송된다. - 댓글, 사용자, 팀, 프로젝트 같은 데이터는 멀티플레이어 시스템이 아니라 Postgres와 별도 동기화 시스템으로 관리한다. - 문서 편집과 기타 애플리케이션 데이터는 성능, 오프라인 지원, 보안 요구 사항이 다르기 때문에 구현을 분리했다. ## 오프라인 편집과 재접속 - 사용자는 네트워크가 끊긴 상태에서도 임의의 시간 동안 계속 편집할 수 있다. - 다시 온라인이 되면 클라이언트는 서버에서 최신 문서 사본을 받는다. - 그 위에 오프라인 동안 발생한 로컬 변경을 다시 적용한다. - 이후 새로운 WebSocket 연결을 통해 서버와 변경 사항을 계속 동기화한다. - 연결과 재연결 자체는 단순하게 만들고, 멀티플레이어의 핵심 복잡성은 이미 연결된 문서에 동시에 변경 사항이 들어오는 상황에 집중했다. ## OT와 CRDT 대신 자체 방식을 택한 이유 - OT는 Google Docs 등에서 널리 사용된 표준적인 협업 알고리즘이다. - 여러 사용자의 동시 작업을 변환해 충돌을 해결할 수 있지만, 구현과 검증이 복잡하다. - Figma는 텍스트 편집기와 달리 계층적인 디자인 객체를 편집하므로, 일반적인 OT 모델을 그대로 적용할 필요가 없다고 판단했다. - 스타트업으로서 빠르게 기능을 개발하고 실험하려면 더 단순한 구조가 유리했다. - Figma는 디자인 문서의 데이터 구조와 편집 패턴에 맞춘 전용 동기화 방식을 만들었다. ## 프로토타입을 통한 설계 검증 - 실제 제품 코드에 바로 적용하지 않고, 여러 클라이언트와 서버를 시뮬레이션하는 별도의 웹 기반 실험 환경을 만들었다. - 프로토타입에서는 여러 사용자의 상태와 변경 사항을 시각적으로 확인할 수 있었다. - 오프라인 클라이언트, 느린 네트워크, 제한된 대역폭 등 다양한 상황을 쉽게 재현했다. - 이를 통해 협업 알고리즘과 데이터 구조를 빠르게 비교하고 실험했다. - 설계가 확정된 뒤 프로토타입에서 검증한 아이디어를 기존 코드베이스에 이식했다. ## 서버 중심의 동기화 모델 - 하나의 문서에 연결된 사용자들은 같은 서버 프로세스를 공유한다. - 서버는 문서에 들어오는 변경 사항을 순서대로 처리하고 다른 클라이언트에 전달한다. - 클라이언트는 처음 받은 문서 상태를 기준으로 로컬 변경을 수행하면서 서버의 업데이트를 계속 반영한다. - 서버를 문서의 조정자로 두면 여러 클라이언트가 서로 직접 충돌을 해결할 필요가 줄어든다. - 동시에 발생한 변경의 처리 순서는 서버가 정하며, 모든 클라이언트가 최종적으로 같은 문서 상태에 도달하도록 한다. ## 이 접근법의 핵심 장점 - 범용 협업 알고리즘보다 Figma의 객체 중심 데이터 모델에 맞게 단순하게 구현할 수 있다. - 온라인 편집뿐 아니라 오프라인 작업과 재접속도 지원한다. - 별도 프로토타입으로 다양한 장애 상황을 반복적으로 테스트할 수 있다. - 문서 편집 동기화와 서비스 메타데이터 동기화를 분리해 각각의 요구 사항에 맞게 최적화할 수 있다. 실용적으로는 모든 협업 애플리케이션이 OT나 CRDT를 그대로 도입해야 하는 것은 아니다. 데이터 구조가 명확하고 충돌 패턴이 제한적이라면, 서버 중심의 단순한 프로토콜과 충분한 시뮬레이션 테스트를 결합하는 편이 더 빠르고 유지보수하기 쉬울 수 있다.

figma

순서가 지정된 시퀀 (새 탭에서 열림)

Figma는 실시간 협업에서 여러 사용자가 객체의 순서를 동시에 변경해도 모든 클라이언트가 동일한 최종 상태에 도달하도록 해야 했다. 처음에는 Operational Transformation(OT)을 고려했지만, 텍스트 편집에 필요한 고급 기능과 구현 복잡도가 Figma에는 과도하다고 판단했다. 대신 각 객체에 순서를 나타내는 분수형 인덱스를 부여하고 정렬하는 방식을 사용해, 단순성과 안정성을 확보했다. ## 실시간 순서 편집의 문제 - Figma의 문서, 그룹, 컴포넌트 등은 자식 객체의 순서가 있는 목록을 가진다. - 사용자는 객체를 삽입·삭제하거나 드래그해 순서를 변경할 수 있다. - 각 클라이언트는 편집을 즉시 로컬에 적용한 뒤 서버로 전송한다. - 네트워크 상황에 따라 각 클라이언트가 작업을 서로 다른 순서로 받을 수 있다. - 따라서 작업 적용 순서가 달라도 모든 클라이언트의 문서가 동일해지는 eventual consistency가 필요하다. ## Operational Transformation의 접근법 - OT는 동시 작업이 서로의 위치와 의미를 깨뜨리지 않도록 작업을 변환한다. - 예를 들어 텍스트 `bcde`에 대해 한 사용자가 앞에 `x`를 삽입하고 다른 사용자가 `bc`를 삭제하면, 삭제 위치를 삽입만큼 보정한다. - 서버와 클라이언트는 다른 작업을 기준으로 각 연산을 변환해 같은 결과를 만든다. - OT는 오래된 협업 편집 알고리즘이며 텍스트 편집기에서 널리 사용됐다. ### OT의 장단점 - 장점 - 매우 큰 시퀀스에서도 성능과 메모리 효율이 좋다. - 같은 위치에 동시에 삽입된 문자열을 서로 끼워 넣지 않고 연속된 덩어리로 정렬할 수 있다. - 단점 - 구현과 정확성 검증이 어렵다. - 일반적으로 객체 이동을 삭제 후 삽입으로 처리한다. - 연산 종류가 늘어날수록 모든 연산 쌍 간 변환 규칙이 필요해 복잡도가 크게 증가한다. - Figma는 거대한 시퀀스나 삽입 결과의 비인터리빙이 필요하지 않았고, 객체 이동이 빈번했기 때문에 OT를 선택하지 않았다. ## 분수형 인덱싱 - 각 객체에 `0과 1 사이의 위치값`을 부여하고, 이 값을 기준으로 자식 객체를 정렬한다. - 두 객체 사이에 삽입할 때는 양쪽 인덱스의 평균을 새 객체의 인덱스로 사용한다. - 예를 들어 `0.2`와 `0.6` 사이에 삽입하면 `0.4`를 사용할 수 있다. - 인덱스는 64비트 부동소수점 대신 임의 정밀도 분수로 저장해 반복 삽입으로 정밀도가 고갈되는 문제를 피한다. - Figma는 인덱스를 문자열로 저장하고 문자열 조작으로 평균을 계산한다. - 저장 공간을 줄이기 위해 `0.`을 생략하고, 숫자만이 아니라 전체 ASCII 범위를 사용한 base 95 표현을 적용한다. ## 분수형 인덱싱의 장단점 - 장점 - 알고리즘이 단순하고 이해·구현하기 쉽다. - 객체를 이동할 때 위치값 하나만 변경하면 된다. - 이동을 삭제와 삽입으로 나눌 필요가 없다. - 단점 - 반복적인 삽입과 재배치로 인덱스 문자열이 길어질 수 있다. - 여러 클라이언트가 같은 위치에 동시에 삽입하면 새 객체들이 서로 섞일 수 있다. - 동일한 두 인덱스 사이의 평균을 다시 계산할 수 없다. ## 동시 삽입 충돌 처리 - 동일한 위치에 두 클라이언트가 동시에 객체를 삽입하면 두 객체가 같은 인덱스를 가질 수 있다. - 서버가 두 객체에 동일한 위치값이 생기지 않도록 두 번째 삽입에 고유한 위치를 부여한다. - 인덱스 길이 증가는 Figma에서 객체 수와 사용자 활동이 실용적인 범위로 제한되므로 큰 문제가 되지 않는다. - 디자인 문서에서는 동시에 삽입된 객체가 서로 겹치지 않는 경우가 많아, 순서가 일부 인터리빙되는 것도 허용 가능하다. Figma의 사례는 가장 정교한 알고리즘보다 제품의 요구사항에 맞는 단순한 알고리즘이 더 유리할 수 있음을 보여준다. 대규모 텍스트 편집처럼 강한 순서 보장이 필요하지 않다면, 분수형 인덱싱은 구현·유지보수 비용이 낮고 객체 이동에도 효율적인 실용적 선택이다.

figma

Figma의 멀티플레이어 편집 (새 탭에서 열림)

Figma는 파일을 내려받아 로컬에서 편집한 뒤 전체 문서를 저장하는 방식이 협업 환경과 근본적으로 맞지 않는다는 문제를 해결하기 위해 실시간 멀티플레이어 편집을 도입했다. 사용자의 변경 사항을 서버에 전송하고 실시간으로 다른 사용자에게 브로드캐스트함으로써 최신 버전 보장, 덮어쓰기 방지, 원격 협업과 리뷰 개선을实现했다. 구현 과정에서는 실행 취소·다시 실행, 충돌 해결, 성능과 파일 형식 개선 등 복잡한 기술적 과제를 해결해야 했다. ## 기존 저장 방식의 한계 - 초기 Figma는 문서를 브라우저로 내려받아 로컬에서 편집하고, 일정 주기마다 전체 문서를 다시 업로드했다. - 개인 사용자에게는 구현과 이해가 쉬웠고, Dropbox 같은 동기화 서비스와도 유사해 익숙한 방식이었다. - 하지만 팀 기능이 추가되면서 다음 문제가 발생했다. - 여러 사용자가 서로의 작업을 모르고 저장해 변경 사항을 덮어씀 - 저장이 완료되기 전에 공유한 링크를 열면 이전 버전이 표시됨 - 버전 기록에는 모든 저장이 남았지만, 협업 중 발생하는 혼란 자체를 막지는 못함 - 한 명씩 편집권을 넘기는 ‘baton-passing’ 방식도 검토했지만, 동시 편집만큼 단순하고 자연스럽지는 않았다. ## 실시간 멀티플레이어 모델 - 각 사용자의 변경 사항을 서버로 보내고, 서버가 이를 다른 사용자에게 실시간으로 전달한다. - 서로 다른 객체나 속성에 대한 변경은 독립적으로 처리한다. - 동일 객체의 동일 속성을 동시에 수정하면 최신 변경을 적용하는 방식으로 충돌을 해결한다. - 이 구조를 통해 사용자는 항상 최신 문서를 보고, 편집 중인 파일 버전을 따로 확인하거나 충돌을 조정할 필요가 줄어든다. - Figma는 이 기능이 협동 멀티플레이어 게임과 비슷하다는 점에서 “multiplayer”라는 이름을 붙였다. ## 멀티플레이어 실행 취소와 다시 실행 - 단일 사용자 환경에서는 실행 취소가 “내가 방금 한 작업을 되돌리는 것”으로 정의되지만, 여러 사용자가 같은 객체를 수정하면 의미가 복잡해진다. - 다른 사용자의 변경 이후 실행 취소를 수행할 때 자신의 과거 변경을 다시 덮어써서는 안 된다. - Figma는 다음 원칙을 기준으로 동작을 설계했다. - 사용자가 여러 작업을 실행 취소한다. - 중간 상태에서 무언가를 복사한다. - 다시 실행해 현재 상태로 돌아온다. - 이 과정만으로 문서 내용이 달라져서는 안 된다. - 단순히 과거 상태를 복원하는 방식이 아니라, 다른 사용자의 후속 변경을 보존하는 방향으로 redo를 설계해야 했다. ## 충돌 해결의 복잡성 - 한 객체의 여러 속성이 함께 변경되어야 하는 경우, 특정 속성 하나의 변경만으로 전체 상태를 덮어쓰지 않도록 세밀한 병합이 필요했다. - 겉보기에는 한 객체만 수정하는 작업이 실제로는 다른 객체에도 영향을 줄 수 있었다. - 이 때문에 서로 무관해 보이는 객체를 두 사용자가 동시에 편집해도 충돌이 발생할 수 있었다. - 일부 레이아웃 기능은 이러한 협업 상황에 맞게 동작 방식을 수정해야 했다. - 단순한 “최신 변경 우선”만으로는 충분하지 않고, 작업 간 의존성과 여러 객체에 걸친 부수 효과까지 고려해야 했다. ## 성능과 파일 형식 개선 - 전문 디자인 도구 수준의 실시간 협업을 위해 지속적인 측정과 성능 튜닝이 필요했다. - 기존 파일 형식은 작은 변경 사항을 효율적으로 표현하기에 적합하지 않아 파일 포맷 자체를 개편했다. - 전체 문서를 반복해서 전송하는 대신, 작은 변경 메시지를 빠르게 전달하고 처리하는 것이 중요했다. - 멀티플레이어 기능은 에디터의 핵심 구조에 영향을 주는 대규모 기술 투자였지만, 향후 협업 기능을 위한 기반이 되었다. ## 협업 경험을 단순하게 만든 UI - 멀티플레이어는 기능을 추가했지만, 기존에 사용자들이 협업을 위해 사용하던 복잡한 우회 절차를 없애 UX를 오히려 단순하게 만들었다. - 현재 문서에 참여 중인 사용자의 마우스 커서와 선택 영역을 표시한다. - 누가 문서에 있는지 확인할 수 있다. - 다른 사람이 어느 부분을 작업 중인지 알 수 있다. - 커서를 특정 객체 옆에 두어 다른 사람의 주의를 끌거나 대상을 가리킬 수 있다. - 모든 사용자는 화면 오른쪽 위에 아바타로 표시된다. - 아바타는 참여자 확인뿐 아니라 프레젠테이션에도 활용된다. - Figma는 한 명의 발표자를 강제하고 모두가 그 사람을 따라가게 하는 방식을 시험했지만, 사용자의 선택권을 제한한다고 판단했다. - 대신 다른 사용자의 아바타를 클릭해 발표를 따라가는 선택적 방식이 더 자연스럽다고 보았다. ## 실용적인 결론 실시간 협업 기능은 단순히 변경 사항을 동기화하는 문제가 아니라, 실행 취소·충돌 병합·파일 포맷·레이아웃 모델·사용자 인터페이스까지 함께 설계해야 하는 시스템 문제다. 협업 제품을 만들 때는 기존의 개인용 저장 모델에 기능을 덧붙이기보다, 처음부터 동시 편집과 사용자 간 맥락 공유를 핵심 구조로 고려하는 것이 바람직하다.