애매한 고생보다 확실한 고생이 낫다: 이메일 에디터를 직접 만든 이유
Engineering | 2026년 10월 29일
직접 설계한 에디터로 기존 소재까지 모두 전환하기
데이터라이즈에서 이메일 에디터를 직접 구축하고, 기존 레거시 에디터에서 100% 전환하기까지의 여정을 공유해볼게요.우리는 왜 새로운 이메일 에디터가 필요했을까?
데이터라이즈는 오래된 버전의 Unlayer를 기반으로 이메일 에디터를 제공하고 있었어요.
매월 수천만 통의 이메일이 나가고 있었지만, 당시 데이터라이즈의 이메일 에디터는 안정성이 충분하지 않은 상황이었어요.
데이터라이즈가 일본에 진출하면서 이메일은 큰 힘이 되었지만, 에디터는 그만한 준비가 되어 있지 못했어요. 여러 플랫폼에 대응해야 했고, 우리 제품에 필요한 커스텀 컴포넌트도 구현해야 했어요.
하지만 커스텀 컴포넌트를 만들려면 개발자가 Unlayer 자체를 먼저 이해해야 했고, 이메일 플랫폼별 대응도 우리가 원하는 만큼 직접 다루기 어려웠어요.
요금제도 함께 고민해야 했어요. 우리가 원하는 기능을 더 구현하거나 커스텀하고, 기존 기능을 업그레이드하려면 당시 비용의 두 배가 넘는 상위 요금제가 필요했었죠.
다만 당시에는 우리가 하려는 일을 위해 거기까지 올라갈 필요는 없다고 판단했어요. 그래서 오픈소스를 바탕으로 직접 만들면 플랫폼별로 필요한 대응을 직접 제어하고, 데이터라이즈 사용자에게 맞는 제품으로 만들어볼 수 있겠다고 생각했어요.
출발점과 목표점이 명확해야 하는 이유
직접 만들기로 했지만, 기존보다 부족한 에디터를 사용자에게 제공할 수는 없었어요.
출발점은 PM분이 정리해주신 신규 이메일 에디터의 기능 명세서였어요. 100개쯤 되는 항목을 살펴보면서 직접 구현할 수 있겠다는 생각이 들었어요.
목표는 기존 에디터에서 데이터라이즈 사용자에게 제공하던 기능을 모두 지원하면서 더 나은 사용자 경험을 제공하고, 일본 고객사가 사용하는 이메일 플랫폼에도 모두 대응하는 것이었어요.
여기에 앞으로 기능을 확장할 때 개발이 병목이 되지 않아야 한다는 기준도 더했어요. 기존 기능을 옮기는 데 그치지 않고, 이후에 필요한 기능을 어떻게 추가할지도 함께 고민했어요.
기능이 많다고 원하는 이메일을 쉽게 만들 수 있는 건 아니에요. 기본값과 설정 순서, 상품·쿠폰을 선택하는 방식에 따라 편집 경험은 달라져요. 직접 설계하면 이런 선택을 우리 사용자에게 맞출 수 있다고 봤어요.
이제는 조금 더 개발 이야기로 들어가볼게요.
GrapesJS와 MJML을 하나의 에디터로 연결하기
이메일 에디터를 만들면서 어떤 기술을 사용하고, 이 기술을 어떤 방식으로 연결해 에디터를 구성할지 결정해야 했어요.
꾸준히 관리되어 온 GrapesJS와 MJML을 각각 편집과 이메일 HTML 생성의 기반으로 선택하고, 상품·쿠폰 같은 데이터라이즈의 기능과 소재 작성 흐름을 연결해 하나의 에디터로 구성했어요.
GrapesJS는 블록의 구조·속성·스타일 같은 편집 모델을 관리하고, 선택이나 실행 취소 같은 편집 동작을 제공해요. MJML은 이메일의 레이아웃과 스타일을 전용 마크업으로 표현하고, 이를 이메일 HTML로 변환하는 역할을 맡아요.
현재 구현을 기준으로 역할을 정리하면 다음과 같아요.
| 영역 | 맡고 있는 역할 |
|---|---|
| React UI | 블록 목록, 속성 패널, 툴바와 소재 작성 단계 구성 |
| GrapesJS | 편집 중인 블록의 구조·속성·스타일과 선택·실행 취소 관리 |
| MJML | 재편집할 디자인 원본 표현과 이메일 HTML 변환 |
| 커스텀 렌더링 계층 | 모델 변경에 따른 하위 구조와 캔버스 DOM 갱신 |
편집 중인 내용의 기준은 GrapesJS의 컴포넌트 모델이에요. React의 속성 패널은 그 모델을 구독해 값을 보여주고, 사용자가 입력하면 모델에 반영해요. 소재 이름과 제목, 현재 작성 단계는 Zustand에서 관리하고요. 디자인 단계에서 다음으로 이동할 때 편집 결과를 보관하고, 다시 돌아오면 저장해둔 MJML을 불러와요. 편집 중인 모델과 작성 흐름을 위한 상태의 책임을 나눠둔 거예요.
프로토타입의 깜빡임에서 렌더러까지
초기 프로토타입은 동료가 만들었고, 이후 함께 설계를 고민하면서 구체화했어요. 그 과정에서 눈에 띈 문제는 콘텐츠를 바꿀 때의 깜빡임이었어요. 콘텐츠를 변경할 때 해당 HTML 구조를 전부 없애고 다시 만들다 보니, 사용자가 편집 결과를 자연스럽게 확인하기 어려웠어요.
이 문제를 보면서 React가 화면 변경을 반영하는 방식을 떠올렸어요. React의 렌더와 커밋 설명처럼, 바뀔 내용을 계산하고 필요한 변경만 반영하는 데서 아이디어를 얻었어요. 이를 바탕으로 이메일 에디터를 위한 렌더러를 만들었어요.
현재 구현에는 두 종류의 비교가 있어요. 먼저 상품·쿠폰 같은 커스텀 블록의 설정이 바뀌면, 새로 필요한 하위 구조와 기존 GrapesJS 모델 트리를 비교해요. 같은 위치의 컴포넌트 타입과 자식 수가 같으면 기존 모델의 속성과 내용을 갱신하고, 구조가 달라지면 해당 부분을 다시 구성해요.
캔버스에 반영할 때는 MJML을 HTML로 컴파일한 결과를 임시 DOM으로 파싱해 기존 DOM과 비교해요. 같은 서브트리는 건너뛰고, 태그가 같으면 속성과 자식·텍스트를 갱신해요. 구조가 맞지 않는 부분은 교체하고요.
텍스트 편집 중인 내용도 지켜야 했어요. 화면에 입력한 내용과 모델 사이에 시차가 있을 수 있어서, 다시 그리기 전에 입력 중인 내용을 먼저 모델에 반영하고 불필요한 재렌더링을 막았어요. 화면 구조를 유지하는 것뿐 아니라 방금 입력한 내용을 잃지 않는 것도 중요했어요.
물론 구조나 배치 조건이 달라지면 다시 구성해야 하는 경우도 있어요. 모든 재생성을 없애기보다는, 유지할 수 있는 모델과 DOM은 남겨두는 방향으로 접근했어요. 이렇게 불필요한 재생성을 줄이면서 화면이 깜빡이는 문제도 조금 나아졌다고 느꼈어요.
오픈소스를 직접 개선하기
이메일 에디터를 개발하다 GrapesJS의 AssetManager에서 드래그앤드롭 업로드에 파일 필터가 적용되지 않는 문제를 발견했어요. 같은 문제가 이미 이슈로 등록돼 있어, 파일 필터를 개선하는 PR을 작성했고 병합됐어요. 이와 함께 DataSources의 타입 가드 보강에도 기여했고, 이 변경도 병합됐어요.
GrapesJS의 MJML 플러그인인 grapesjs-mjml에는 이미지 렌더링을 개선하는 PR도 제안했어요. mj-image의 속성·스타일 변경 때 하위 DOM을 통째로 교체하는 대신 기존 DOM과 비교해 갱신하면서, 이미지 재로딩과 깜빡임을 줄이려는 변경이에요. 이 PR은 글을 쓰는 현재 아직 열려 있어요.
제품을 만들다 발견한 문제를 직접 고치고, 그 개선을 오픈소스에도 반영할 수 있어 좋았어요.
다음 개발자를 위해 설계의 맥락 남기기
에디터를 직접 만든 뒤에도 계속 개선하려면, 다른 개발자도 설계의 맥락을 이해할 수 있어야 했어요. 작은 스타트업에서 직접 만든 에디터가 시간이 지나 새로운 기술 부채로 남을 수 있다는 점도 고민했어요.
그래서 기술적 선택과 컴포넌트별 설계를 스펙과 플랜 문서로 정리해 FE Brain 저장소에 기록했어요. 어떤 구조로 만들었는지, 어떤 요구사항과 고민을 거쳐 그 구조를 선택했는지도 함께 남겼어요.
다른 개발자가 이메일 에디터를 인수인계받거나 새로운 기능을 개발할 때, 문서를 통해 현재 구조와 선택의 이유를 쉽게 파악할 수 있도록 하는 데 신경 썼어요. 요구사항이 달라졌을 때 기존 결정을 다시 검토할 수 있도록, 판단의 근거를 남기는 것도 중요했어요.
기존 소재까지 전환을 마치며
10월 1일을 기준으로 기존 Unlayer 에디터에서 생성한 이메일 소재까지 신규 에디터 소재로 모두 전환했어요. 이로써 자체 에디터로의 전환을 100% 마무리하면서, 처음에 정했던 전환 목표에 도달했어요.
운영과 기능 확장의 제약에서 출발한 작업이었지만, 만드는 과정에서는 편집 경험과 렌더링 구조, 이후의 유지보수까지 함께 고민할 수 있었어요. 데이터라이즈의 글로벌 사용자에게 필요한 기능과 편집 흐름을 직접 설계하고 개선할 수 있었다는 점이 좋은 경험으로 다가와서, 짧게나마 관련해서 공유드려봅니다.