diff --git a/src/hooks/util/parseMarkdown.tsx b/src/hooks/util/parseMarkdown.tsx
index 07472ce..97d2504 100644
--- a/src/hooks/util/parseMarkdown.tsx
+++ b/src/hooks/util/parseMarkdown.tsx
@@ -1,4 +1,3 @@
-// HTML 특수문자 이스케이프 (XSS 방지)
function escapeHtml(text: string): string {
return text
.replace(/&/g, '&')
@@ -8,80 +7,102 @@ function escapeHtml(text: string): string {
.replace(/'/g, ''');
}
+const formatInline = (raw: string) => {
+ if (!raw) return '';
+
+ // Bold 처리
+ const boldTokens: string[] = [];
+ const tokenized = raw.replace(/\*\*(.+?)\*\*/g, (_, g1) => {
+ const index = boldTokens.length;
+ boldTokens.push(`${escapeHtml(g1)}`);
+ return `%%BOLD_${index}%%`;
+ });
+
+ // 인라인 코드 처리
+ const inlineTokens: string[] = [];
+ const inlineProcessed = tokenized.replace(/`([^`]+)`/g, (_, code) => {
+ const index = inlineTokens.length;
+ inlineTokens.push(`${escapeHtml(code)}`);
+ return `%%INLINE_${index}%%`;
+ });
+
+ // placeholder 복원
+ let restored = inlineProcessed.replace(/%%BOLD_(\d+)%%/g, (_, i) => boldTokens[Number(i)]);
+ restored = restored.replace(/%%INLINE_(\d+)%%/g, (_, i) => inlineTokens[Number(i)]);
+
+ return restored;
+};
+
function parseMarkdown(text: string): string {
if (!text || typeof text !== 'string') return '';
- // 1단계: 코드블록을 먼저 플레이스홀더로 추출 (내부 내용이 다른 regex에 오염되지 않도록)
+
+ // 코드블록 추출
const codeBlocks: string[] = [];
let processed = text.replace(/```[\w]*\n([\s\S]*?)```/g, (_, code) => {
const index = codeBlocks.length;
codeBlocks.push(
- `
${escapeHtml(code)}`
+ `${escapeHtml(
+ code
+ )}`
);
return `%%CODEBLOCK_${index}%%`;
});
- // 2단계: 인라인 코드도 플레이스홀더로 추출
- const inlineCodes: string[] = [];
- processed = processed.replace(/`([^`]+)`/g, (_, code) => {
- const index = inlineCodes.length;
- inlineCodes.push(`${escapeHtml(code)}`);
- return `%%INLINECODE_${index}%%`;
- });
+ // 라인 단위로 처리
+ const lines = processed.split('\n');
+ const result: string[] = [];
+
+ let inList = false;
+ let listItems: string[] = [];
+ const flushList = () => {
+ if (!inList) return;
+ result.push(`