타이핑 효과와 커서 깜빡임, JS 아닌 CSS 원리

타이핑 효과가 걸린 문구를 보면 자바스크립트가 실시간으로 한 글자씩 그려 넣는 것처럼 보입니다. 옆에서 깜빡이는 커서까지 더해지면 더 그렇게 느껴집니다. 하지만 이 효과는 순수 CSS 애니메이션 두 개만으로 만들어집니다.

요약 ① 글자가 한 글자씩 나타나는 것은 텍스트를 추가하는 게 아니라 요소의 너비(width)를 0에서 전체 글자 수만큼 늘리는 CSS 애니메이션입니다 ② overflow: hidden이 아직 늘어나지 않은 너비 밖의 글자를 가려서, 너비가 늘어날수록 글자가 나타나는 것처럼 보이게 만듭니다 ③ 옆의 커서는 별개의 border-right + 점멸(blink) 애니메이션이며, 타이핑 애니메이션과 동시에 실행될 뿐 서로 다른 효과입니다

타이핑 효과 — 왜 자바스크립트처럼 보이나

글자가 시간차를 두고 하나씩 나타나기 때문에 대부분 자바스크립트가 setInterval 같은 걸로 문자열을 한 글자씩 이어 붙인다고 생각합니다. 실제로 그렇게 구현하는 라이브러리도 많습니다.

하지만 CSS만으로도 똑같은 착시를 만들 수 있습니다. 텍스트 전체를 처음부터 요소 안에 넣어두고, 그 요소의 너비를 0에서 전체 텍스트 폭까지 애니메이션으로 늘립니다. 요소에 overflow: hidden을 걸어두면 아직 너비가 닿지 않은 부분의 글자는 화면에 보이지 않습니다.

너비가 조금씩 늘어날 때마다 가려져 있던 뒤쪽 글자가 순서대로 드러나므로, 실제로는 텍스트를 추가하는 게 아니라 '가리개'가 옆으로 밀려나는 것뿐입니다. 그런데 이 너비 증가가 매끈하게 이어지면 글자 단위가 아니라 픽셀 단위로 흐르듯 보이게 됩니다.

여기서 CSS의 steps() 타이밍 함수가 핵심입니다. steps(글자수, end)를 쓰면 너비 애니메이션이 부드럽게 흐르지 않고 글자 수만큼 딱딱 끊어서 늘어납니다. 글자 폭이 모두 같은 monospace(고정폭) 글꼴, 그리고 글자 하나의 너비를 뜻하는 CSS 단위 ch를 함께 쓰면 한 단계가 정확히 한 글자와 일치합니다.

확인하는 방법 — 코드로 직접 만들어보기

먼저 텍스트를 감쌀 요소에 overflow: hidden, white-space: nowrap, font-family: monospace를 지정합니다. 줄바꿈과 넘침을 막아야 너비 애니메이션이 의도대로 동작합니다.

다음으로 너비를 0에서 텍스트 글자 수만큼(예: 20글자면 20ch)으로 늘리는 keyframes를 만들고, animation 속성에 duration과 steps(글자수, end)를 지정합니다. 이 값이 실제 글자 수와 다르면 텍스트가 다 보였는데도 애니메이션이 계속 진행 중이거나, 반대로 애니메이션이 끝났는데 마지막 글자가 아직 안 보이는 어긋남이 생깁니다.

커서는 별도로 만듭니다. border-right로 세로선을 하나 그려 넣고, border-color를 투명과 커서색 사이에서 왕복시키는 blink-caret 애니메이션을 콤마로 이어서 함께 겁니다. step-end를 써야 서서히 흐려지지 않고 딱딱 켜지고 꺼지는 커서처럼 보입니다.

저희 도구로 확인하기. 문구와 타이핑 속도, 커서 색상을 입력하면 이 원리대로 계산된 CSS·HTML 코드가 미리보기와 함께 바로 만들어집니다.

CSS 타이핑 텍스트 효과 생성기

무료 · 가입 불필요 · 브라우저에서 바로 CSS 코드 생성

자주 막히는 지점

상황원인
텍스트를 바꿔도 처음부터 다시 재생되지 않음너비 애니메이션은 요소가 새로 렌더링될 때 시작되므로, 텍스트 변경 시 요소를 다시 마운트해야 처음부터 재생됩니다
일반 폰트에서 타이핑이 부자연스러움글자마다 실제 폭이 달라 ch 단위 너비와 맞지 않아, 글자가 미리 다 보이거나 끝에 빈 여백이 남습니다
steps() 숫자와 글자 수가 다름애니메이션 진행 속도와 실제로 보이는 글자 수가 어긋나 마지막 글자가 늦게 나타나거나 일찍 다 드러납니다
커서가 깜빡이지 않고 계속 떠 있음border-right만 넣고 별도의 blink-caret 애니메이션을 함께 걸지 않은 경우입니다

표에 정리된 상황 대부분은 '너비 애니메이션의 steps 값'과 '실제 글자 수'가 어긋나는 데서 시작됩니다. 두 값을 항상 같은 곳에서 함께 계산하면 이런 어긋남이 생기지 않습니다.

정리

타이핑 효과에서 글자가 한 글자씩 나타나는 것은 텍스트 추가가 아니라, overflow: hidden으로 가려둔 요소의 너비를 steps() 단위로 늘리는 CSS 애니메이션입니다. 옆의 커서 깜빡임은 여기에 border-right와 별도의 점멸 애니메이션을 더한 것뿐입니다.

문구 하나를 한 번 타이핑하고 반복하는 정도라면 자바스크립트 없이 이 두 애니메이션 조합만으로 충분합니다.

CSS 타이핑 텍스트 효과 생성기

무료 · 가입 불필요 · 브라우저에서 바로 CSS 코드 생성


📖 타이핑 효과에서 흔히 겪는 3가지 현실적인 사연

프론트엔드 개발자나 퍼블리셔들이 CSS 타이핑 효과를 구현할 때 겪는 대표적인 실패 사례 3가지입니다.

사연 1: 글자가 와이퍼처럼 스르륵 잘려 보이는 현상

포트폴리오 사이트를 만들던 개발자 A씨는 20글자의 제목을 타이핑하기 위해 steps(20, end)를 적용했습니다. 그런데 글자가 한 글자씩 탁탁 끊기며 나타나는 대신, 자동차 와이퍼가 지나가듯 픽셀 단위로 글자가 반쯤 잘리며 부드럽게 나타나는 현상이 발생했습니다. 원인은 폰트였습니다. 각 글자의 너비가 1ch로 동일한 monospace(예: Fira Code) 폰트 대신, 글자마다 폭이 다른 가변폭 폰트(Inter)를 사용했기 때문에 1-step이 정확히 1-character와 일치하지 않아 발생한 참사였습니다.

사연 2: 커서가 터미널처럼 깜빡이지 않고 서서히 점멸하는 현상

랜딩 페이지를 제작하던 B씨는 타이핑 커서가 터미널 프롬프트처럼 뚜렷하게 켜지고 꺼지길 원했습니다. 그러나 결과물에서는 커서가 숨을 쉬듯 서서히 흐려졌다 밝아지는 페이드(fade) 효과가 나타났습니다. 원인은 blink-caret 키프레임 애니메이션에 ease-in-out 타이밍 함수를 적용했기 때문입니다. 이 효과를 제대로 내려면 중간 과정 없이 상태가 즉시 전환되는 step-end를 사용해야만 했습니다.

사연 3: 한국어(CJK) 문자열에서 중간에 멈춰버린 타이핑

다국어 SaaS 웹사이트를 개발하던 C씨는 한국어 헤딩 텍스트에 타이핑 효과를 적용했습니다. 영문 20글자는 steps(20)width: 20ch로 완벽하게 동작했지만, 한국어 20글자는 중간쯤에서 타이핑이 멈춰버렸습니다. 영문과 달리 한국어 등 CJK(중일한) 문자는 monospace 폰트에서도 대략 2ch 너비를 차지하기 때문입니다. 결국 목표 너비를 40ch로, 단계 수를 steps(40)으로 두 배 늘려서야 정상적으로 끝까지 애니메이션을 마칠 수 있었습니다.


🔬 핵심 기술 메커니즘: CSS 가리개 해제 모델과 계단 함수

자바스크립트 없이 어떻게 글자가 하나씩 추가되는 것처럼 보일까요? 바로 너비 기반 가리개 해제 모델(CSS Width Masking Reveal Model) 덕분입니다.

1. 가리개 착시 구조 (Overflow Masking)

실제로 텍스트가 DOM에 동적으로 추가되는 것이 아닙니다. 텍스트는 처음부터 완성된 상태로 DOM에 존재합니다. 단지 부모 컨테이너에 overflow: hidden이 적용되어 있어 현재 width를 벗어난 텍스트가 잘려서 보이지 않을 뿐입니다. @keyframes typing { from { width: 0; } to { width: Nch; } } 애니메이션을 통해 너비를 서서히 늘려주면 가려져 있던 글자들이 차례로 드러납니다.

2. steps(N, end) 계단 타이밍 함수

자연스럽게 스르륵 늘어나는 너비를 '한 글자 단위'로 딱딱 끊어서 늘려주는 것이 steps() 함수의 역할입니다. 수식으로 표현하면 다음과 같습니다: $$ ext{At time } t_k = rac{k}{N} cdot T, quad ext{width jumps to } rac{k}{N} imes N ext{ch} = k ext{ch} $$ 전체 시간 $T$ 동안, 너비는 정확히 $N$번의 계단식 도약(discrete jump)을 하며, 매 도약마다 정확히 1ch만큼 너비가 증가합니다.

3. ch 단위와 고정폭(Monospace) 글꼴의 필수성

CSS 단위인 1ch는 현재 폰트에서 숫자 "0"의 너비를 의미합니다.

  • 고정폭(Monospace) 글꼴: 모든 글자(A, i, W 등)의 너비가 1ch로 동일합니다. 따라서 1-step 너비 증가 = 1-character 드러남의 공식이 완벽하게 성립합니다.
  • 가변폭(Proportional) 글꼴: "i"는 약 0.3ch, "W"는 약 1.2ch 등 너비가 다릅니다. 이 때문에 스텝 증가폭과 글자 폭이 어긋나 타이핑 착시가 깨지게 됩니다.

텍스트 끝에서 깜빡이는 커서는 텍스트 애니메이션과 완전히 독립된 별개의 효과입니다. 보통 텍스트 컨테이너의 우측 테두리(border-right: 3px solid)를 이용해 커서를 그립니다. 여기에 border-colortransparent와 지정된 색상 간에 전환하는 애니메이션을 걸되, 반드시 step-end를 사용하여 흐려짐(페이드) 없이 무 자르듯 색상이 온/오프되게 만듭니다.


📊 비교 분석표: 텍스트 타이핑 효과 구현 방식

순수 CSS 방식과 자바스크립트를 활용한 방식의 장단점을 비교해 보았습니다.

구현 방식설명JS 의존성폰트 제약다중 줄(Multi-line) 지원커스터마이징 유연성SEO 텍스트 크롤링
순수 CSS Width Masksteps()ch 단위를 이용한 가리개 해제 방식0KB (없음)Monospace 필수어려움 (단일 줄 권장)낮음완벽함 (처음부터 DOM 존재)
JS innerHTML AppendsetInterval로 텍스트를 한 글자씩 DOM에 추가높음없음 (모든 폰트 가능)보통매우 높음문제 가능성 (동적 렌더링)
GSAP SplitText텍스트를 쪼개어 각각에 딜레이(Stagger) 부여높음 (라이브러리 필요)없음 (모든 폰트 가능)뛰어남매우 높음완벽함
Typed.js Library유명한 타이핑 애니메이션 전용 JS 라이브러리높음 (라이브러리 필요)없음뛰어남최고 (백스페이스 지원 등)설정에 따라 다름
SVG stroke-dashoffset손글씨를 직접 써내려가는 듯한 SVG 경로 효과0KB ~ 낮음(SVG 경로 의존)불가능경로 그리기 한정텍스트 노출 안됨

⚠️ 흔히 저지르는 5가지 타이핑 CSS 실수

타이핑 애니메이션을 작성할 때 빈번하게 발생하는 5가지 주요 실수들입니다.

  1. 가변폭(Proportional) 폰트 사용: 글자 폭이 균일하지 않아 애니메이션 중간에 글자가 반쯤 쪼개져 보이는 현상을 유발합니다.
  2. 문자 수(steps) 계산 오류: 특히 다국어(한국어, 일본어, 중국어)의 경우 영문 알파벳의 2배(약 2ch)의 너비를 차지한다는 사실을 간과하여 애니메이션이 중간에 끊기게 됩니다.
  3. 부드러운 이징(ease, linear) 사용: steps(N, end) 대신 부드러운 타이밍 함수를 쓰면, 타이핑이 아니라 텍스트가 옆으로 스윽 부드럽게 나타나는 슬라이드 마스크 효과가 되어 버립니다.
  4. white-space: nowrap 누락: 텍스트가 길어질 때 줄바꿈이 발생하면 가로 너비 마스킹 뷰를 완전히 망치게 됩니다.
  5. animation-fill-mode: forwards 누락: 이 속성이 없으면 애니메이션이 100% 도달 후 즉시 초기 상태(width: 0)로 되돌아가, 타이핑을 마친 텍스트가 눈앞에서 사라져 버립니다.

💡 픽셀 퍼펙트 타이핑 효과 5단계 워크플로우

  1. 텍스트 컨테이너 설정: 타이핑할 텍스트를 감싸는 요소에 overflow: hidden; white-space: nowrap;을 부여하여 가리개 역할을 만들고 줄바꿈을 방지합니다.
  2. Monospace 폰트 적용: 요소에 font-family: 'Fira Code', 'Courier New', monospace;를 적용하여 모든 글자 폭을 동일하게 만듭니다.
  3. 정확한 글자 수($N$) 계산: 띄어쓰기를 포함한 총 글자 수를 셉니다. 영문/숫자는 1자당 1, 한글/한자 등 CJK 문자는 1자당 2로 계산합니다.
  4. 타이핑 키프레임과 steps 애니메이션 연결: @keyframes typing { from { width: 0 } to { width: Nch } }를 정의하고 요소에 animation: typing 2s steps(N, end) forwards;를 적용합니다.
  5. 독립된 커서 점멸 추가: 동일한 요소에 border-right: 2px solid;로 커서를 그리고, @keyframes blink-caret을 정의한 후 step-end 방식으로 깜빡임 애니메이션을 추가 할당합니다.

🔍 전문가의 타이핑 애니메이션 꿀팁 7선

  1. forwards 모드 필수 지정: 타이핑 완료 후 텍스트가 화면에 계속 남아있게 하려면 animation-fill-mode: forwards를 절대 잊지 마세요.
  2. 커서 점멸은 반드시 step-end: 터미널 커서의 날렵한 점멸 감각을 재현하려면 ease 대신 step-end를 사용해야 합니다.
  3. 초기 지연시간(Delay) 추가: 사용자에게 읽을 준비 시간을 주기 위해 타이핑 애니메이션 시작 전 animation-delay: 1s 정도의 지연을 주면 휠씬 자연스럽습니다.
  4. CJK 문자 호환 처리: 한국어나 일본어 적용 시, 목표 widthN2ch로 늘리고 steps도 N2로 설정하세요. 또는 폰트 크기 상대 단위인 em 단위를 활용하여 너비를 수동으로 매칭하는 방법도 있습니다.
  5. 다중 줄(Multi-line) 체이닝 기법: 여러 줄의 타이핑 효과를 구현할 때는 각 줄 요소에 animation-delay를 순차적으로 더 길게 줘서, 윗줄이 끝날 때 아랫줄이 시작하도록 타이밍을 맞춥니다.
  6. 타이핑 종료 후 커서 숨기기: 무한히 깜빡이는 커서가 거슬린다면, animation: blink-caret 0.75s step-end 5처럼 반복 횟수를 지정하여 몇 번 깜빡인 후 커서가 사라지게 만드세요.
  7. 자동화 생성 도구 활용: CSS 코드를 직접 짜고 글자 수를 세기 번거롭다면, 이 페이지에서 제공하는 무료 제너레이터 웹 툴을 사용하여 글자 수와 steps가 자동 계산된 코드를 추출하세요.

🎯 의사결정 트리: 텍스트 노출 애니메이션 선택 가이드

내 프로젝트에 어떤 방식을 사용해야 할까요?

  • Q1. 사용해야 하는 폰트가 가변폭(Proportional) 폰트인가?
    • Yes ➔ GSAP SplitText 또는 JS 기반 라이브러리 (Typed.js)
    • No (Monospace 사용 가능) ➔ Q2로 이동
  • Q2. 텍스트가 여러 줄(Multi-line)에 걸쳐 자동으로 줄바꿈이 일어나야 하는가?
    • Yes ➔ JS 기반 라이브러리
    • No (단일 줄 또는 수동 줄바꿈) ➔ Q3으로 이동
  • Q3. 손으로 직접 필기하는 듯한 펜글씨 효과가 필요한가?
    • Yes ➔ SVG stroke-dashoffset 핸드라이팅 애니메이션
    • No ➔ 순수 CSS steps() 기반 타이핑 효과 (최고 성능, 제로 의존성)

📑 핵심 용어 (Terminology) 치트시트

  • 타이핑 효과(Typewriter Effect): 기계식 타자기나 터미널 창처럼 텍스트가 한 글자씩 차례대로 나타나는 시각 효과.
  • 계단 함수(steps()): CSS 애니메이션 타이밍 함수 중 하나로, 중간 프레임을 스무스하게 연결하지 않고 지정한 횟수만큼 화면을 즉시 컷 전환시킵니다.
  • ch 단위(Character Unit): CSS 폰트 상대 길이 단위. 현재 폰트의 아라비아 숫자 '0' 문자의 너비값을 1ch로 가집니다.
  • 고정폭 폰트(Monospace Font): 모든 문자가 동일한 가로 너비를 가지도록 디자인된 폰트 (예: Courier, Fira Code).
  • 가리개 착시(Overflow Masking): overflow: hidden을 이용해 특정 영역 밖의 요소를 숨기는 기법.
  • 커서 점멸(Cursor Blink): 타이핑 텍스트 맨 끝에 위치한 얇은 사각형 또는 선형 커서가 주기적으로 나타나고 사라지는 효과.
  • 너비 애니메이션(Width Animation): width: 0에서 목표 치수까지 가로 길이가 늘어나는 CSS 트랜지션/애니메이션.
  • CJK 더블바이트 문자: 중국어(Chinese), 일본어(Japanese), 한국어(Korean) 문자로, 고정폭 환경에서도 알파벳보다 약 2배의 공간을 차지하는 문자 체계.

자주 묻는 질문

Q1. 타이핑 효과에서 고정폭(Monospace) 폰트 사용이 '권장'이 아니라 '필수'인 이유는 무엇인가요?

A1. steps(N) 함수는 지정된 목표 너비(Nch)를 정확하게 N개의 동일한 길이 간격으로 쪼갭니다. 이때 각 글자의 실제 폭이 다르면, 어떤 글자는 1스텝보다 좁아서 다음 글자 일부가 함께 보이고, 어떤 글자는 넓어서 아직 다 보여지지 않는 현상이 생깁니다. 1스텝 = 1글자 노출의 공식을 성립시키려면 모든 글자가 정확히 같은 1ch 폭을 가져야만 하기 때문입니다.

Q2. 한국어 같은 CJK 문자가 2ch 너비를 차지할 때 이를 어떻게 해결해야 하나요?

A2. 두 가지 접근법이 있습니다. 첫째, 목표 너비와 steps 값을 모두 2배로 늘립니다. 예를 들어 10글자 한글이라면 width: 20ch; steps(20). 둘째, ch 단위 대신 em 단위(폰트 크기 자체)를 활용하고 수동으로 비율을 튜닝합니다. 최근에는 가변폭 폰트와 한글을 완벽하게 지원하기 위해 순수 CSS 대신 JS를 이용해 글자를 하나씩 append 하는 방식을 혼용하는 추세이기도 합니다.

Q3. 커서 깜빡임 애니메이션에서 부드러운 easing을 사용하면 왜 안 되나요?

A3. 실제 컴퓨터 터미널이나 타자기의 커서는 디지털 신호처럼 On/Off 상태만 존재합니다. easelinear를 사용해 불투명도가 0에서 1로 부드럽게 변하면, 커서라기보다는 고장난 형광등이나 호흡하는 LED처럼 보여 몰입감을 심각하게 저해합니다. 무 자르듯 색상이 전환되는 step-end를 사용하는 것이 터미널의 리얼리티를 살리는 핵심 디테일입니다.

가격 보기카톡 무료 상담