이제는 TipTap? 아직도 CKEditor?
최근 편집기에 대해 조사하던 중 많이 변했다는 것을 체감했습니다.
Quill 주로 사용하고 좀 더 많은 기능이 필요하다면 CKEditor를 사용하는 게 일반적이었는데, Tiptap과 같은 새로운 Editor가 나와 오늘은 Tiptap과 CKEditor, Quill에 대해 비교해 보겠습니다.
ProseMirror와 TipTap – 모던 에디터의 탄탄한 기반
먼저 TipTap을 이야기하기 전에, 그 기반 기술인 ProseMirror를 짚고 넘어가야 합니다.
ProseMirror는 CodeMirror를 만든 Marijn Haverbeke가 설계한 문서 편집 엔진으로, 수년간 다양한 에디터에서 활용되며 안정성을 검증받은 배틀 테스트된(editor) 라이브러리입니다.
ProseMirror는 문서 모델(schema)과 편집 상태(state), 뷰(view), 변환(transforms)을 명확히 분리한 MVC 구조를 갖고 있어서, 복잡한 문서 편집 기능도 일관되게 처리할 수 있습니다.
이러한 탄탄한 설계 덕분에 Atlassian의 Confluence/Jira 에디터 등 여러 기업 제품에 ProseMirror가 사용되고 있습니다.
다만 단점은 기본기능만으로 에디터를 “바닥부터” 구현하려면 상당히 많은 코드와 높은 러닝커브가 요구된다는 것입니다.
프로그래밍 난이도가 높아 "취미로는 하기 어려운" 라이브러리라는 악명이 있습니다.
그래서 최근에는 ProseMirror 위에 사용자 친화적 API를 제공하는 래퍼(wrapper) 에디터들이 각광받고 있습니다. (ex. TipTap, Remirror, Atlaskit Editor 등)
TipTap은 이러한 ProseMirror 위에 구축된 대표적인 현대적 에디터입니다.
TipTap 개발 팀은 ProseMirror의 강력함은 취하면서, 직접 다루기 까다로운 부분들을 추상화하여 개발자 경험(DX)을 크게 개선했습니다.
쉽게 말해, ProseMirror의 복잡한 내부를 숨기고 플러그인식 모듈 구조와 직관적인 API를 제공하여, 개발자가 보다 빠르게 기능을 구현하도록 돕습니다.
예를 들어 TipTap에서는 미리 작성된 확장 모듈(Extensions)을 가져와 에디터에 붙이는 방식으로 굵게/기울임, 리스트, 표, 이미지, 코드블록 등 대부분의 기본 기능을 손쉽게 추가할 수 있습니다.
또한 사용자 정의 노드/마크나 커맨드를 만들어 기존 기능을 확장하거나 동작을 오버라이드할 수도 있습니다.
TipTap은 내부적으로 여전히 ProseMirror의 Schema와 트랜잭션(transaction) 개념을 사용하지만, 일반적인 사용자는 이를 직접 다루지 않고도 확장 모듈을 조합해 기능을 구현할 수 있습니다.
(필요하다면 ProseMirror의 Plugin을 직접 제어할 수 있는 인터페이스도 제공됩니다.)
TipTap의 또 다른 강점은 모듈화와 경량화입니다.
기본 기능 세트(StarterKit)를 포함해도 불필요한 부분은 트리 쉐이킹(tree shaking)으로 제외할 수 있어, 번들 크기가 Quill이나 Slate 등의 다른 에디터보다 작습니다.
실제로 TipTap의 core 패키지는 수십 kB 수준으로 매우 가볍게 설계되어 있고, 필요한 기능만 추가하는 식이라 대규모 웹 앱에도 부담이 적습니다.
또한 React, Vue, Svelte 등 다양한 프론트엔드 프레임워크를 공식 지원하며, MIT 라이선스 기반의 완전한 오픈 소스인 점도 매력적입니다.
단, 최근 TipTap 팀에서 일부 고급 기능(예: 실시간 협업, 코멘트, 멘션 등)을 유료 Pro 확장으로 제공하며, 이러한 확장은 소스가 공개되지 않고 별도 라이선스로 제공되니 참고해야 합니다.
물론 TipTap도 단점이나 한계는 있습니다.
ProseMirror를 감싸고 있다 보니 완전히 추상화되지 않은 부분이 있어서, 아주 깊은 수준의 커스터마이징을 하려면 결국 ProseMirror의 동작원리를 공부해야 합니다.
그리고 TipTap의 커맨드 체인 API도 처음 배우기까지는 러닝커브가 있는 편입니다.
하지만 소스 코드와 예제가 잘 갖춰져 있어 개발자가 내부를 파고들 수 있도록 도와줍니다.
또한 TipTap은 기본적으로 브라우저 환경에서 돌아가기 때문에, Node.js 같은 서버 사이드 환경에서 문서를 조작하는 기능은 바로 지원되지 않습니다.
(JSON으로 문서 상태를 받아 서버에서 가공한 뒤 다시 로드하는 식으로 우회는 가능합니다.)
마지막으로 성능 측면에서는, TipTap/ProseMirror 자체는 큰 문서도 거뜬히 다룰 정도로 최적화되어 있지만, React 통합 시 주의가 필요합니다.
모든 트랜잭션마다 리액트 리렌더를 트리거하지 않도록 shouldRerenderOnTransaction 설정을 false로 둔다든지, 문서 상태를 직접 탐색하는 빈도를 줄인다든지 하는 주의가 필요합니다.
이러한 규칙을 지키지 않으면 편집 내용이 많아질 때 속도가 느려질 수 있습니다.
하지만 다행하게도 공식 문서와 커뮤니티에서 성능 팁을 공유하고 있으니 큰 문제는 아니었습니다.
TipTap은 제품 레벨 기능 확장에도 유연합니다.
예를 들어 문서 불러오기/내보내기의 경우 HTML이나 Markdown을 파싱해 TipTap의 JSON 문서로 변환하는 유틸리티를 제공하며, HTML 문자열을 초기 콘텐츠로 바로 로드할 수도 있습니다.
데이터 저장은 TipTap의 JSON 포맷이나 HTML로 할 수 있는데, JSON으로 저장하면 나중에 다시 편집하기 용이하고 HTML로 저장하면 바로 웹에 표시하기 편리합니다.
페이지네이션(페이지 구분) 기능은 TipTap에 기본 제공되진 않지만, ProseMirror의 플러그인을 응용하여 페이지 나누기 노드를 만들고 렌더링 시 페이지 CSS를 적용하는 커스텀 솔루션들이 있습니다 (ex. tiptap-pagination-breaks).
즉, TipTap은 "에디터 엔진"에 가까워서, 개발자가 노력하면 Google Docs와 유사한 페이지 구분 편집기도 구현 가능하지만, 그만큼 추가 작업이 필요합니다.
반면 실시간 협업 기능은 오히려 TipTap 쪽이 비교적 수월한 편인데, Yjs 같은 CRDT 기반 라이브러리와 연동하기 쉽고 Liveblocks 등에서 제공하는 TipTap 전용 협업 백엔드도 활용할 수 있습니다.
요약하면 TipTap은 "유연하고 현대적인 에디터 프레임워크"입니다.
완성형 에디터라기보다 에디터를 만드는 툴킷에 가깝지만, 기본적인 글 편집부터 고급 기능까지 필요한 것만 골라 붙일 수 있는 확장성이 가장 큰 장점입니다.
React 기반 제품에서 커스터마이징이 많이 필요한 엔터프라이즈의 경우, 초기 러닝커브를 넘어서면 TipTap만큼 만족도를 주는 선택지는 드문 것 같습니다. 저 역시 최신 트렌드를 반영한 UX와 개발 편의성 측면에서 크게 만족하고 있습니다.
CKEditor 5 – 20년 노하우의 올인원 에디터
CKEditor는 웹 에디터 역사에서 빠질 수 없는 전통의 강자입니다.
2000년대 초반부터 이어져 온 CKEditor는 현재 버전 5까지 진화하며, 현대적인 모듈식 구조와 깨끗한 UI로 거듭났습니다.
CKEditor 5는 내부에 자체 개발한 편집 엔진을 가지고 있는데, 이것은 앞서 언급한 ProseMirror와 개념적으로 비슷하게 문서 모델(Model)과 뷰(View)를 분리한 MVC 아키텍처를 채택했습니다.
편집 중 사용자가 보는 DOM과 별개로 내부 모델에 콘텐츠 상태를 유지하고, 이를 업캐스팅/다운캐스팅 규칙에 따라 상호 변환하는 구조입니다.
이런 추상화 덕분에 CKEditor는 내용의 유효성을 보장하고 복잡한 서식도 정확히 다룰 수 있습니다.
그리고 모든 기능이 플러그인 단위로 구현되어 있어 필요시 플러그인 형태로 로딩할 수 있고, Angular, React, Vue, Next.js 등 다양한 환경에 맞춘 공식 통합 패키지도 제공합니다.
CKEditor의 가장 큰 강점은 뭐니 뭐니 해도 풍부한 기본 기능입니다. "배터리 포함(batteries-included)" 에디터라는 표현처럼, 처음 설치만 해도 워드 프로세서에 필적할 만한 대부분의 편집 기능이 작동합니다.
기본적인 서식 도구, 표/이미지/미디어 삽입, 링크, 북마크, 텍스트 검색/치환, 트랙 체인지, 심지어 수식 편집까지 지원됩니다.
공식 사이트에 공개된 플러그인만 해도 수십 가지이며, 심지어 문서 내보내기(PDF로 익스포트, Word(docx)로 내보내기)나 페이지 구분선 표시 등의 고급 기능도 제공합니다.
실제로 CKEditor 5의 Pagination 기능을 활성화하면 편집 중에 A4 용지 기준 페이지 나뉨을 가이드선으로 보여줄 수 있고, 내보낼 때 해당 구분대로 PDF를 생성할 수도 있습니다.
(물론 Pagination 기능은 일반 유료 기능도 아닌 Enterprise 기능입니다.)
이런 기능들은 프로덕션 수준의 요구사항을 바로 충족시켜 주기 때문에, 별도 개발 리소스 없이 빠르게 풍부한 에디터를 제공해야 하는 상황이라면 CKEditor가 유용합니다.
하지만 이런 "올인원" 성격 때문에 제약도 존재합니다.
우선 라이선스 측면에서, CKEditor 5는 기본적으로 GPL-2 라이선스로 공개되어 있습니다.
GPL 특성상 만약 우리 제품에 CKEditor를 탑재하고 수정했다면 그 파생물을 공개해야 할 수도 있습니다.
이를 피하려면 CKEditor를 만든 회사(CKSource)로부터 상용 라이선스를 구매해야 합니다.
오픈 소스 프로젝트나 내부용이 아니라면 사실상 유료로 생각해야 합니다.
또한 에디터 기본 기능 중 상당수가 유료 플러그인으로 묶여 있습니다.
예를 들어 Markdown으로 편집하기, 미디어 임베드, 멘션(언급), 코멘트, 트랙 체인지, 실시간 협업 등은 모두 별도 구매 영역입니다.
무료 버전으로도 글 쓰는 데 지장은 없지만, 제품 레벨에서 흔히 요구되는 댓글 기능이나 멘션, 협업 기능을 쓰려면 결국 유료 플랜을 고려해야 합니다.
특히 실시간 협업은 CKEditor Cloud Services를 통해서만 가능하며(자체 구축 불가), 사용량에 따라 Editor Load 기반 과금이 책정됩니다.
그렇기 때문에 사용자가 많아지면 편집기 로드 횟수도 많아지기 때문에 그 비용도 무시할 수 없습니다.
개발적인 관점에서 보면, CKEditor는 커스터마이징 난이도가 비교적 높은 편입니다.
앞서 TipTap이 개발자 친화적이라면, CKEditor는 "이미 다 갖춰진 툴"을 약간 비틀어 쓰는 느낌입니다.
제공되는 설정 옵션이나 플러그인 API 범위 안에서는 꽤 유연하게 행동을 바꿀 수 있지만, 그 밖의 커스터마이징 (예를 들어 UI를 완전히 바꾸거나, 에디터 동작을 깊게 변경하는 것)은 쉽지 않습니다.
저 역시 CKEditor나 비슷한 TinyMCE를 React 환경에서 조금 뜯어고쳐서 써봤는데, 모델-뷰 컨버전 구조나 이벤트 시스템을 이해해야 해서 진입장벽이 Tiptap보다 오히려 높은 느낌이었습니다.
공식 문서와 예제가 잘 나오긴 했지만 원하는 대로 고치다 보면 사이드 이펙트가 발생할 여지도 컸습니다.
요약하면 CKEditor는 의견이 강한(opinionated) 솔루션으로, 가이드되는 대로 쓰기에는 편하지만 그 궤도를 많이 벗어나긴 어렵습니다.
러닝커브도 처음엔 낮아 보이지만, 심화 기능을 활용하려면 꽤 많은 시간을 들여 구조를 이해해야 했습니다.
그럼에도 불구하고 CKEditor는 기업 환경에서 검증된 안정성과 풍부한 기능이라는 강력한 매력을 갖습니다.
만약 시간이 부족하고 예산이 충분하며, 편집기에 대한 요구사항이 일반적인 수준("그냥 MS Word 비슷하게만 나오면 돼")이라면, CKEditor를 도입하고 필요한 유료 기능만 추가 구매하는 것이 가장 빠른 길일 수 있습니다.
반대로 우리 제품만의 차별화된 편집 경험을 만들어야 하거나, 라이선스 이슈로 코드를 개방할 수 없다면 CKEditor는 신중히 고려해야 합니다.
Quill – 간결하고 검증된 오픈 소스 에디터
Quill은 2010년대에 큰 인기를 끌었던 경량 WYSIWYG 에디터로, Slack이나 LinkedIn, Airtable 같은 유명 서비스들에도 채택되어 온 검증된 선택지입니다.
Quill은 본질적으로 간단하고 사용하기 쉬운 에디터를 지향하여, 복잡한 설정 없이도 기본적인 서식, 리스트, 링크, 이미지 등을 편집할 수 있습니다.
제가 Quill을 선호했던 이유 중 하나는 직관적인 데이터 모델(Delta 포맷)입니다.
Quill은 편집기 내용을 JSON 기반의 델타(Delta)라는 구조체로 다루는데, 삽입/삭제/서식 변경 등의 연산을 시퀀스로 기록하는 형태입니다.
이런 Delta 포맷 덕분에 변경 내역 추적이나 협업 머지에 유리하고, 데이터 저장 시 XSS 필터링 등을 거친 깨끗한 JSON으로 저장할 수 있습니다.
(원한다면 quill.getText()나 getHTML()로 간단히 HTML 추출도 가능합니다.)
Quill의 커스터마이즈 방법도 독특합니다. Parchment라는 모듈을 통해 "블롯(blot)"이라 불리는 요소를 정의함으로써 새 서식이나 임베드 요소를 추가할 수 있습니다.
예를 들어 멘션 기능을 넣고 싶다면 Mention Blot을 만들고 이를 에디터에 등록하는 식입니다.
이 개념은 ProseMirror의 노드/마크를 정의하는 것과 유사하며, 실제로 Quill의 Parchment는 HTML DOM을 추상화한 객체 모델을 사용합니다.
덕분에 개발자가 필요한 포맷만 딱 추가해서 쓸 수 있고, 불필요한 기능은 배제한 미니멀리즘 에디터로 유지할 수 있습니다.
또한 Quill은 프레임워크에 무관하게 동작해서, 우리 팀처럼 React 환경에서는 react-quill 같은 래퍼를 쓰거나 직접 Quill API를 호출하면 되고, Vue 등 다른 프레임워크에서도 쉽게 통합할 수 있습니다.
라이선스도 BSD-3-Clause로 매우 자유롭기 때문에 상업 제품에서도 마음 놓고 쓸 수 있습니다.
한동안 Quill은 버그 픽스가 더딘 정체 상태였지만, 2024년 4월에 Quill 2.0이 릴리스 되면서 정말 달라졌다고 말해도 모자랄 것입니다.
Quill 2.0은 내부를 TypeScript로 다시 작성하고 과거 이슈들을 상당수 개선했는데, 제가 Quill을 떠났던 많은 문제들이 해결되었더라고요.
그래서 v2 출시 전 몇 년간 업데이트가 뜸했지만, 이제 다시 활발한 커뮤니티가 형성되는 추세입니다.
그렇다고 Quill이 만능은 아닙니다.
오히려 Quill은 CKEditor나 TipTap에 비해 기능성은 떨어지지만 단순함으로 승부하는 툴입니다.
기본 제공 기능이 비교적 한정적이라, 멀티레벨 리스트나 고급 테이블 편집 같은 것들은 아직 부족합니다.
(일부는 커뮤니티 모듈이 있지만 Quill 2 호환이 미비함)
플러그인 생태계도 방대하지 않습니다.
예를 들어, Quill에는 문서 협업 시 유용한 "데코레이션" 개념이 없습니다.
텍스트 일부를 하이라이트 하거나 다중 커서 위치를 표시하는 기능은 문서 모델과 별개로 표현 레이어가 필요한데, Quill은 모든 스타일이 내용 Delta에 녹아들어 가는 구조여서 이를 지원하지 못합니다.
그래서 quill-cursors처럼 에디터 위에 별도 DOM 요소를 덧씌워 커서를 표시하는 서드파티 라이브러리가 등장하기도 했습니다.
실시간 협업 역시 자체 솔루션은 없지만, Yjs를 Quill Delta와 연결해 주는 y-quill 커넥터를 통해 구현할 수는 있습니다.
다만 이런 협업 구현은 TipTap만큼 매끄럽지는 않아서, 협업 편집이 핵심이라면 Quill은 조금 부족하게 느껴질 수 있습니다.
성능 면에서 Quill은 경량 에디터인 만큼 적당한 분량의 콘텐츠까지는 충분히 빠릿빠릿하게 동작합니다.
하지만 라이브블록스의 분석에 따르면 Quill 2의 번들 사이즈가 TipTap이나 Slate보다 2배가량 크다고 합니다.
(아마 Quill 코어+테마 CSS 등의 전체 크기를 말하는 듯합니다.)
번들 최적화가 중요하다면 이 점도 고려해야겠지만, 메모리 사용이나 일반 타이핑 성능은 세 에디터 모두 큰 차이 없습니다.
정리하면, Quill은 "검증된 무난한 오픈 소스 에디터"입니다.
특별히 화려한 기능은 없지만 문서화가 잘 되어 있고, 사용 방법이 단순해서 빠르게 적용할 수 있다는 강점이 있습니다.
에디터 커스터마이징에 많은 시간을 쓰기 힘들거나, 요구사항이 기본 서식 편집 정도에 그친다면 Quill이 좋은 선택일 수 있습니다.
특히 라이선스 걱정 없이 오픈 소스로 자유롭게 쓰고 싶을 때, Quill은 좋은 후보입니다.
어떤 에디터를 선택해야 할까?
세 가지 에디터를 비교해 보았는데, 결국 "최적의 선택은 애플리케이션의 목적과 우선순위에 달렸다"는 평범한 진리가 남습니다.
각 에디터의 특징을 다시 한번 요약하고 시나리오별로 추천을 해보겠습니다.
- TipTap – 커스터마이징 최우선, 확장성 중시 상황에 적합합니다. 예를 들어 노션(Notion)처럼 우리만의 독특한 편집 경험을 만들고 싶다면 TipTap이 정답입니다. 개발 초기에는 약간 복잡할 수 있지만, 필요한 기능만 모듈화 하고 React와 자연스럽게 통합할 수 있다는 점에서 프론트엔드 개발자의 자유도를 보장합니다.
또한 오픈 소스 라이선스로 비용 부담이 없고, 협업이나 멘션 등도 추가 라이브러리를 통해 구현 가능하므로 스타트업 제품에도 잘 맞습니다. 단기간에 완제품 같은 에디터를 뚝딱 만들기는 어렵지만, 장기적으로 유지보수하며 우리 제품에 녹아드는 편집기를 원한다면 TipTap을 권합니다. - CKEditor 5 – 기능 풍부, 즉시 사용 가능한 솔루션이 필요하다면 여전히 매력적인 선택입니다. 사내 시스템이나 엔터프라이즈 솔루션에서 문서 편집 기능을 빠르게 제공해야 할 때 유용합니다. 예를 들어 적은 개발 시간에서 "워드 문서 임포트/익스포트가 꼭 필요하다"거나 "사용자들에게 친숙한 오피스 스타일 UI가 중요하다"면 CKEditor가 유일한 현실적인 대안일 수 있습니다.
다만 라이선스 비용과 유지보수 비용을 고려해야 합니다. 단순 오픈 소스 라이브러리보다 도입 난이도가 높고, 커스터마이징 폭이 제한적이라 요구사항 변화에 대응하기 어렵다면 오히려 독이 될 수 있습니다. 협업 편집을 자체 구현할 수 없고 CKEditor Cloud에 의존해야 하는 점도 제약이므로, 실시간 협업을 중요시한다면 다른 옵션을 보는 게 나을 수 있습니다. - Quill – 경량, 기본기에 충실한 에디터가 필요하면 고려해 볼 만합니다. 블로그나 게시판 글쓰기처럼 표준적인 WYSIWYG 기능만 필요하고, 개발 리소스나 시간도 한정되어 있다면 Quill이 빠른 해결책입니다. React에 react-quill로 쉽게 붙이고, UI도 심플해서 사용자 교육 부담이 적습니다. 모바일 웹에서도 무난히 동작하고, Delta 포맷으로 데이터 관리도 투명하게 할 수 있습니다.
다만 새로운 기능 추가 요구가 생기면 한계에 부딪힐 수 있으므로, 장기적인 확장 가능성을 따져보아야 합니다. Quill 2로 개선되었다 해도 생태계가 아주 활발하지는 않다는 점에서, 프로젝트의 미래 요구사항까지 커버할지는 고민이 필요합니다
마지막으로, 저의 개인적인 선택을 덧붙이자면 TipTap을 선택할 것 같습니다.
React/TypeScript 친화성, 라이선스 자유도, 커스터마이징 여지 측면에서 TipTap이 너무 뛰어나다고 생각합니다.
ProseMirror의 개념 몇 가지를 학습한다면, 원하는 기능을 제약 없이 넣을 수 있으며, 성능 및 개발자 경험도 만족스러운 라이브러리라고 생각합니다.
반면 CKEditor는 "그 틀 안에서 사용하는" 느낌이라 커스텀한 기능을 접목하기 어려웠고, Quill은 가벼워서 좋았지만 사이드 프로젝트가 아닌 실제 서비스에서 사용하기에는 장기적으로 유지보수가 불안하다는 점이 있습니다.
결론적으로, TipTap vs CKEditor vs Quill 중 정답은 프로젝트의 성격에 따라 달라집니다.
이 글의 비교와 사례들이 선택에 도움이 되었으면 합니다.
에디터는 한번 도입하면 사용자 생성 데이터와 직결되어 교체가 쉽지 않으니 초기 선정 시 요구사항을 확실히 따져보기를 추천합니다.