자바스크립트 없이 CSS만으로 만드는 hover 효과, 페이드인, 스피너, 바운스 애니메이션. transition과 @keyframes 두 가지만 알면 사이트가 살아납니다.
CSS 애니메이션에는 두 가지 접근법이 있어요. 상태 변화에는 transition, 반복 움직임에는 @keyframes를 사용합니다.
언제 뭘 쓸까요? hover·focus처럼 "A 상태 → B 상태"로 부드럽게 바뀌는 효과라면 transition이 간단해요. 스피너, 바운스처럼 여러 단계로 반복되는 효과라면 @keyframes를 사용하세요.
CSS 속성 값이 바뀔 때 그 변화를 부드럽게 만들어줘요. hover, focus, class 추가 등 상태 변화에 씁니다. 단 두 상태(시작→끝)만 정의해요.
0%부터 100%까지 여러 키프레임을 직접 정의해요. 자동 반복, 방향 전환, 일시정지 등 세밀한 제어가 가능합니다. 로딩 스피너, 주의 효과에 적합해요.
요소를 이동(translate), 회전(rotate), 확대(scale), 기울이기(skew)해요. GPU가 처리하므로 성능이 가장 좋습니다. 애니메이션의 단짝이에요.
브라우저에게 "이 요소는 곧 애니메이션될 거야"라고 미리 알려줘요. 무거운 애니메이션을 부드럽게 처리하는 성능 힌트지만, 남용하면 오히려 역효과예요.
| 속성 | 트리거 | 반복 | 중간 단계 | 사용 사례 |
|---|---|---|---|---|
transition |
상태 변화 (hover 등) | 없음 | 2단계만 | hover 효과, 메뉴 슬라이드 |
@keyframes |
자동 / JS 클래스 추가 | 가능 | 무제한 | 스피너, 바운스, 페이드인 |
이 네 가지만 조합하면 사이트에서 보이는 애니메이션 99%를 만들 수 있어요.
transition: all 0.3s → 0.3초 동안 변화해요. 일반적으로 hover는 0.15s~0.3s, 페이드인은 0.4s~0.8s가 자연스러워요. 너무 길면 답답하고 너무 짧으면 인지 못해요.
ease는 느리게 시작해 빠르게 가다 느리게 끝나요(가장 자연스러움). ease-in은 가속, ease-out은 감속, linear는 일정 속도예요. 스피너엔 linear, 버튼엔 ease를 주로 써요.
animation-iteration-count: infinite로 무한 반복해요. 숫자를 넣으면 그 횟수만 반복돼요. animation-direction: alternate를 함께 쓰면 앞뒤로 왕복합니다.
animation-fill-mode: both는 애니메이션 전후 상태를 유지해요. forwards는 끝 상태를 유지, backwards는 시작 상태를 유지해요. 페이드인처럼 한 번만 실행될 때 꼭 필요해요.
가장 자주 쓰이는 패턴 4가지예요. 복사해서 AI에게 "이 스타일로 우리 사이트에 적용해줘"라고 하면 바로 쓸 수 있어요.
클릭 가능한 느낌을 주는 가장 기본적인 패턴이에요.
/* 버튼 hover 효과 */ .btn { background: #4f46e5; color: #fff; padding: 12px 24px; border-radius: 8px; border: none; cursor: pointer; transition: background .2s, transform .15s, box-shadow .2s; } .btn:hover { background: #4338ca; transform: translateY(-2px); box-shadow: 0 6px 16px rgba(79,70,229,.35); } .btn:active { transform: translateY(0); }
페이지 로드 시 또는 스크롤할 때 자연스럽게 나타나는 효과예요.
/* 아래에서 위로 페이드인 */ @keyframes fadeInUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .fade-in-up { animation: fadeInUp 0.6s ease both; } /* 딜레이로 순서 연출 */ .card:nth-child(1) { animation-delay: 0s; } .card:nth-child(2) { animation-delay: .1s; } .card:nth-child(3) { animation-delay: .2s; }
API 요청 중 보여줄 수 있는 간단한 CSS 스피너예요.
/* 로딩 스피너 */ @keyframes spin { to { transform: rotate(360deg); } } .spinner { width: 40px; height: 40px; border: 3px solid #e5e7eb; border-top-color: #4f46e5; border-radius: 50%; animation: spin 0.7s linear infinite; }
CTA 버튼이나 알림 배지에 주의를 끄는 효과예요.
/* 주의 유도 pulse */ @keyframes pulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(79,70,229,.4); } 70% { box-shadow: 0 0 0 12px rgba(79,70,229,0); } } .pulse { animation: pulse 2s ease-out infinite; border-radius: 50%; /* 또는 버튼 border-radius */ }
성능 팁: 애니메이션을 만들 때는 opacity와 transform만 변경하세요. width, height, margin, top을 변경하면 레이아웃을 다시 계산해야 해서 버벅임이 생겨요. GPU가 처리하는 transform과 opacity가 성능이 가장 좋습니다.
각 카드의 버튼을 클릭하면 CSS 애니메이션이 실행됩니다. 코드는 ③ 섹션에서 복사할 수 있어요.
rotate(360deg) 무한 반복
translateY로 위아래 반복
scale(1.2) 반복
translateX로 좌우 흔들
투명도 + 위로 이동
rotate 왕복 반복
버튼을 클릭해서 transform의 다양한 효과를 확인하세요.
AI에게 애니메이션을 요청할 때 "예쁘게 해줘"보다 구체적으로 어떤 동작을 원하는지 설명할수록 정확한 코드를 받을 수 있어요.
아래 프롬프트를 복사해서 Claude, ChatGPT, Cursor에 붙여넣어 보세요.
내 웹페이지 버튼과 카드에 CSS 애니메이션을 추가해줘. 요구사항: - .btn 클래스: hover 시 2px 위로 올라가고 그림자가 생기는 transition - .card 클래스: 페이지 로드 시 아래에서 위로 페이드인, 카드마다 0.1s씩 딜레이 - .loading 클래스: 회전하는 스피너 (CSS만으로) - .badge-new 클래스: pulse 효과로 시선 유도 조건: - JavaScript 없이 CSS만으로 구현 - 성능을 위해 transform, opacity만 변경 - 기존 HTML은 그대로 유지하고 CSS만 추가
CSS 애니메이션을 처음 배울 때 헷갈리는 부분들이에요.
transition이 훨씬 간단해요. 세 단계 이상이거나 자동으로 반복되어야 하는 효과라면 @keyframes + animation을 사용하세요. 대부분의 hover 버튼, 메뉴 슬라이드는 transition으로 충분합니다.
width, height, top, margin 같은 레이아웃 속성을 애니메이션하면 브라우저가 레이아웃을 매 프레임 재계산해서 버벅여요. transform과 opacity만 변경하면 GPU가 처리하므로 훨씬 부드럽습니다. 위치 이동은 left 대신 translateX를 사용하세요.
opacity:0에서 시작해 끝난 뒤에도 opacity:1로 유지"할 때 꼭 필요합니다. both를 생략하면 애니메이션이 끝나자마자 초기 상태로 돌아가요.
element.classList.add('animated')로 클래스를 붙이고, 애니메이션이 끝나면 classList.remove로 제거하면 다음 클릭 때 다시 실행됩니다. animationend 이벤트로 끝나는 시점을 잡을 수 있어요.
transition과 @keyframes는 모든 모던 브라우저(Chrome, Firefox, Safari, Edge)에서 지원돼요. 구형 iOS Safari 대응이 필요하면 -webkit- 접두어를 붙이지만, 2020년 이후 버전이라면 대부분 불필요합니다.