Skip to content

Commit 28cbcf8

Browse files
authored
[#31] “Code 하이라이팅” 코드 품질 개선 (#36)
* 🔨 refactor: 변수 유틸함수로 분리 * 🔨 refactor: 애니메이션 코드 커스텀 훅으로 분리 * 🙀 chore: 훅에 주석 처리 * 🔨 refactor: 애니메이션 지속 시간 (매직넘버) 상수화 * 🙀 chore: 오타 수정 * 🔨 refactor: 클래스명 계산 로직 추상화 * 🔨 refactor: 마우스 호버 이벤트 훅 관리 + 라인클래스이름 추상화 * 🔨 refactor: 파싱 코드 추상화 + 함수 설명 주석 추가 * 🔨 refactor: Props Drilling이 나타나는 컴포넌트를 삭제하고 부모 컴포넌트로 이전. * 🔨 refactor: viewer 클래스명 생성 함수 분리 * 🔨 refactor: 타입 분리 + index.ts로 import 문 간소화하여 가독성 개선
1 parent e62079f commit 28cbcf8

File tree

17 files changed

+234
-165
lines changed

17 files changed

+234
-165
lines changed

apps/client/src/shared/code-highlighter/components/CodeContent/CodeContent.stories.tsx

Lines changed: 0 additions & 44 deletions
This file was deleted.

apps/client/src/shared/code-highlighter/components/CodeContent/CodeContent.tsx

Lines changed: 0 additions & 76 deletions
This file was deleted.

apps/client/src/shared/code-highlighter/components/CodeViewer/CodeViewer.tsx

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,10 @@
1-
import { CodeContent } from '../CodeContent/CodeContent';
2-
import { LineNumbers } from '../LineNumbers/LineNumbers';
3-
import { parseHighlightCss } from '../../utils/parseHighlightCss';
4-
import { parseHighlightHtml } from '../../utils/parseHighlightHtml';
51
import styles from '../../styles/CodeViewer.module.css';
2+
import { LineNumbers } from '../../components';
3+
import { useDiffCodeAnimate } from '../../hooks';
4+
import { getParsedCodeLineList, getClassNames, getViewerClassNames } from '../../utils';
5+
import { CodeViewerProps } from '../../types';
66
import { useCoachMarkStore } from '@/shared/store/useCoachMarkStore';
77

8-
type CodeViewerProps = {
9-
code: string;
10-
type: 'html' | 'css';
11-
theme?: 'light' | 'dark';
12-
selectedBlockStartLine?: number;
13-
selectedBlockLength?: number;
14-
selectedBlockType?: string | null;
15-
};
16-
178
/**
189
*
1910
* @description
@@ -27,27 +18,36 @@ export const CodeViewer = ({
2718
selectedBlockLength,
2819
selectedBlockType,
2920
}: CodeViewerProps) => {
30-
const parsedCode =
31-
type === 'html'
32-
? parseHighlightHtml(code, styles, selectedBlockType!)
33-
: parseHighlightCss(code, styles, selectedBlockType!);
34-
const codeLineList = parsedCode.split('\n').filter((line) => line.trim() !== '');
21+
const codeLineList = getParsedCodeLineList({ code, type, styles, selectedBlockType });
22+
const highlightedLines = useDiffCodeAnimate({ code, codeLineList });
3523
const { currentStep } = useCoachMarkStore();
3624

3725
return (
38-
<div
39-
className={`${styles.viewer} ${theme === 'dark' ? styles.dark : styles.light} ${currentStep === 3 ? 'z-[200] bg-white' : ''}`}
40-
>
26+
<div className={getViewerClassNames({ styles, theme, currentStep })}>
4127
<div className={styles.scrollContainer}>
4228
<LineNumbers codeLineList={codeLineList} />
4329

44-
<CodeContent
45-
code={code}
46-
codeLineList={codeLineList}
47-
selectedBlockLength={selectedBlockLength}
48-
selectedBlockStartLine={selectedBlockStartLine}
49-
selectedBlockType={selectedBlockType}
50-
/>
30+
<pre className={styles.codeContent}>
31+
<code>
32+
{codeLineList.map((line, index) => {
33+
return (
34+
<div
35+
key={index}
36+
className={getClassNames({
37+
styles,
38+
index,
39+
line,
40+
highlightedLines,
41+
selectedBlockStartLine,
42+
selectedBlockLength,
43+
selectedBlockType,
44+
})}
45+
dangerouslySetInnerHTML={{ __html: line }}
46+
/>
47+
);
48+
})}
49+
</code>
50+
</pre>
5151
</div>
5252
</div>
5353
);

apps/client/src/shared/code-highlighter/components/LineNumbers/LineNumbers.tsx

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,15 @@
11
import styles from '../../styles/CodeViewer.module.css';
2-
import { useState } from 'react';
3-
4-
type LineNumbersProps = {
5-
codeLineList: string[];
6-
};
2+
import { useHoverLineNumber } from '../../hooks';
3+
import { getLineNumberClass } from '../../utils';
4+
import { LineNumbersProps } from '../../types';
75

86
/**
97
*
108
* @description
119
* 코드의 줄 수를 표시하는 컴포넌트
1210
*/
1311
export const LineNumbers = ({ codeLineList }: LineNumbersProps) => {
14-
const [hoveredLineNumber, setHoveredLineNumber] = useState<number | null>(null);
15-
16-
// 마우스 enter, leave 색상 변화
17-
const handleMouseEnter = (lineNumber: number) => {
18-
setHoveredLineNumber(lineNumber);
19-
};
20-
21-
const handleMouseLeave = () => {
22-
setHoveredLineNumber(null);
23-
};
12+
const { hoveredLineNumber, handleMouseEnter, handleMouseLeave } = useHoverLineNumber();
2413

2514
return (
2615
<div className={styles.lineNumbers}>
@@ -29,7 +18,12 @@ export const LineNumbers = ({ codeLineList }: LineNumbersProps) => {
2918
key={index}
3019
onMouseEnter={() => handleMouseEnter(index + 1)}
3120
onMouseLeave={handleMouseLeave}
32-
className={`${styles.lineNumber} ${hoveredLineNumber === index + 1 ? styles.lineHighlight : ''}`}
21+
className={getLineNumberClass(
22+
styles.lineNumber,
23+
styles.lineHighlight,
24+
index + 1,
25+
hoveredLineNumber
26+
)}
3327
>
3428
{index + 1}
3529
</div>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { CodeViewer } from './CodeViewer/CodeViewer';
2+
export { LineNumbers } from './LineNumbers/LineNumbers';
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
*
3+
* @description
4+
* 애니메이션 지속 시간 (1초)
5+
*/
6+
export const ANIMATION_DURATION_MS = 1000;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { useDiffCodeAnimate } from './useDiffCodeAnimate';
2+
export { useHoverLineNumber } from './useHoverLineNumber';
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { useEffect, useState } from 'react';
2+
3+
import { ANIMATION_DURATION_MS } from '../constants';
4+
import { useDiffCodeAnimateProps } from '../types';
5+
6+
/**
7+
*
8+
* @description
9+
* 변환된 HTML 코드에서 바뀐 코드 라인을 애니메이션 주면서 할당
10+
*/
11+
export const useDiffCodeAnimate = ({ code, codeLineList }: useDiffCodeAnimateProps) => {
12+
const [previousCodeLines, setPreviousCodeLines] = useState<string[]>([]);
13+
const [highlightedLines, setHighlightedLines] = useState<number[]>([]);
14+
15+
useEffect(() => {
16+
const newLineList: number[] = [];
17+
18+
codeLineList.forEach((line, index) => {
19+
if (!previousCodeLines[index] || previousCodeLines[index] !== line) {
20+
newLineList.push(index);
21+
}
22+
});
23+
24+
setHighlightedLines(newLineList);
25+
const timeout = setTimeout(() => setHighlightedLines([]), ANIMATION_DURATION_MS);
26+
setPreviousCodeLines(codeLineList);
27+
28+
return () => clearTimeout(timeout);
29+
}, [code]);
30+
31+
return highlightedLines;
32+
};
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { useState } from 'react';
2+
3+
/**
4+
*
5+
* @description
6+
* 마우스 호버한 라인넘버의 색상을 변경하는 함수
7+
*/
8+
export const useHoverLineNumber = () => {
9+
const [hoveredLineNumber, setHoveredLineNumber] = useState<number | null>(null);
10+
11+
const handleMouseEnter = (lineNumber: number) => {
12+
setHoveredLineNumber(lineNumber);
13+
};
14+
15+
const handleMouseLeave = () => {
16+
setHoveredLineNumber(null);
17+
};
18+
19+
return { hoveredLineNumber, handleMouseEnter, handleMouseLeave };
20+
};
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// components 타입
2+
export type CodeViewerProps = {
3+
code: string;
4+
type: 'html' | 'css';
5+
theme?: 'light' | 'dark';
6+
selectedBlockStartLine?: number;
7+
selectedBlockLength?: number;
8+
selectedBlockType?: string | null;
9+
};
10+
11+
export type LineNumbersProps = {
12+
codeLineList: string[];
13+
};
14+
15+
// hooks 타입
16+
export type useDiffCodeAnimateProps = {
17+
code: string;
18+
codeLineList: string[];
19+
};
20+
21+
// utils 타입
22+
export type getClassNamesProps = {
23+
styles: { [key: string]: string };
24+
index: number;
25+
line: string;
26+
highlightedLines: number[];
27+
selectedBlockStartLine?: number;
28+
selectedBlockLength?: number;
29+
selectedBlockType?: string | null;
30+
};
31+
32+
export type getParsedCodeLineListProps = {
33+
code: string;
34+
type: 'html' | 'css';
35+
styles: { [key: string]: string };
36+
selectedBlockType?: string | null;
37+
};
38+
39+
export type getViewerClassNamesProps = {
40+
styles: any;
41+
theme: string | undefined;
42+
currentStep: number;
43+
};

0 commit comments

Comments
 (0)