배경
리뷰에서 제기된 내용처럼, 현재 관리자 미리보기와 실제 유저 화면의 Markdown 렌더링 경로가 분리되어 있습니다. 코드 확인 결과 리뷰 방향은 타당합니다.
- 관리자 레슨 본문 미리보기:
MarkdownContent
src/components/admin/courses/admin-lesson-management-page-client.tsx:1142
- 관리자 코스 상세 미리보기:
MarkdownContent
src/components/admin/courses/admin-course-detail-page-client.tsx:196
- 유저 레슨 상세/피드 상세:
MarkdownContentCore
src/app/(class-lesson)/class/[slug]/lesson/[id]/page.tsx:252
src/app/(class-lesson)/class/[slug]/lesson/[id]/_components/lesson-builder-feed-detail-modal.tsx:123
src/app/(landing)/class/[slug]/(learning)/feed/[id]/page.tsx:371
확인된 문제
두 컴포넌트가 단순 스타일 래퍼만 다른 것이 아니라, 렌더링 정책 일부를 각자 들고 있습니다.
MarkdownContent
normalizeMarkdownContent()로 입력 정규화
shouldRenderMarkdownAsMarkdown()로 HTML/Markdown 분기 보정
markdown-sanitizer.ts의 SANITIZE_OPTIONS, applyPostSanitizeAttributes() 사용
- HTML 테이블 보정(
renderMarkdownTablesInHtml)과 Mermaid 렌더링 처리 포함
- effect에서
innerHTML 주입 후 code highlight / mermaid render 실행
MarkdownContentCore
- 별도 inline
SANITIZE_OPTIONS, applyPostSanitizeAttributes(), 이미지 width clamp 정책 보유
isHtmlContent(contentWithEmbeds)만으로 HTML/Markdown 분기
- Mermaid 처리 없음
dangerouslySetInnerHTML로 렌더링
- 유저 화면용 typography / image constraint 별도 보유
이미 markdown-content-shared.ts, markdown-rendering-utils.ts, youtube-utils.ts처럼 일부 공통 유틸은 존재하지만, 최종 렌더링/정화/후처리 정책은 아직 두 군데에 남아 있어 한쪽 수정이 다른 화면에 반영되지 않을 수 있습니다.
체크리스트
제안 해결 방향
1안: 공통 렌더링 코어 추출
MarkdownContent와 MarkdownContentCore가 공통으로 사용할 렌더링 파이프라인을 추출합니다.
예시:
renderMarkdownHtml(content, options)
sanitizeMarkdownHtml(html, options)
applyMarkdownPostSanitizeAttributes(html, originalHtml)
MarkdownRenderer + variant="admin-preview" | "lesson-detail"
이때 variant는 폰트 크기, margin, max image size 같은 스타일 차이만 담당하고, HTML/Markdown 판별과 sanitizer/post-processing 정책은 공유하는 것이 좋습니다.
2안: 즉시 통합이 부담되면 parity harness 먼저 추가
전면 통합 전이라도 아래 fixture를 기반으로 양쪽 renderer의 정책 동등성을 검증하는 테스트를 추가합니다.
- Markdown 이미지:

- raw HTML 이미지:
<img src="..." width="...">
- signed image URL /
/images/... / blob URL
- YouTube 단독 링크
- Markdown table
- fenced code block 안의 HTML 예제
- TipTap degraded paragraph HTML:
<p></p>
- Mermaid 코드블록: 지원 여부를 명시적으로 결정
- 빈 문자열 / 공백 문자열
완료 기준
기대 효과
관리자 화면에서 정상으로 보였는데 실제 유저 화면에서 이미지, 표, 링크, 코드블록, YouTube embed가 다르게 보이는 회귀를 사전에 막을 수 있습니다.
배경
리뷰에서 제기된 내용처럼, 현재 관리자 미리보기와 실제 유저 화면의 Markdown 렌더링 경로가 분리되어 있습니다. 코드 확인 결과 리뷰 방향은 타당합니다.
MarkdownContentsrc/components/admin/courses/admin-lesson-management-page-client.tsx:1142MarkdownContentsrc/components/admin/courses/admin-course-detail-page-client.tsx:196MarkdownContentCoresrc/app/(class-lesson)/class/[slug]/lesson/[id]/page.tsx:252src/app/(class-lesson)/class/[slug]/lesson/[id]/_components/lesson-builder-feed-detail-modal.tsx:123src/app/(landing)/class/[slug]/(learning)/feed/[id]/page.tsx:371확인된 문제
두 컴포넌트가 단순 스타일 래퍼만 다른 것이 아니라, 렌더링 정책 일부를 각자 들고 있습니다.
MarkdownContentnormalizeMarkdownContent()로 입력 정규화shouldRenderMarkdownAsMarkdown()로 HTML/Markdown 분기 보정markdown-sanitizer.ts의SANITIZE_OPTIONS,applyPostSanitizeAttributes()사용renderMarkdownTablesInHtml)과 Mermaid 렌더링 처리 포함innerHTML주입 후 code highlight / mermaid render 실행MarkdownContentCoreSANITIZE_OPTIONS,applyPostSanitizeAttributes(), 이미지 width clamp 정책 보유isHtmlContent(contentWithEmbeds)만으로 HTML/Markdown 분기dangerouslySetInnerHTML로 렌더링이미
markdown-content-shared.ts,markdown-rendering-utils.ts,youtube-utils.ts처럼 일부 공통 유틸은 존재하지만, 최종 렌더링/정화/후처리 정책은 아직 두 군데에 남아 있어 한쪽 수정이 다른 화면에 반영되지 않을 수 있습니다.체크리스트
제안 해결 방향
1안: 공통 렌더링 코어 추출
MarkdownContent와MarkdownContentCore가 공통으로 사용할 렌더링 파이프라인을 추출합니다.예시:
renderMarkdownHtml(content, options)sanitizeMarkdownHtml(html, options)applyMarkdownPostSanitizeAttributes(html, originalHtml)MarkdownRenderer+variant="admin-preview" | "lesson-detail"이때 variant는 폰트 크기, margin, max image size 같은 스타일 차이만 담당하고, HTML/Markdown 판별과 sanitizer/post-processing 정책은 공유하는 것이 좋습니다.
2안: 즉시 통합이 부담되면 parity harness 먼저 추가
전면 통합 전이라도 아래 fixture를 기반으로 양쪽 renderer의 정책 동등성을 검증하는 테스트를 추가합니다.
<img src="..." width="...">/images/.../ blob URL<p></p>완료 기준
기대 효과
관리자 화면에서 정상으로 보였는데 실제 유저 화면에서 이미지, 표, 링크, 코드블록, YouTube embed가 다르게 보이는 회귀를 사전에 막을 수 있습니다.