[1010] 웹사이트 대문 바꿈

방법이있지·2025년 10월 10일

웹개발

목록 보기
16/19

웹사이트 링크

참고로 원랜 이랬어요.

사실 제가 짠건 아니고 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 시간만큼 대기 후 타이핑을 시작합니다.
    • 대기 중일 땐 startedfalse. 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>
  )
}
  • "뭔가 만드는 걸 좋아하는" 타이핑 → 완료 시 showSecondLinetrue로, 이후 showSecondLine && 오른쪽 TypingEffect 렌더링.
  • 200ms 대기 후 "송상록" 타이핑 → 완료 시 showThirdParttrue로, 이후 showThirdPart && 오른쪽 TypingEffect 렌더링.
  • 100ms 대기 후 "입니다" 타이핑

의존성배열에 뭐가 많은 건 react-hooks/exhaustive-deps 규칙 때문인데, 예상치 못한 버그를 방지하려고 useEffect 내 모든 변수가 들어갔다고 생각하심 됩니다...

profile
뭔가 만드는 걸 좋아하는 개발자 지망생입니다. 프로야구단 LG 트윈스를 응원하고 있습니다.

2개의 댓글

comment-user-thumbnail
2025년 10월 11일

LG 스타일로 만든 건가요

1개의 답글