Skip to content

feat(rich-text): admin 에디터·미리보기·레슨 상세 렌더링 동기화 + WYSIWYG 표#727

Merged
seong-jin-jo merged 1 commit into
mainfrom
feat/markdown-editor-preview-sync
Jun 14, 2026
Merged

feat(rich-text): admin 에디터·미리보기·레슨 상세 렌더링 동기화 + WYSIWYG 표#727
seong-jin-jo merged 1 commit into
mainfrom
feat/markdown-editor-preview-sync

Conversation

@seong-jin-jo

@seong-jin-jo seong-jin-jo commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

release: minor

배경

공개 콘텐츠를 그리는 렌더러가 둘로 갈라져(drift) 레슨 상세에서 표·취소선이 사라지고, admin 미리보기와 결과가 달랐습니다.

  • MarkdownContent (admin 미리보기 + 미션/과제/그룹스터디 소개): 공유 sanitizer 사용, 정상
  • MarkdownContentCore (레슨 상세 + 빌더 피드 + 랜딩 피드 + 커뮤니티): 인라인 복사본 — 표 태그/표 CSS/취소선(<s>)/이미지 URL 해석/mermaid/normalize 누락

또한 에디터에서 표는 | 항목 | 설명 | 텍스트로만 보여 에디터-미리보기-상세가 어긋났습니다.

변경

렌더 동기화 (미리보기 ≡ 상세 ≡ 모든 공개 화면)

  • markdown-render-pipeline.ts 신규: 단일 렌더 파이프라인(renderMarkdownToSafeHtml) + 후처리(enhanceRenderedMarkdown) + 빈 단락→줄바꿈 복원(restoreEmptyParagraphsAsLineBreaks)
  • MarkdownContent·MarkdownContentCore 모두 이 파이프라인 사용 → 표/취소선/이미지 URL 해석/mermaid/normalize/줄바꿈 일치 (스타일 토큰만 화면별로 유지)

에디터 WYSIWYG 표

  • TipTap Table 확장 도입(extension-table) → 툴바 "표" 버튼이 실제 표 삽입
  • 붙여넣기: HTML 표(엑셀/시트/Notion)·탭 구분 텍스트를 실제 표 노드로 삽입
  • 정화기 colspan/rowspan 허용, 에디터 표 CSS 추가

영향 / 의도된 변화

  • MarkdownContent(admin) 로직 동일 → 미션/과제/그룹스터디 영향 없음
  • MarkdownContentCore는 전부 "추가" → 레슨/피드/커뮤니티에 개선. 단 (1) 이미지 폭 클램프 200800으로 admin과 통일(기존 80400), (2) 빈 줄이 상세/미리보기에도 표시(에디터와 일치)
  • 기존 파이프-텍스트 표 콘텐츠는 renderMarkdownTablesInHtml로 계속 표 렌더(하위호환)

검증

  • ✅ typecheck · lint(0 errors) · prettier · unit 19/19 · webpack 컴파일
  • ⚠️ 로컬 yarn build/sitemap.xml에서 백엔드 500(환경 이슈)로만 중단 — 렌더링 무관, CI는 백엔드 접속되어 통과 예상
  • 🔲 권장 수동 확인: 레슨 상세에서 표·취소선·줄바꿈·이미지, 에디터 표 삽입/붙여넣기

🤖 Generated with Claude Code

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 에디터에서 표 생성 및 편집 기능 추가
    • 마크다운 콘텐츠 렌더링 파이프라인 개선 및 안정화
    • 표 병합 셀(colspan, rowspan) 속성 지원
  • 스타일

    • 에디터 표 렌더링을 위한 스타일 추가
  • 테스트

    • 마크다운 렌더링 로직 관련 테스트 추가

…·레슨 상세 렌더링 동기화 + WYSIWYG 표

release: minor

- 단일 렌더 파이프라인(markdown-render-pipeline) 추출: admin 미리보기(MarkdownContent)와
  레슨 상세·피드·커뮤니티(MarkdownContentCore)가 동일 파이프라인을 사용하도록 통합
- 레슨 상세에서 누락되던 표/취소선(<s>)/이미지 URL 해석/mermaid/normalize 확보
- 빈 단락을 줄바꿈(<p><br></p>)으로 복원해 에디터-미리보기-상세 줄바꿈 일치
- 에디터에 TipTap 표 확장 도입 → 툴바·붙여넣기에서 실제 표(WYSIWYG) 편집
- 정화기 colspan/rowspan 허용, 에디터 표 CSS 추가

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 14, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
study-platform-client-dev Ready Ready Preview, Comment Jun 14, 2026 2:47pm

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Tiptap 네이티브 테이블 확장 4개 패키지를 추가하고, 분산된 마크다운 렌더링/위생 처리 로직을 markdown-render-pipeline.ts로 통합합니다. MarkdownEditor의 테이블 삽입 및 클립보드 붙여넣기가 마크다운 문자열 방식에서 HTML 테이블 노드 삽입 방식으로 전환되며, MarkdownContentMarkdownContentCore가 공통 파이프라인을 사용하도록 변경됩니다.

Changes

Tiptap 테이블 지원 및 렌더 파이프라인 통합

Layer / File(s) Summary
렌더 파이프라인 핵심 로직 및 테스트
src/components/common/ui/editor/markdown-render-pipeline.ts, src/components/common/ui/editor/markdown-render-pipeline.test.ts, src/components/common/ui/editor/markdown-sanitizer.ts
renderMarkdownToSafeHtml, restoreEmptyParagraphsAsLineBreaks, enhanceRenderedMarkdown을 새 파일에 구현하고, SANITIZE_OPTIONScolspan/rowspan을 허용 속성으로 추가하며, restoreEmptyParagraphsAsLineBreaks에 대한 vitest 테스트를 추가합니다.
Tiptap 테이블 확장 등록 및 패키지 추가
package.json, src/components/common/ui/editor/extensions.ts
@tiptap/extension-table* 4개 패키지를 추가하고, MarkdownTableExtensions(resizable: false)를 내보내는 export를 신규 추가합니다.
convertTabularTextToHtmlTable 유틸 추가
src/components/common/ui/editor/markdown-table-utils.ts
탭 구분 텍스트를 <table> HTML 문자열로 변환하는 convertTabularTextToHtmlTable 함수를 추가합니다. 브라우저 환경이 아니거나 변환에 실패하면 undefined를 반환합니다.
MarkdownEditor 테이블 삽입/붙여넣기 전환
src/components/common/ui/editor/markdown-editor.tsx
handleInsertTable을 TipTap insertTable 명령으로 교체하고, 클립보드 붙여넣기 경로를 HTML 테이블 insertContent로 전환하며, MarkdownTableExtensionsuseEditor extensions에 등록합니다.
MarkdownContent/MarkdownContentCore 파이프라인 위임 및 스타일 추가
src/components/common/ui/editor/markdown-content.tsx, src/components/common/ui/rich-text/markdown-content-core.tsx, src/app/global.css
두 컴포넌트의 내부 렌더링/위생 로직을 renderMarkdownToSafeHtml + enhanceRenderedMarkdown 호출로 교체하고, 테이블/Mermaid/del 관련 Tailwind 스타일 및 global.css 테이블 규칙을 추가합니다.

Sequence Diagram(s)

sequenceDiagram
  participant MarkdownEditor
  participant MarkdownTableExtensions
  participant insertTable
  participant renderMarkdownToSafeHtml
  participant DOMPurify
  participant enhanceRenderedMarkdown

  rect rgba(99, 179, 237, 0.5)
    note over MarkdownEditor,insertTable: 툴바 표 삽입
    MarkdownEditor->>MarkdownTableExtensions: extensions 등록
    MarkdownEditor->>insertTable: insertTable({ rows:3, cols:2, withHeaderRow:true })
    insertTable-->>MarkdownEditor: TipTap 테이블 노드 삽입
  end

  rect rgba(154, 230, 180, 0.5)
    note over MarkdownEditor,DOMPurify: 클립보드 붙여넣기
    MarkdownEditor->>MarkdownEditor: isHtmlTableOnlyPaste 판별
    MarkdownEditor->>MarkdownEditor: pastedHtml 또는 convertTabularTextToHtmlTable(pastedText)
    MarkdownEditor->>insertTable: insertContent(htmlTable)
  end

  rect rgba(251, 211, 141, 0.5)
    note over renderMarkdownToSafeHtml,enhanceRenderedMarkdown: 뷰어 렌더링
    renderMarkdownToSafeHtml->>DOMPurify: sanitize(html, SANITIZE_OPTIONS)
    DOMPurify-->>renderMarkdownToSafeHtml: sanitizedHtml
    renderMarkdownToSafeHtml-->>enhanceRenderedMarkdown: container
    enhanceRenderedMarkdown->>enhanceRenderedMarkdown: hljs + renderMermaidBlocks
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related issues

Possibly related PRs

  • code-zero-to-one/study-platform-client#662: markdown-content-core의 HTML 감지 및 marked.parse 건너뛰기 로직이 이 PR의 renderMarkdownToSafeHtml 파이프라인에서 동일하게 유지됩니다.
  • code-zero-to-one/study-platform-client#654: HTML 태그 포함 마크다운의 줄바꿈 처리 경로 변경이 이 PR의 restoreEmptyParagraphsAsLineBreaks 도입과 코드 레벨에서 직접 맞닿아 있습니다.
  • code-zero-to-one/study-platform-client#655: markdown-content.tsx, markdown-editor.tsx, markdown-content-core.tsx의 마크다운/HTML 처리 흐름 변경이 이 PR과 동일한 파일에서 겹칩니다.

Poem

🐇 토끼가 표를 만들었네,
마크다운 문자열은 이제 안녕~
TipTap 노드로 탁! 삽입하고,
파이프라인 하나로 쏙 모았지.
colspan, rowspan 잃지 않고,
깔끔한 테이블 세상이 열렸어요! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 핵심 변경 사항인 '관리자 에디터·미리보기·레슨 상세 렌더링 동기화'와 'WYSIWYG 표' 기능을 명확하게 요약하고 있으며, raw_summary와 pr_objectives의 주요 내용과 일치합니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/markdown-editor-preview-sync

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/components/common/ui/rich-text/markdown-content-core.tsx (1)

29-35: 💤 Low value

두 컴포넌트 간 렌더링 패턴 불일치 (optional)

markdown-content.tsx는 빈 div에 useEffect에서 innerHTML을 직접 설정하는 반면, 이 컴포넌트는 dangerouslySetInnerHTML을 사용합니다. 기능상 문제는 없지만, 코드베이스 일관성을 위해 하나의 패턴으로 통일하는 것을 고려해 볼 수 있습니다.

dangerouslySetInnerHTML 방식이 React에서 더 관용적이며, 초기 렌더링 시 콘텐츠 깜빡임을 방지합니다.

Also applies to: 78-80

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/common/ui/rich-text/markdown-content-core.tsx` around lines 29
- 35, Standardize the markdown content rendering pattern across both files to
use dangerouslySetInnerHTML for consistency and to prevent content flickering.
In markdown-content-core.tsx, ensure both the useEffect block around lines 29-35
(where enhanceRenderedMarkdown is called) and the code around lines 78-80 follow
the same dangerouslySetInnerHTML pattern rather than setting innerHTML directly
in useEffect. Additionally, update markdown-content.tsx to align with this same
pattern for codebase consistency, as dangerouslySetInnerHTML is the more
idiomatic React approach and provides better rendering performance by preventing
initial content flicker.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/components/common/ui/rich-text/markdown-content-core.tsx`:
- Around line 29-35: Standardize the markdown content rendering pattern across
both files to use dangerouslySetInnerHTML for consistency and to prevent content
flickering. In markdown-content-core.tsx, ensure both the useEffect block around
lines 29-35 (where enhanceRenderedMarkdown is called) and the code around lines
78-80 follow the same dangerouslySetInnerHTML pattern rather than setting
innerHTML directly in useEffect. Additionally, update markdown-content.tsx to
align with this same pattern for codebase consistency, as
dangerouslySetInnerHTML is the more idiomatic React approach and provides better
rendering performance by preventing initial content flicker.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0e2f4938-78ca-4793-9c2b-4d8e1160e7b1

📥 Commits

Reviewing files that changed from the base of the PR and between 6940adc and 6fa6214.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (10)
  • package.json
  • src/app/global.css
  • src/components/common/ui/editor/extensions.ts
  • src/components/common/ui/editor/markdown-content.tsx
  • src/components/common/ui/editor/markdown-editor.tsx
  • src/components/common/ui/editor/markdown-render-pipeline.test.ts
  • src/components/common/ui/editor/markdown-render-pipeline.ts
  • src/components/common/ui/editor/markdown-sanitizer.ts
  • src/components/common/ui/editor/markdown-table-utils.ts
  • src/components/common/ui/rich-text/markdown-content-core.tsx

@seong-jin-jo seong-jin-jo merged commit 533d2a4 into main Jun 14, 2026
17 of 20 checks passed
@seong-jin-jo seong-jin-jo deleted the feat/markdown-editor-preview-sync branch June 14, 2026 15:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release:minor Minor production release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant