
참고로 원랜 이랬어요.

사실 제가 짠건 아니고 AI 시킨 건데, 적어도 내용은 복습해야 할 것 같아서 올려봅니다.
위에선 "뭔갈 만드는 걸 좋아하는", "송상록", "입니다"를 별도의 TypingEffect.jsx 컴포넌트로 처리해 줍니다
// TypingEffect.jsx
'use client'
import { useEffect, useState } from 'react'
interface TypingEffectProps {
text: string // 타이핑할 전체 텍스트
delay?: number // 각 글자 사이 지연 시간 (ms)
className?: string // 스타일 클래스
onComplete?: () => void // 타이핑 완료 시 실행할 콜백
startDelay?: number // 타이핑 시작 전 대기 시간 (ms)
}
export default function TypingEffect({
text,
delay = 100,
className = '',
onComplete,
startDelay = 0,
}: TypingEffectProps) {
// 화면에 실제로 표시되는 텍스트
const [displayedText, setDisplayedText] = useState('')
// 현재까지 출력된 글자의 인덱스
const [currentIndex, setCurrentIndex] = useState(0)
// 시작 지연 후 타이핑이 시작되었는지 여부
const [started, setStarted] = useState(false)
useEffect(() => {
// startDelay가 설정되어 있고 아직 시작 안 했으면
if (startDelay > 0 && !started) {
const startTimeout = setTimeout(() => {
setStarted(true) // 지연 시간 후 시작
}, startDelay)
// 컴포넌트 재렌더링 후 이전 타이머 남아있는 것을 방지 (cleanup)
return () => clearTimeout(startTimeout)
} else if (startDelay === 0) {
setStarted(true) // 지연이 없으면 바로 시작
}
}, [startDelay, started])
// 타이핑 효과
useEffect(() => {
if (!started) return
// 아직 출력할 글자가 남아있으면
if (currentIndex < text.length) {
const timeout = setTimeout(() => {
// 현재 인덱스의 글자를 displayedText에 추가
setDisplayedText((prev) => prev + text[currentIndex])
// 다음 글자로 이동
setCurrentIndex((prev) => prev + 1)
}, delay)
return () => clearTimeout(timeout)
// 모든 글자 출력 완료 시 콜백 실행
} else if (currentIndex === text.length && onComplete) {
onComplete()
}
}, [currentIndex, delay, text, onComplete, started])
// 아직 시작 안 했으면 빈 span 반환
if (!started) {
return <span className={className}></span>
}
return (
<span className={className}>
{displayedText}
{currentIndex < text.length && (
<span className="animate-pulse text-primary-500">|</span>
)}
</span>
)
}
startDelay 시간만큼 대기 후 타이핑을 시작합니다.started는 false. setStarted로 제어합니다.delay (기본 100ms) 간격으로 한글자씩 가져와 displayedText state에 추가합니다.currentIndex로 추적합니다onComplete를 출력합니다.// MainTitle.jsx
'use client'
import TypingEffect from './TypingEffect'
import { useState } from 'react'
export default function MainTitle() {
const [showSecondLine, setShowSecondLine] = useState(false)
const [showThirdPart, setShowThirdPart] = useState(false)
return (
<div className="space-y-4">
<h1 className="min-h-[6rem] text-4xl leading-tight font-bold tracking-tight text-gray-900 sm:min-h-[8rem] sm:text-5xl md:min-h-[10rem] md:text-6xl dark:text-gray-100">
<TypingEffect
text="뭔가 만드는 걸 좋아하는"
delay={80}
onComplete={() => setShowSecondLine(true)}
/>
<br />
{showSecondLine && (
<>
<TypingEffect
text="송상록"
delay={80}
className="text-primary-500"
startDelay={200}
onComplete={() => setShowThirdPart(true)}
/>
{showThirdPart && <TypingEffect text="입니다" delay={80} startDelay={100} />}
</>
)}
</h1>
</div>
)
}
"뭔가 만드는 걸 좋아하는" 타이핑 → 완료 시 showSecondLine을 true로, 이후 showSecondLine && 오른쪽 TypingEffect 렌더링.200ms 대기 후 "송상록" 타이핑 → 완료 시 showThirdPart을 true로, 이후 showThirdPart && 오른쪽 TypingEffect 렌더링.100ms 대기 후 "입니다" 타이핑의존성배열에 뭐가 많은 건 react-hooks/exhaustive-deps 규칙 때문인데, 예상치 못한 버그를 방지하려고 useEffect 내 모든 변수가 들어갔다고 생각하심 됩니다...
LG 스타일로 만든 건가요