많은 분들이 Jira를 사용해보셨을 거라 생각합니다. 개발팀이 명확하게 프로젝트를 관리하고 커뮤니케이션할 수 있도록 돕는 훌륭한 제품이지만, 실시간으로 다수의 사용자가 협업을 하는 경우에는 꽤나 불편한 문제들이 발생합니다.
실시간 동기화
바로 실시간 동기화 문제입니다. 예컨데 동시에 여러 사람이 같은 지라 이슈 리스트를 기반으로 일을 할 때, 누군가가 이슈의 순서를 바꾸면 다른 사람들에겐 새로고침을 하기 전까지 즉각적으로 반영되지 않습니다. 혹은 특정 이슈의 상세 페이지 내에 머무르고 있을 때 다른 사람이 상태를 변경하는 경우도 마찬가지로 내 화면에는 반영되지 않습니다.
Jira는 원래 다수의 사람이 다수 개의 이슈를 관리하기 위한 툴이라는 점에서, 당연히 실시간으로 변화사항이 반영되어야 할 것 같은데 그렇지 않습니다. 왜일까요?
답은 단순합니다. 쉽지 않은 일이기 때문입니다.
왜 어려운가
예를 들어, 데이터 A가 변경되었다고 가정해 봅니다. 데이터 A의 변경사항을 모든 접속자에게 보내주면 그만일 것 같지만 실상은 그렇지 않습니다. 비즈니스 로직상 데이터 A가 다른 데이터와 완전히 독립적이라면 모르겠습니다만, 실제로는 그렇지가 않은 경우가 훨씬 많습니다. 즉, A의 변경사항이 동시에 데이터 B, C의 변경을 유발합니다. 데이터 B, C는 각각 다시 D, E의 변경을 유발합니다.
게다가 이 모든 데이터 변경은 각각의 권한 설정에 따라 필요한 사람에게만 전파되어야 합니다. 또한 접속하지 않은 유저에게는 변경사항을 보낼 필요가 없고, 접속을 해 있더라도 특정 데이터를 그릴 필요가 없는 페이지에 있는 경우도 걸러져야 합니다.
결국 경우의 수는 파생 데이터 수와 유저 수을 곱한 값을 기본으로 하지만, 권한 설정이나 유저의 상태값까지 더해져 기하급수적으로 늘어납니다. 개발팀 입장에서 이러한 실시간 데이터 동기화 이슈를 다루는 것은 매우 어렵고, 다른 말로는 비쌉니다.
현재 Yess에서는 어떤 참여자가 데이터의 수정을 가했을 때, 그 변경사항을 볼 권한이 있고 그 변경사항이 드러나는 페이지에 있는 모든 참여자에게 실시간으로 동기화됩니다. 이번 글에서는 초기 스타트업인 Yess에서 이와 같이 어려운 문제를 어떻게 해결했는지 공유 드리고자 합니다.
기반 기술의 선정
Yess의 요구사항
우리는 Yess를 에이전시가 내부 프로세스를 관리하는 동시에 클라이언트가 필요한 절차에 참여하며 협업하는 공간으로 설계하였습니다. 그리고 우리는 무엇보다 고객들로 하여금 클라이언트에게 “새로고침 한 번 해주시겠어요?”라는 구시대적인 요청을 하지 않게 만들고 싶었습니다.
미팅을 통해 실시간으로 노트를 작성하고, 작업물을 공유하고, 또 피드백을 받으며 빈번하게 변경사항이 발생하는 업무 특성상 Yess에서 데이터의 변경 사항이 실시간으로 반영되는 것은 굉장히 중요한 일입니다. 고객의 비즈니스를 돕는 B2B SaaS로서 이 문제는 단지 개발의 문제가 아니라, 우리 고객에 대한 클라이언트의 신뢰 이슈를 발생시킨다는 점에서 매우 크리티컬합니다.
더불어 하나의 프로젝트에도 여러 주체가 참여하기 때문에 접근 권한의 제어 역시 필수적이었습니다. 어렵지만 권한 체계를 포함한 실시간 동기화를 해내야 했습니다.
기술 전략의 수립
우리 개발팀의 목표는 3가지였습니다.
1.동시성 문제 해결: 서로 다른 두 작업자가 거의 '동시' 에 변경사항을 발생시켰을 때 둘 중 하나를 순서를 정해 주고, 두 의도가 잘 반영되어야 합니다.
2.선택적 구독: 클라이언트가 필요한 것들만 골라서 동기화 받을 수 있도록 해 주어야 합니다.
3.권한 제어: 권한에 따라 봐야 할 것들만 볼 수 있도록 해주어야 합니다.
사실 가장 핵심이 되는 것은 1번, 어떻게 동시성 문제를 해결할 것인가 하는 부분이었습니다. 나머지는 그 중심 로직에 따라 구성해 나가기로 했습니다.
현재 이와 같이 동시성을 다루는 기술로는, 대표적으로 CRDT와 OT가 있습니다.
우리 팀은 길고 긴 검토 끝에 OT를 선택하게 되었습니다. 여러 가지 이유가 있었지만 가장 중요한 이유는 중재를 위한 서버의 존재였습니다. CRDT의 경우 동등한 여러 작업자를 상정해서 만들어진 로직입니다. 그래서 모두가 평등하게 같은 데이터를 수정하게 되어 있죠. 그 경우 단 하나의 진실이 존재하는 것이 아니라 각자가 진실을 가지고 있고 각자의 버전이 동등하게 진실입니다. 여러 개의 진실이 서로 영향을 주고받죠. 아주 쿨한 로직입니다만, 권한 제어를 하기에는 적합하지 않았습니다. 불가능한 건 아니겠지만, 글쎄요. 굳이 바퀴를 개발할 필요는 없겠죠! OT는 애초에 컨트롤을 담당하는 하나의 서버가 여러 사용자의 요청을 중재하고 조정하는 식으로 작동합니다.
동기화 레이어: MacroDelta와 Delta Engine
기술을 결정했으니, 이것을 우리 서비스에 적용해야 했습니다. 이를 위해서 Yess앱 내에 있는 자료들을 다룰 수 있는 한 단계 상위 모델을 만들었습니다. MacroDelta라는 이 모델입니다. 그리고 그 모델을 다루는 Delta Engine이라는 모듈이 이 모델을 관리하게 했습니다. 이 두 기술 조합은 다음과 같이 3가지 목표를 해결합니다.
- 동시성 문제: MacroDelta는 각 개별 데이터에 대해 OT에서 필요한 연산을 지원합니다. 변경 사항의 반영을 뜻하는 ‘Compose’ 와 변경 사항을 버전에 맞게 조정해주는 ‘Transform’ 연산인데요. 이를 Delta Engine이 동시에 같은 데이터에 대해서 들어온 변경사항을 조정하고 저장할 수 있도록 했습니다. (이에 대한 자세한 이야기는 너무 길어지니까 다음 기회로 미루겠습니다.)
- 선택적 구독: MacroDelta는 하나의 메인 주제가 되는 데이터를 중심으로 관련된 하위 데이터가 모여 있는 형태로 구성되었습니다. 이것은 앱 내의 데이터들을 주요 주제에 따라서 클러스터 형태로 모이게 합니다. 이렇게 함으로써, 유저의 현재 상황에 따라 필요한 데이터 클러스터만 ‘구독’ 함으로써 불필요한 실시간 동기화 처리를 하지 않고 필요한 데이터만 받을 수 있도록 해 줍니다. 예를 들어 Workspace와 Project를 중심으로 데이터가 모여 있는데요. 이렇게 함으로 해서 워크스페이스 세팅을 만지고 있는 유저는 Project의 변경사항은 받지 않고 Workspace 변경 사항만 받습니다.
- 권한 제어: 각 데이터 클러스터들 내에서는 다시 권한에 따라서 볼 수 있는 데이터와 볼 수 없는 데이터가 있습니다. 예를 들어 특정 권한이 없는 유저는 워크스페이스의 특정 세팅 정보를 볼 수 없습니다. 서버가 이 MacroDelta를 관리하는 과정에서 유저별 권한에 따라 불필요한 정보는 필터링합니다.
이렇게 MacroDelta라고 하는 하나의 레이어를 둠으로 해서, 원래대로라면 굉장히 복잡했을 실시간 동기화와 권한 관리 등의 복잡도를 한 레이어에 집중시키는 것이 가능했습니다. 이렇게 함으로써 앱의 다른 부분들은 이와 같은 복잡한 데이터 동기화 로직으로부터 자유롭게 되었습니다.
마무리하며
Yess는 일찍부터 이와 같은 실시간 업데이트를 기본적인 UX로 가정했습니다. 그랬기 때문에 선제적으로 이와 같은 복잡도를 컨트롤하는 레이어를 만들 수 있었고, 지금도 Macro Delta를 중심으로 새로운 기능을 빌드합니다. 이 개념을 한번 익히고 나면 상태 관리에 대한 커뮤니케이션에서 상당한 시간을 절약할 수 있게 됩니다. 어느 타이밍에 새로운 데이터를 받고 렌더링해야 하는지 고민할 필요가 없고, 그 시간에 더 중요한 UX 문제에 집중할 수 있기 때문입니다.
물론 실시간 동기화와 권한 관리도 역시 중요한 UX에 해당합니다. 하지만 항상 창의적으로 대응해야 하는 문제는 아닙니다. 중요하지만 복잡하고 반복되는 것들을 격리시켜 일괄적으로 해결하고, 그 시간에 그때그때 창조적으로 대응해야 하는 부분에 더 시간을 쓰고자 하는 것이 우리의 사고방식입니다. 이것은 개발에만 적용되는 것이 아닙니다. Yess가 Agency나 Freelancer들의 워크플로우에서 반복적이지만 복잡하고 중요한 부분들을 자동화하고자 하는 것도 같은 맥락입니다.유저들이 더 자기만이 발휘할 수 있는 잠재력을 실현하게 하기 위해, 나머지 부분들을 Yess가 맡아 훌륭하게 수행하고자 합니다. 우리는 이를 통해 세상이 좀더 나은 곳이 될 수 있을 것이라고 믿습니다.
Yess에서 백엔드 엔지니어를 모십니다.
백엔드 채용 공고 확인하기