CSS / 애니메이션 / animation
CSS animation은 @keyframes로 정의한 여러 상태를 시간에 따라 재생하는 애니메이션입니다. 재생 시간, 반복 횟수, 진행 방향과 일시 정지 등을 CSS로 제어할 수 있습니다. 두 상태 사이의 변화에 반응하는 transition과 달리, 키프레임 순서에 따라 여러 단계를 진행하거나 반복하는 효과에 적합합니다.
@keyframes와 animation이 나누어 맡는 역할
@keyframes에는 어느 시점에 어떤 속성 값을 사용할지 적습니다. 0%는 한 번의 재생 시작, 100%는 그 재생의 끝이며 from·to로도 쓸 수 있습니다. 50% 같은 중간 지점을 추가하면 그 지점을 거쳐 진행합니다. 중간 값을 표현할 수 있는 속성은 키프레임 사이에서 계산되어 바뀌며, 모든 CSS 속성이 같은 방식으로 부드럽게 변하는 것은 아닙니다.
animation은 어떤 키프레임을 어떤 조건으로 재생할지 정합니다. 아래는 일반적인 시간 기반 애니메이션에 필요한 기본 설정입니다. 스크롤 타임라인 등 확장 기능은 이 실습에 포함하지 않습니다.
| 속성 | 예제 값과 의미 |
|---|---|
| animation-name | travel: 사용할 @keyframes 이름 |
| animation-duration | 2s: 한 번의 재생 시간 |
| animation-timing-function | linear: 각 키프레임 구간을 일정한 진행률로 전환 |
| animation-delay | 0s: 시작 전 지연 없음 |
| animation-iteration-count | infinite: 계속 반복 |
| animation-direction | alternate: 정방향과 역방향을 번갈아 재생 |
| animation-fill-mode | none: 지연·종료 구간에 키프레임 상태를 유지하지 않음 |
| animation-play-state | paused: 처음에는 일시 정지, 체크하면 running |
duration은 전체 반복을 모두 합친 시간이 아닙니다. 2s를 4회 재생하면 양의 지연이 없고 일시 정지하지 않은 조건에서 재생 구간은 8초입니다. alternate에서 오른쪽으로 간 뒤 왼쪽으로 오는 동작은 각각 한 번씩이므로 두 번의 반복을 사용합니다. 지연 시간은 매 반복 앞에 자동으로 다시 붙지 않습니다.
전체 예제: 이동·크기 변화와 일시 정지
다음 코드를 animation.html로 저장하고 브라우저에서 엽니다. 외부 파일은 필요하지 않습니다. 마우스로 체크하거나 Tab으로 체크박스를 선택한 뒤 Space를 눌러 재생과 일시 정지를 비교하세요.
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>체크박스로 CSS 애니메이션 제어</title>
<style>
body { margin: 20px; font: 16px/1.6 "Noto Sans KR", sans-serif; color: #203348; }
* { box-sizing: border-box; }
h1 { font-size: 24px; }
label { display: inline-block; padding: 10px 4px; }
.stage { max-width: 500px; min-height: 130px; padding: 24px 16px;
border: 1px solid #8ba8bb; background: #eef4f8; }
.box { width: 64px; height: 64px; background: #1768a5;
transform: translateX(0) scale(1); transform-origin: center;
animation: travel 2s linear 0s infinite alternate none paused; }
@keyframes travel {
0% { transform: translateX(0) scale(0.7); background: #1768a5; }
50% { transform: translateX(70px) scale(1.15); background: #d99d33; }
100% { transform: translateX(140px) scale(1.3); background: #087b76; }
}
#play:checked ~ .stage .box { animation-play-state: running; }
@media (prefers-reduced-motion: reduce) {
.box { animation: none; }
}
</style>
</head>
<body>
<h1>재생과 일시 정지 비교</h1>
<input id="play" type="checkbox">
<label for="play">애니메이션 재생</label>
<div class="stage"><div class="box"></div></div>
<p>체크를 해제하면 일시 정지하고, 다시 체크하면 멈춘 지점부터 이어집니다.</p>
</body>
</html>
일반 동작 환경에서 상자는 시작 위치의 작은 파란 모양에서 오른쪽의 큰 초록 모양으로 이동합니다. 50%에서는 중간 위치와 크기를 거치며, 정방향 2초 다음에는 역방향 2초가 이어집니다. 체크를 해제하면 현재 지점에서 멈추고 다시 체크하면 이어집니다. 페이지를 새로 고치면 처음의 일시 정지 상태로 돌아갑니다.
크기 변화는 width·height 대신 transform의 scale로 표현했습니다. 시각적으로 커져도 문서 흐름에서 차지하는 원래 64×64px 상자 크기는 그대로입니다. 주변 요소를 실제로 밀어내며 크기를 바꿔야 한다면 width·height 방식과 레이아웃 영향을 따로 검토하세요. 이 예제는 변형된 상자가 들어갈 공간을 부모에 확보했습니다.
속도·지연·반복·방향을 하나씩 비교
기본 .box의 2s만 4s로 바꾸면 한 번의 이동이 느려집니다. 확인 후 2s로 복원하고 linear만 ease-in-out으로 바꾸어 같은 경로에서 완급이 달라지는지 봅니다. 키프레임에 50%가 있으므로 속도 함수는 각 키프레임 구간에 적용된다는 점을 고려하세요. 다시 linear로 복원합니다.
0s를 1s로 바꾸면 재생이 시작되기 전에 지연됩니다. 음수 delay는 단순히 기다리는 대신 애니메이션이 그만큼 이미 진행한 지점에서 시작하게 합니다. 시간 변화가 보이지 않는다면 아래의 동작 줄이기 조건을 먼저 확인하세요.
infinite를 2로 바꾸면 두 번만 재생합니다. alternate라면 정방향 후 역방향으로 돌아오며 종료됩니다. normal은 매번 정방향, reverse는 매번 역방향, alternate-reverse는 역방향부터 번갈아 재생합니다. alternative·alternative-reverse는 유효한 값이 아니므로 각각 alternate·alternate-reverse를 사용해야 합니다.
단축 속성은 누락한 개별 설정을 초기화할 수 있습니다. 일시 정지나 fill-mode 등을 별도 선언할 때는 단축 속성보다 뒤에 두거나 실제 우선순위를 확인하세요. 애니메이션을 여러 개 적용하려면 쉼표로 나누지만, 같은 속성을 함께 바꾸면 결합·우선순위 문제가 생길 수 있으므로 처음에는 한 개씩 검증하는 편이 이해하기 쉽습니다.
fill-mode는 지연 중과 종료 후 상태를 정한다
none은 재생 구간 밖에서 키프레임 효과를 유지하지 않고 일반 CSS를 따릅니다. forwards는 마지막으로 재생한 키프레임의 값을 종료 후에도 유지하고, backwards는 처음 재생할 키프레임의 값을 지연 구간에도 적용합니다. both는 이 두 동작을 함께 적용합니다. forwards가 언제나 100% 모양을 남기는 것은 아닙니다. 방향과 반복 횟수에 따라 마지막 상태가 달라집니다.
/* 전체 예제의 .box 규칙 안에서 단축 속성 다음에 추가해 비교 */
animation-iteration-count: 1;
animation-direction: normal;
animation-fill-mode: forwards;
위 조건에서 체크해 정방향 한 번을 끝내면 오른쪽의 큰 초록 모양이 남습니다. forwards를 none으로 바꾼 뒤 새로 고쳐 다시 재생하면 종료 후 일반 .box에 지정한 시작 위치·기본 크기·파란 배경으로 돌아옵니다. 비교 후 추가한 세 선언을 제거하면 원래의 무한 왕복 실습으로 복원됩니다.
backwards를 비교하려면 delay를 1s처럼 양수로 두고 시작 전에 보이는 상태를 관찰합니다. 예제의 일반 CSS는 scale(1)이지만 첫 키프레임은 scale(0.7)이므로 차이가 있습니다. fill-mode는 기본 CSS 선언을 영구 수정하는 기능은 아니며 animation이 제거되면 효과도 해제됩니다.
동작 줄이기와 재생 문제 확인
prefers-reduced-motion: reduce 조건에서는 예제가 animation을 제거하고 기본 모양을 표시합니다. 이 환경에서 체크해도 움직이지 않는 것은 의도된 동작입니다. 시간·방향 실습을 할 때는 본인에게 편안한 환경에서 해당 설정을 확인하세요. 실제 사이트에서도 불필요한 반복·큰 이동을 줄이고, 정지 상태에서도 내용과 조작 기능을 이해할 수 있게 만들어야 합니다.
예상대로 재생되지 않으면 키프레임 이름이 일치하는지, duration이 0s인지, play-state가 paused인지 확인합니다. 재개는 처음부터 재시작이 아니므로 이미 한 번 재생을 마친 유한 애니메이션이 체크 변경만으로 새로 시작하지 않을 수 있습니다. 실습을 처음부터 반복하려면 페이지를 새로 고칩니다. CSS가 다른 animation 선언으로 덮였는지도 계산된 스타일에서 확인하세요.
기본 구문은 MDN animation 문서와 @keyframes 문서, 재생 전후 상태와 재개 동작은 animation-fill-mode와 animation-play-state 문서에서 확인할 수 있습니다.




