박준영 보고 (6/12) — “메일 초안 저장 후 재로딩 시 행과 열이 모두 변경되어 보임”
증상은 하나지만 원인은 두 개의 독립 트랙이다. 진짜 코드 버그는 P1이며, 실데이터의 대량 이상치는 P2(별도 데이터 이벤트)로 코드 버그가 아니다.
<table>| 포함 textCreateCampaignStep2.tsx:178-181 — 보존된 emailBodyHtml을 버리고 emailBodyText를 markdownToHtml로 재파싱. 해결 = HTML을 콘텐츠 SSOT로 통일.에디터(TiptapEditor)는 emailBodyText를 value로 받는다 — EmailEditorPanel.tsx:373 value={currentStep?.emailBodyText}. 즉 멀쩡한 emailBodyHtml(190행)이 손에 있는데도 쓰지 않고, 재파싱된 값을 보여준다.
Tiptap은 표를 지원하는 HTML 에디터다 — tiptap-editor.tsx:1043 content: value, :1045 onUpdate: getHTML(), ExtendedTable 확장 보유. 따라서 emailBodyHtml을 그대로 넣으면 무손실인데, 굳이 plain을 GFM으로 재구성해 깨뜨린다.
SequenceStepForm(:107,133,740)과 LaunchModalStepEditor는 MDEditor(markdown)라, HTML 본문을 markdown으로 깎아 넣는다. 표 변환 규칙이 없어 <table>이 평문으로 무너진다.
FE가 항상 emailBodyHtml을 보내므로 정상 경로에선 폴백이 안 탄다. 다만 emailBodyText가 이미 HTML일 때 markdownToHtml로 또 파싱하면 깨지므로 isHtmlContent 가드 추가가 안전하다.
emailBodyHtml 우선(EmailPreviewDialog.tsx:89, StepBodyPreview.tsx:19). 오직 에디터 주입 경로(①③)만 emailBodyText를 재파싱한다.body_html이 빈 row 61,330건을 전수 추적 → 단일 시각 2026-06-02 02:49:10, 1개 워크스페이스·1개 시퀀스에 61,306건 일괄 insert. 일회성 스크립트 작업이며 body_text는 plain(평균 943자, 표 6건)이라 P1과 무관.
body_html을 1회 채우면 해소. P1 코드 수정과 섞지 않는다.| 경로 / 컴포넌트 | 에디터 | 표현 | 표 | 로딩 우선순위 | 상태 |
|---|---|---|---|---|---|
| Step2 CreateCampaignStep2 → EmailEditorPanel | Tiptap (HTML) | HTML | 지원 | emailBodyText 재파싱 | P1 버그 |
| Form SequenceStepForm | MDEditor (md) | markdown | 미지원 | html→md 다운컨버트 | 표 소실 |
| Launch LaunchModalStepEditor | MDEditor (md) | markdown | 미지원 | raw body_text | 표 소실 |
| EmailPreviewDialog / TestSendDialog | iframe·sanitize | HTML | 보존 | emailBodyHtml 우선 | 정상 |
| StepBodyPreview / StepPreviewDialog / Explorer | iframe·sanitize | HTML | 보존 | emailBodyHtml 우선 | 정상 |
bodyHtml = convertTextToHtml(bodyText)). 그런데 Form/Launch는 이를 htmlToStepMarkdown으로 깎아 표를 버린다. Step2(ManualMode)만 emailBodyHtml 우선의 정답 패턴.| source | rows | html 비어있음 | 표 | text 손상 | 판정 |
|---|---|---|---|---|---|
| ai | 315,259 | 18 | 8,553 | 30,853 | html 정상 |
| manual | 62,488 | 61,312 | 112 | 1 | 98% empty=P2 |
| template | 21,722 | 0 | 0 | 0 | 정상 |
| 주(week) | 생성 | empty | empty % | 비고 |
|---|---|---|---|---|
| ~ 2026-05-25 (15주) | 17,000± | 0~12 | ≈0.0% | 정상 운영 |
| 2026-06-01 | 102,472 | 61,306 | 59.8% | P2 단일 insert 02:49 |
| 2026-06-08 | 26,429 | 0 | 0.0% | 정상 복귀 |
| 2026-06-15 | 16,374 | 0 | 0.0% | 정상 |
15주 연속 ≈0% → 단 한 주만 59.8% → 즉시 복귀. 코드 회귀가 아니라 일회성 데이터 주입임을 날짜 통계가 입증.
markdownToHtml·htmlToStepMarkdown 금지.body_html이 빈 과거 row에만 1회 변환.htmlToPlainText(html)로만.markdownToHtml 폴백(레거시 15%).| 단계 | 내용 | 효과 | 위험 |
|---|---|---|---|
| N1 · 최소 즉시 | CreateCampaignStep2.tsx:178 로딩을 emailBodyHtml 1순위로 | 박준영 버그(표 8,665) 즉시 해소 | 최소 |
| N2 · Form/Launch | Tiptap+HTML 직접화, htmlToStepMarkdown 제거, 저장 htmlToPlainText, AI는 emailBodyHtml | 두 경로 표·정렬 보존 + AI 다운컨버트 제거 | 중 (발송 text·서명 주입 검증) |
| N3 · 정리 | 죽은 유틸 제거 | SSOT 단일화 | 낮음 |
| P2 · 백필 데이터 | 2026-06-02 단일 시퀀스 61,306건 body_html 채움 | 해당 시퀀스 정상화 | 낮음 |