한 줄 정답 —
@keyframes 이름 { 0% {...} 100% {...} }으로 변화 구간을 정의하고, 움직일 요소에animation: 이름 시간 ...;을 걸면 재생된다. 예:@keyframes fade { from { opacity: 0; } to { opacity: 1; } }+.box { animation: fade 0.4s ease; }.transition이 상태가 바뀔 때만 동작한다면,animation은 트리거 없이 스스로 시작하고 무한 반복도 된다.
핵심 요약#
- 두 단계다:
@keyframes로 구간을 정의하고, 요소에animation으로 그 구간을 재생한다. @keyframes는from/to또는0%~100%로 중간 지점까지 마음대로 끊을 수 있다.animation단축에는 여덟 속성이 들어간다: name · duration · timing-function · delay · iteration-count · direction · fill-mode · play-state.- 무한 반복은
animation-iteration-count: infinite, 끝 상태 유지는animation-fill-mode: forwards다. transition은 hover 같은 상태 변화에 반응할 때,animation은 스스로 움직이거나 반복할 때 쓴다.
CSS 애니메이션 keyframes 사용법, 두 단계로 나눠 보기#
CSS 애니메이션은 늘 두 조각으로 이뤄진다. 먼저 무엇이 어떻게 변할지를 @keyframes에 적어 두고, 그 변화를 어떤 요소에 얼마 동안 재생할지를 animation 속성으로 지정한다. 이름표 하나로 둘을 잇는 구조다.
transition과 가장 큰 차이는 트리거다. transition은 :hover나 클래스 토글처럼 값이 바뀌는 순간에만 동작한다. animation은 요소가 화면에 그려지는 순간 스스로 시작하고, 원하면 끝없이 반복한다. 로딩 스피너, 깜빡임, 자동 등장 효과가 모두 animation의 영역이다.
/* 1단계: 변화 구간을 정의 */
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
/* 2단계: 요소에 재생 */
.box {
animation: fade-in 0.4s ease;
}
@keyframes — 변화의 구간을 그린다#
@keyframes 뒤에 이름을 붙이고, 중괄호 안에 시점별 상태를 적는다. 시작과 끝만 필요하면 from/to, 중간을 끊고 싶으면 백분율을 쓴다.
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.15); } /* 중간에 가장 커짐 */
100% { transform: scale(1); }
}
from은 0%, to는 100%와 같다. 적지 않은 시점은 브라우저가 앞뒤 값을 보고 알아서 보간한다. 같은 속성을 여러 구간에 흩어 적으면 그 속성이 그 흐름대로 변한다.
animation — 정의한 구간을 재생한다#
요소 쪽에는 어떤 keyframes를, 얼마 동안, 어떻게 재생할지를 적는다. 최소한 이름과 시간(duration) 두 가지는 있어야 움직인다. duration이 없으면 기본이 0s라 아무것도 보이지 않는데, 이는 애니메이션이 "안 먹는" 가장 흔한 원인이다.
.loader {
animation-name: pulse;
animation-duration: 1s;
animation-iteration-count: infinite; /* 무한 반복 */
}
animation의 여덟 속성 정리#
animation은 여덟 개의 하위 속성을 한 줄로 묶은 단축이다. 각각이 무엇을 정하는지 알아야 단축을 자유롭게 쓸 수 있다.
| 속성 | 정하는 것 | 자주 쓰는 값 |
|---|---|---|
animation-name | 재생할 @keyframes 이름 | 직접 지은 이름 |
animation-duration | 한 번 도는 데 걸리는 시간 | 0.4s, 1s |
animation-timing-function | 속도 곡선 | ease, linear, ease-out |
animation-delay | 시작까지 기다리는 시간 | 0s, 0.2s |
animation-iteration-count | 몇 번 반복할지 | 숫자, infinite |
animation-direction | 반복 방향 | normal, alternate |
animation-fill-mode | 재생 전후로 어떤 값을 유지할지 | forwards, both |
animation-play-state | 재생/일시정지 | running, paused |
iteration-count와 direction — 반복을 다룬다#
iteration-count는 반복 횟수다. 숫자로 적으면 그만큼, infinite면 무한히 돈다. direction은 반복할 때 방향인데, alternate로 두면 갔다가 되돌아오며 자연스럽게 왕복한다.
.heartbeat {
animation: pulse 1.2s ease-in-out infinite alternate;
}
infinite alternate 조합은 0% → 100%로 갔다가 다시 100% → 0%로 돌아오므로, keyframes에 끝 구간을 따로 적지 않아도 매끄럽게 반복된다.
fill-mode — 끝난 뒤 상태를 정한다#
기본적으로 애니메이션은 끝나면 원래(CSS에 적힌) 값으로 되돌아간다. 등장 효과를 만들었는데 끝나자마자 다시 사라지는 건 이 때문이다. animation-fill-mode: forwards를 주면 마지막 키프레임 상태를 그대로 유지한다.
@keyframes slide-up {
from { opacity: 0; transform: translateY(16px); }
to { opacity: 1; transform: translateY(0); }
}
.card {
animation: slide-up 0.5s ease-out forwards; /* 끝난 자리에 멈춤 */
}
fill-mode 값 | 효과 |
|---|---|
none (기본) | 재생 전후 모두 원래 값 |
forwards | 끝난 뒤 마지막 키프레임 값 유지 |
backwards | delay 동안 첫 키프레임 값을 미리 적용 |
both | forwards + backwards 둘 다 |
단계별로 따라하기 — 자동 등장 카드 만들기#
스크롤하다 나타나는 듯한, 가장 많이 쓰는 "아래에서 떠오르며 페이드인" 패턴이다.
@keyframes에 **시작(투명·아래)**과 **끝(불투명·제자리)**을 정의한다.- 요소에
animation으로 이름·시간·곡선을 건다. - 끝난 상태를 유지하도록
forwards를 붙인다. - 움직임은
top·margin대신transform으로 준다(부드럽고 성능에 유리).
@keyframes slide-up {
from {
opacity: 0;
transform: translateY(24px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.card {
animation: slide-up 0.5s ease-out forwards;
}
/* 여러 카드를 시차를 두고 등장시키기 */
.card:nth-child(2) { animation-delay: 0.1s; }
.card:nth-child(3) { animation-delay: 0.2s; }
animation-delay를 카드마다 조금씩 늘리면 순차로 등장하는 효과가 난다. 카드들을 가로로 늘어놓는 배치는 CSS flexbox 정렬 — 주축·교차축으로 한 번에 이해하기와 함께 보면 레이아웃과 움직임을 같이 잡을 수 있다.
transition과 animation, 언제 무엇을 쓸까#
둘 다 시간에 걸쳐 값을 바꾸지만 쓰임이 다르다. 헷갈릴 땐 트리거가 있느냐로 가른다.
| 기준 | transition | animation |
|---|---|---|
| 시작 방식 | 상태 변화(hover·focus·클래스 토글) | 스스로 시작 |
| 중간 구간 | 시작·끝 두 값만 | @keyframes로 여러 구간 |
| 반복 | 불가(왕복은 상태 왕복으로만) | infinite로 무한 반복 |
| 적합한 곳 | 버튼 hover, 펼침/접힘 | 스피너, 깜빡임, 자동 등장 |
버튼에 마우스를 올렸을 때 색을 바꾸는 정도라면 transition이 더 단순하고 적합하다. 그 기본기는 CSS transition 사용법 — hover 애니메이션 부드럽게 만들기에 따로 정리해 두었다. 반대로 트리거 없이 계속 돌거나, 여러 구간을 거치는 복잡한 움직임이면 animation이 맞다.
성능과 접근성 — 빠뜨리기 쉬운 두 가지#
성능: 움직임을 줄 때 left·top·width·height를 애니메이션하면 매 프레임 레이아웃이 다시 계산돼 끊길 수 있다. 가능하면 transform과 opacity만 애니메이션한다. 이 둘은 레이아웃을 건드리지 않아 부드럽게 도는데, 위치를 옮기는 무한 애니메이션일수록 차이가 크다.
접근성: 화면이 계속 움직이면 어지럼증·전정기관 질환이 있는 사용자에게 불편하거나 위험할 수 있다. 운영체제에서 "동작 줄이기"를 켠 사용자를 위해 prefers-reduced-motion 미디어 쿼리로 애니메이션을 약하게 하거나 끈다.
@media (prefers-reduced-motion: reduce) {
.card,
.loader {
animation: none;
}
}
CSS 애니메이션이 안 움직일 때 — 흔한 원인#
대부분 다음 중 하나다.
duration이 없다.animation에 시간을 안 적으면 기본0s라 즉시 끝나 보이지 않는다. 가장 흔한 실수다.@keyframes이름과animation-name이 다르다. 오타 하나면 재생할 구간을 못 찾아 아무 일도 일어나지 않는다.- 끝난 뒤 원래대로 돌아간다. 등장 효과가 다시 사라진다면
animation-fill-mode: forwards가 빠진 것이다. - 애니메이션되지 않는 속성을 바꿨다.
display같은 값은 보간되지 않는다.opacity로 우회한다. - 요소가 화면에 없다.
display: none인 요소는 애니메이션이 시작되지 않는다.
자주 묻는 질문#
Q. keyframes는 어떻게 사용하나요?
A. 두 단계입니다. 먼저 @keyframes 이름 { from {...} to {...} }(또는 0%~100%)로 변화 구간을 정의하고, 움직일 요소에 animation: 이름 시간;을 걸어 재생합니다. 이름표가 둘을 잇는 핵심이라 @keyframes의 이름과 animation-name이 정확히 같아야 합니다.
Q. transition과 animation은 무엇이 다른가요?
A. transition은 hover·클래스 토글처럼 상태가 바뀌는 순간에만 동작하고 시작·끝 두 값만 다룹니다. animation은 트리거 없이 스스로 시작하며 @keyframes로 여러 구간을 거치고 infinite로 무한 반복할 수 있습니다. 버튼 hover는 transition, 스피너·자동 등장은 animation이 맞습니다.
Q. CSS로 무한 반복 애니메이션은 어떻게 만드나요?
A. animation-iteration-count: infinite를 주면 됩니다. 단축으로는 animation: pulse 1s ease-in-out infinite처럼 적습니다. 갔다가 되돌아오며 왕복시키려면 infinite alternate를 함께 씁니다.
Q. 애니메이션이 끝나면 원래대로 돌아가는데 끝 상태를 유지하려면요?
A. animation-fill-mode: forwards를 주면 마지막 키프레임 값을 그대로 유지합니다. 기본값 none은 재생이 끝나면 CSS에 적힌 원래 값으로 되돌아가기 때문에 등장 효과가 다시 사라집니다. delay 동안 첫 키프레임을 미리 적용하려면 both를 씁니다.
관련 글#
애니메이션은 결국 "시간에 걸쳐 값이 변하는 것"이다. 상태 변화에 반응하는 transition을 먼저 익히고, 배치(레이아웃)와 함께 보면 화면에 자연스러운 움직임을 입히기 쉬워진다.
- CSS transition 사용법 — hover 애니메이션 부드럽게 만들기
- scroll-snap과 sticky 헤더 — JavaScript 없이 만드는 스크롤 인터랙션
- CSS flexbox 정렬 — 주축·교차축으로 한 번에 이해하기