하이브리드 업무 시대에 더욱 주목받는 실시간 문서 협업, 어떻게 가능해졌을까?

하이브리드 업무 시대에 더욱 주목받는 실시간 문서 협업, 어떻게 가능해졌을까?

전통적인 방식의 문서 동기화

클라우드 기반의 업무 환경이 정착되어감에 따라 과거 전통적인 방식의 협업은 지속적으로 변화를 맞고 있습니다. 특히 최근 팬데믹 상황으로 인해 재택근무 등 원격 업무가 일반화되어 감에 따라 이러한 상황은 더욱 가속화되었습니다.

전통적인 방식으로 동일 문서를 함께 작업하는 방식은 아래와 같았습니다.

1. 문서의 오너십을 가진 사람이 문서의 파트를 쪼개서 각기 다른 이들에게 분배하여 작업한 후 수거하여 직접 수작업으로 취합하기 → 전체 문서 내용에 대한 이해도가 있는 매니저가 필요하며, 그 한 명에게 많은 부하가 집중
2. 클라우드상에서 한 개의 문서를 여러 사람들이 시간차를 두고 작업하기 → 이전 순서의 작업자가 작업을 마칠 때까지 자신의 작업을 멈추고 대기
3. 각자 문서 사본을 가져가서 편집하고, 시간차를 두고 각자 자신의 순서에 편집한 부분만을 골라 Copy & Paste와 같은 수작업으로 merge 하기 → 대기 시간이 짧아지기에 다소나마 효율 상승
4. 각자 문서 사본을 가져가서 편집하고 merge-tool을 이용하여 merge 하기 → git과 같은 형상 관리 툴을 이용

이렇게 차례로 변화를 거듭해 갔죠. 내용을 보면 짐직할 수 있듯이, 전통적인 방식의 문서 동기화는 비실시간이 기본이었습니다. 실시간성을 배제한다면 git과 같은 merge-tool을 이용한 동기화는 훌륭한 대안이 될 수 있습니다.

동시 편집 기술의 대두

비실시간 문서 동기화의 문제는 다른 이의 작업물은 Merge가 이루어지기 전까지는 전혀 알 수 없다는 점입니다. 또한 일단 문서의 사본을 가져온 상태에서 작업하기 때문에 그만큼 추가적인 저장 공간 및 작업 툴을 필요로 합니다.

때문에 요즘은 온라인상에서 동시 편집 기능을 제공해 주는 것이 대세가 되고 있습니다. 예를 들어 Google의 Docs와 같은 워드프로세서는 온라인상에서 공유된 사용자들끼리 한 개의 문서를 동시에 편집할 수 있으며, 이때 실시간으로 여러 유저의 편집 내용이 바로 각자의 편집 화면에 반영됩니다. Docs 외에도 Confluence와 같은 Wiki-like 툴은 물론, MS Office와 같은 상업용 오피스 제품들도 동시 편집 기능을 지원하고 있는 추세입니다.

단순 문서 외에도 개발용 IDE 환경에서도 이런 동시 편집 솔루션은 존재합니다. 무료 솔루션으로 Atom의 경우엔 Teletype이, VSCode엔 LiveShare가 있고, 추가적인 서비스를 제공하는 유료 솔루션들도 존재하고 있습니다.

Teletype on Atom Editor

이렇게 동시 편집은 협업을 위해 필수적으로 요구되는 기능이기에 많은 툴들이 이를 도입하려고 하지만, 불행히도 동시 편집은 인간의 눈에 직관적인 것과 달리, 내부적으로는 무척 복잡한 문제를 안고 있습니다. 분산된 환경에서의 동시성은 언제나 모호함을 해결하기 위한 노력이 뒤따르게 되죠.

원격 문서 편집

실제로 물리적인 종이 문서 하나를 사이에 두고 2명의 작업자가 직접 펜을 들고 가필 및 편집을 진행하고 있다고 가정해 보시죠. 이럴 때의 동시성은 정말 물리적인 동시성이라 문서의 내용이 꼬이는 등의 문제가 생길 일이 없습니다. 하지만 이것을 네트워크상으로 옮기면 상황은 복잡해집니다.

일반적인 문서의 편집은 메모리상에 선형적으로 배열된 스트림과 그걸 변경하기 위한 오퍼레이션으로 표현됩니다.

예를 들어, HAT이라는 내용이 담긴 문서가 있고, 이를 CAT이라는 내용으로 수정하자고 해보죠. 그리고 이걸 위해 먼저 맨 앞에 C를 입력하고 이후 H를 지우는 과정을 거쳐서 수정을 한다 가정하면, 그럼 메모리와 오퍼레이션은 다음과 같이 표현될 것입니다.

HAT / 0 1 2/ H A T
  • insert 'C' to 0
CHAT / 0 1 2 3 / C H A T
  • delete at 1
CAT / 0 1 2/ C A T

이렇게 스트림과 오퍼레이션으로 표현한다면 원격으로 이런 작업을 하더라도 동일한 결과가 나올 것입니다. 유저의 오퍼레이션을 전달해 주는 중계(relay) 서버가 이런 역할을 해 주는 것이죠. 이를 도식화하면 다음과 같습니다.

HAT / 0 1 2/ H A T
  • Relay Server / insert 'C' to 0 / User
CHAT / 0 1 2 3 / C H A T
  • Relay Server / delete at 0 / User
HAT / 0 1 2/ H A T

문서의 위치가 로컬이든 원격지이든 적절한 스트림과 오퍼레이션만 정의한다면 동일한 결과를 나타낼 것입니다.

원격 문서 동기화 문제

원격 편집 기능 다음으로 생각해 볼 수 있는 것이 바로 편집자가 2명 이상일 경우입니다. 물론 비실시간으로 편집을 진행한다면 이 역시 문제가 될 여지가 없습니다. 하지만 2명 이상의 유저가 동시에 같은 문서를 편집하려고 할 때는 상황이 조금 복잡해집니다.

만약 2명의 유저가 원격으로 같은 문서를 대상으로 동시에 편집 작업을 진행하고 이 작업 진행 상황을 서로 동기화하려 한다면 어떻게 해야 할까요? 직관적으로 생각했을 때, 2명의 유저가 각자 입력한 동작을 상대에게 전달해 주고, 그 오퍼레이션을 각자의 스트림에 적용하면 서로가 한 동작이 동일하게 재현되기에 결과적으로 문서 역시 동일한 결과물을 나타낼 거로 생각할 수 있습니다.

User 1 ->
  • ⁢- Relay Server ->
⁢- User 2

예를 들어, 위의 2개 동작을 2명의 유저가 각자 나눠서 수행했다고 가정해 보겠습니다. 그럼 각자 문서의 스트림을 갖고 있는 상태로 첫 번째 유저가 ‘C’ 문자를 입력하고 두 번째 유저가 첫 번째 문자를 지우는 동작을 수행할 것이고, 각각의 동작은 오퍼레이션으로써 릴레이 서버에 의해 서로 상대에게 전달되어 각자의 스트림에 적용 될 것입니다.

User 1 : HAT / 0 1 2/ H A T, User 2 : HAT / 0 1 2/ H A T
  • insert 'C' to 0 -> Relay Server ⁢- delete at 0
CHAT / 0 1 2 3 / C H A T, AT / 0 1 2 3 / A T
  • Relay Server
HAT / 0 1 2/ H A T, CAT / 0 1 2/ C A T

실제로 이를 적용해 보면 서로 다른 문자열이 나타납니다. 첫 번째 유저는 ‘C’를 입력하자마자 해당 문자가 삭제되어 원래의 “HAT”으로 돌아갔고, 두 번째 유저는 H를 삭제하자 곧바로 그 위치에 ‘C’가 입력되어 “CAT”이 되었습니다. 즉, 결과적으로 서로 다른 스트림을 갖게 되는 것입니다.

이번에는 또 다른 예를 들어 보겠습니다. 같은 문서에서 User 2명이 각자 첫 번째 문자인 ‘H’를 지운다고 해 보죠. 그럼 둘 다 모두 “AT”라는 결과물이 나올 것으로 기대할 것입니다. 하지만 실제로 적용하면 문자 하나가 아닌, 2개가 삭제되어 둘 다 “T”라는 문서를 얻게 됩니다. 비록 결과적으로 두 유저가 같은 스트림을 얻었기에 동기화 자체는 되었다고 생각할지 모르겠지만, 사실 원하는 결과물과는 거리가 있기에 이것도 적절한 동작은 아니라 할 수 있습니다.

User 1 : HAT / 0 1 2/ H A T, User 2 : HAT / 0 1 2/ H A T
  • delete at 0 -> Relay Server ⁢- delete at 0
AT / 0 1 / A T, AT / 0 1 / A T
  • Relay Server
T / 0 / T, T / 0 / T

이런 잘못된 결과가 나온 이유는 여러 유저의 오퍼레이션을 변화 없이 그대로 전달하기만 해서 그렇습니다. 전달된 오퍼레이션은 아직 적용이 유예된 오퍼레이션이라, 실제로 오퍼레이션이 수행되면 실시간으로 문서가 변경되는데, 오퍼레이션은 그 적용 이전의 문서를 대상으로 하기 때문에 불일치한 문서를 대상으로 적용하여 잘 못 된 결과가 나온 것입니다.

이를 해결하기 위해서는 각 오퍼레이션을 그대로 전달하는 것이 아닌, 조금 더 지능적인 방식의 동기화 메커니즘이 필요합니다. 이런 목적으로 만들어진 방식의 대표적인 것이 OT와 CRDT입니다.

OT(Operational Transformation) 방식

OT 방식은 오퍼레이션을 릴레이 서버가 있는 그대로 다른 사용자에게 전달하는 것이 아닌, 오퍼레이션 적용 과정에서 일어나는 스트림의 변화를 고려해서 오퍼레이션을 적절히 변형시켜서 전달하는 방식입니다. 이때 오퍼레이션이 수행된 시간상의 순서를 고려해 이에 따라 오퍼레이션이 적용될 우선순위가 부여되고, 앞서 적용될 오퍼레이션이 미칠 영향이 후순위의 오퍼레이션에 보정 정보로 작용합니다.

실제로 OT를 적용하게 되면 앞선 동작들은 다음과 같이 보정되게 됩니다.

User 1의 맨 앞에 ‘C’를 입력한 오퍼레이션과 User 2의 첫 문자 삭제 오퍼레이션을 서버가 각각 수집합니다. 다음으로 오퍼레이션이 수행된 시간에 맞춰 정렬을 합니다. 이때 ‘C’ 문자 입력 오퍼레이션이 첫째 문자 삭제 오퍼레이션보다 먼저 수행되었다고 가정하면, ‘C’ 문자 입력으로 인해 이후 문자들은 모두 1씩 인덱스가 뒤로 밀리게 되는 것입니다. 결과적으로 첫 문자 삭제 오퍼레이션은 두 번째 문자 삭제로 인덱스가 보정되어 전달되게 되는 거죠. 그 결과 실제로 발생한 오퍼레이션과는 조금 달라진 보정된 오퍼레이션이 각각의 유저에게 전달되어 스트림에 적용되게 됩니다.

User 1 : HAT / 0 1 2/ H A T, User 2 : HAT / 0 1 2/ H A T
  • insert 'C' to 0 -> Relay Server / OT Engine ⁢- delete at 0
CHAT / 0 1 2 3 / C H A T, AT / 0 1 / A T
  • delete at 1 ⁢- Relay Server / OT Engine -> insert 'C' to 0
CAT / 0 1 2/ C A T, CAT / 0 1 2/ C A T

다음으로 User 1과 User 2가 똑같은 위치의 문자를 삭제했을 경우에도 보정 결과 삭제 오퍼레이션을 무시해 버릴 수 있게 됩니다.

User 1 : HAT / 0 1 2/ H A T, User 2 : HAT / 0 1 2/ H A T
  • delete at 0-> Relay Server / OT Engine ⁢- delete at 0
AT / 0 1 / A T, AT / 0 1 / A T
  • operation ignore ⁢- Relay Server / OT Engine -> operation ignore
AT / 0 1 / A T, AT / 0 1 / A T

이런 OT 방식은 꽤 직관적이고 언뜻 보기에 구현도 어려워 보이지 않을 것 같아 보입니다. 때문에 초기의 동시 편집 기능이 도입될 당시엔 무척 인기가 있었습니다. 우리가 흔히 사용하고 있는 Google의 Wave 에디터가 바로 OT 방식으로 구현된 대표적인 예입니다. Google Docs나 Microsoft Office 역시도 OT 방식을 채택하고 있습니다.

OT 방식의 가장 큰 특징은 바로 중앙집중적인 처리 방식입니다. Google의 경우엔 클라우드 스토리지를 기반의 서비스를 제공하고 있기에 어차피 문서가 존재하는 곳으로 데이터가 모일 거라 가정하여 그곳에서 교통정리를 해 주면 됩니다. Microsoft Office 역시도 OneDrive 기반의 클라우드 서비스 이용자에 한해서 동시 편집 기능을 제공해 주고 있습니다.

OT 방식의 문제

OT의 장점은 곧 단점과 연결됩니다. 동시 편집을 위한 Merge 작업이 한곳에서 이루어지기 때문에 트래픽이 증가하며 서버의 부하가 가중된다는 것입니다. 실질적으로 저장을 하는 순간 외엔 굳이 스토리지가 있는 곳으로 데이터가 전송될 필요가 없음에도 동시 편집을 위해 매번 스토리지가 있는 곳으로 데이터가 전송되는 것이죠. 그냥 동시 편집 작업자끼리만 서로 데이터를 교환하면 충분한데도 말입니다. 이 트래픽 증가는 곧 서버 비용 증가로 이어지게 됩니다.

서버 입장뿐 아니라 사용자 입장에서도 불편함을 느낄 수 있습니다. 이를테면 같은 네트워크상에 존재하는 사용자끼리 동시 편집을 하는 상황에서조차 멀리 떨어진 중계 서버로 일단 오퍼레이션 패킷이 전송되어 응답을 받아야 하기 때문입니다. 이는 응답 시간이 추가적으로 발생해서 사용에 지장을 주게 됩니다.

CRDT(Conflict-Free-Replicated Data Types) 방식

2006년 이전까지는 OT가 주류였으나 최근엔 CRDT 방식이 더 각광을 받고 있습니다. CRDT의 장점은 동시성이 요구되는 당사자끼리만 데이터를 교환하면 된다는 것입니다. 이는 서버에 대한 의존성과 부하를 줄이는 효과가 있으며, 각 유저들의 편집 작업 시 반응 속도 향상에도 도움이 됩니다.

OT 방식이 편집 시간과 인덱스를 기반으로 오퍼레이션을 정의하는 반해, CRDT는 이를 무시하고 스트림 상의 각 문자에 고유한 ID를 부여하고, 이를 기반으로 오퍼레이션을 정의한다는 차이가 있습니다.

간단히 문자열 스트림에 순서대로 1, 2, 3과 같이 숫자를 부여하고, 새롭게 문자를 삽입하면 삽입된 위치에 맞춰 각 숫자 사이의 값을 새로 부여한다고 가정해 보죠. 그럼 앞서 예를 들었던 상황이 다음과 같이 표현되게 됩니다.

HAT / 0 1 2/ H A T
  • insert 'C' to id 1 position (new id 0.5)
CHAT / 0.5 1 2 3 / C H A T
  • delete id 1
CAT / 0.5 2 3 / C A T

“HAT” 문자열은 각각 1, 2, 3의 ID 값이 부여되었고, 맨 앞에 ‘C’ 문자를 삽입하면 기존의 문자들의 인덱스가 한 칸씩 뒤로 밀렸던 이전 방식과 달리, ID 값은 변하지 않고 대신 새로 입력된 ‘C’ 문자가 삽입된 위치의 ID 값인 1보다 더 작은 값인 0.5가 부여된 것을 알 수 있습니다. 그리고 삭제 오퍼레이션 역시 인덱스값이 아니라 ID 값을 대상으로 합니다.

이제 User 2명이 앞서 예로 든 상황처럼, 각자 이 오퍼레이션을 나눠서 수행한다고 하면 어떻게 될까요?

CRDT는 각 유저끼리 직접 오퍼레이션을 주고받기에, 중간에 오퍼레이션을 조율하여 가공하는 별도의 엔진이 필요가 없습니다. 즉, 해당 오퍼레이션은 수정 없이 바로 다른 유저의 문서에 적용됩니다. 이때 위치가 아닌, 특정 ID 1의 위치에 ID 0.5인 문자 ‘C’가 삽입되기에 적용 순서와 상관 없이 정확한 위치에 해당 문자가 삽입되게 되는 것입니다.

User 1 : HAT / 1 2 3/ H A T, User 2 : HAT / 1 2 3/ H A T
  • insert 'C' to id 1 position (new id 0.5), delete id 1
CHAT / 0.5 1 2 3 / C H A T, AT / 1 2 / A T
  • delete id 1, insert 'C' to id 1 position (new id 0.5)
CAT / 0.5 1 2 / C A T, CAT / 0.5 1 2 / C A T

다음으로 User 1과 User 2가 똑같은 위치의 문자를 삭제했을 경우에도 위치가 아닌 ID를 기반으로 삭제하기에 이미 삭제된 ID는 더 이상 존재하지 않기 때문에 중복 삭제 문제도 발생하지 않습니다.

User 1 : HAT / 0 1 2 / H A T, User 2 : HAT / 0 1 2 / H A T
  • delete at 0 (id 0), delete at 0 (id 0)
AT / 1 2 / A T, AT / 1 2 / A T
  • delete at 0 (id 0), delete at 0 (id 0)
AT / 1 2 / A T, AT / 1 2 / A T

이렇게 위치 기반이 아닌, ID 기반으로 동기화할 경우엔 굳이 OT 방식과 같이 중간에 오퍼레이션을 보정해 줄 필요 없이 바로 각자의 문서에 다른 유저의 오퍼레이션이 적용이 가능하다는 것을 알 수 있습니다.

CRDT 방식의 문제

CRDT도 OT에 비교해 단점들이 있습니다. 첫째는 OT 방식보다 많은 메모리를 소모한다는 것입니다. 문자열 스트림 외에도 추가적으로 고유 값을 저장하는 메모리와 이를 Tree 구조로 관리하기 위한 메모리를 필요로 합니다. 일반적으로 많이 사용되는 CRDT 알고리즘들은 대체로 문서 용량의 2~3배에 달하는 메모리를 필요로 하죠.

다음으로는 좀 더 심각한 문제입니다. 바로 Peer-to-Peer 통신이 항상 가능하지 않다는 점입니다. 보안의 중요성이 날로 고조되어 가는 상황에서, 기업들뿐 아니라 개인들 역시 점점 가정 내 개인 PC의 네트워크 보안에도 신경을 쓰는 상황에서, 방화벽 등에 의해 Peer 간 직접 접속이 점점 힘들어지고 있는 상황입니다. 이는 NAT 환경에서의 Peer-to-Peer 통신 문제와는 차원이 다른 문제로, 근본적으로 Peer 간 접속이 불가능한 환경이 늘어가고 있다는 것이 핵심입니다. OT의 중앙 집중 방식의 장점이 곧 단점으로 변모했듯이, CRDT 역시 Peer 간 직접 통신이라는 장점이 단점으로 작용하는 아이러니가 발생한 것입니다. 물론 이런 상황을 해결하기 위한 방법은 존재합니다. 하지만 이 방법은 결국 패킷 전송을 중계하는 서버를 이용할 수밖에 없는데, 결국 특정 서버에 부하가 증가하게 되어 CRDT의 핵심 장점이 무색해져 버리는 셈입니다.

다음은 CRDT가 시간을 기반으로 하는 것이 아닌 고유 ID를 기반으로 하기에 이것의 Merge 과정에서 실시간성이 모호해진다는 점이며, 이로 인해 동기화 결과 문자열이 섞여 의도하지 않은 결과물이 나온다는 문제 등이 있습니다. 이 모호함을 해결하기 위해 CRDT는 OT보다 더 복잡한 구현을 필요로 하며, 어떤 케이스는 사실상 근본적인 해결이 힘들기도 합니다. 그럼에도 지속적으로 문제를 보안하기 위한 노력이 이어지고 있고, 유용한 다양한 알고리즘 기법들이 나오고 있습니다. (Logoot, LSEQ, RGA, Treedoc, WOOT, Astrong 등)

삼성SDS 브리티웍스 -
일하는 시간은 줄이고, 일하는 실력은 더 높이는 기업용 업무 자동화 및 협업 솔루션

동시 편집 기술의 전망

OT든 CRDT든, 현재까지 완벽한 알고리즘이 존재한다고 말하기는 힘들었던 것이 현실입니다. 둘 다 다소간의 문제점들을 갖고 있고, 둘 다 지속적으로 개선을 위한 노력이 지속되고 있기 때문입니다. 현재로선 OT가 더 안정적인 동작을 하고 있긴 하지만, 이는 OT가 더 우수해서라기보다는 중앙집중식 처리라는 환경적인 요인이 더 크게 작용합니다. Peer-to-Peer에 비해 발생할만한 문제 상황이 덜 복잡하기 때문입니다. 하지만 이는 양날의 검으로, 중앙집중식 처리의 근본적인 한계가 존재할 수밖에 없습니다. 때문에 지금으로선 OT보다 CRDT가 더 전망이 밝아 보입니다.

일반적으로 클라우드 기반의 툴들이 동시 편집을 강력한 기능으로 효율성을 극대화시켰으며, 이에 따라 기존의 전통적인 툴들도 협업을 위한 속속 동시 편집 솔루션을 도입하려 노력하고 있습니다.

협업을 위한 시스템을 구축할 때, 동시 편집 기능을 탑재한다면 훨씬 경쟁력 있는 시스템이 될 것임은 자명합니다. 때문에 앞으로는 이런 시스템을 위한 솔루션을 개발할 때는, 설계 단계부터 CRDT를 적용하는 것을 고려해 보는 것도 경쟁력 있는 개발자로서의 필수 소양이 될 것이라 생각합니다.



References
[1] CRDTs and the Quest for Distributed Consistency : Martin Kleppmann (QCon London 2008)
https://www.infoq.com/presentations/crdt-distributed-consistency/
[2] A private and secure real-time collaborative text editor (Conclave-team case study)
https://conclave-team.github.io/conclave-site/




▶   해당 콘텐츠는 저작권법에 의하여 보호받는 저작물로 기고자에게 저작권이 있습니다.
▶   해당 콘텐츠는 사전 동의 없이 2차 가공 및 영리적인 이용을 금하고 있습니다.


이 글이 좋으셨다면 구독&좋아요

여러분의 “구독”과 “좋아요”는
저자에게 큰 힘이 됩니다.

subscribe

주경민
주경민

에스코어㈜ 개발플랫폼그룹

Embedded와 PC 및 엔터프라이즈 기반의 개발 플랫폼 및 IDE 개발 전문가로 과거 Tizen IDE 및 Tizen-RT IDE를 개발했으며, 현재 Web 기반의 원격 IDE에 동시 편집 솔루션 개발을 담당하고 있습니다.