이번 편의 결과물: index.html을 열면 히어로 제목·부제·버튼 2개가 위에서 아래 순서로 살짝 떠오르며 나타납니다. 다루는 개념: @keyframes 정의, animation 속성군(지속시간·반복·재생 상태), transition과의 차이(트리거 유무)
이 편에서 만드는 파일
web-practice/portfolio/
└── css/
└── styles.css ~ (@keyframes hero-enter 신규, .hero 자식 요소에 animation 추가)17편까지 .hero는 배경·여백·글자 크기만 정리된 정적인 상태입니다. 이 편은 페이지가 열리는 순간 한 번만 실행되는 애니메이션을 추가합니다.
개념 정리
@keyframes는 애니메이션의 중간 단계를 이름 붙여 정의하는 규칙입니다. from/to(또는 0%/100%)로 시작과 끝을, 필요하면 그 사이 퍼센트로 중간 단계를 지정합니다.
@keyframes hero-enter {
from {
opacity: 0;
transform: translateY(12px);
}
to {
opacity: 1;
transform: translateY(0);
}
}정의한 @keyframes는 animation 속성으로 요소에 연결합니다.
| 하위 속성 | 역할 | 이 사이트에서 쓰는 값 |
|---|---|---|
animation-name | 사용할 @keyframes 이름 | hero-enter |
animation-duration | 한 번 재생에 걸리는 시간 | 600ms |
animation-timing-function | 속도 곡선 | ease-out |
animation-delay | 재생 시작을 늦추는 시간 | 요소마다 0ms–400ms |
animation-fill-mode | 재생 전·후 상태 유지 방식 | both(끝난 뒤 마지막 프레임 유지) |
transition과 animation의 차이는 트리거입니다. transition은 :hover처럼 속성 값이 바뀌는 사건이 있어야 동작하고, 시작·끝 두 상태만 표현합니다. animation은 @keyframes로 여러 단계를 정의할 수 있고, 트리거 없이 요소가 화면에 나타나는 순간(또는 클래스가 붙는 순간) 스스로 재생을 시작합니다. 히어로 등장 연출처럼 “페이지가 열리자마자 자동으로” 실행돼야 하는 효과는 animation이 맞는 선택입니다.
흔한 오해는 animation-fill-mode를 생략하는 것입니다. 기본값(none)에서는 애니메이션이 끝나는 순간 요소가 @keyframes에 정의되지 않은 원래 스타일로 돌아가 버려, opacity: 0으로 시작한 요소가 재생 후 다시 안 보이는 것처럼 깜빡일 수 있습니다.
실습
1. 파일 열기
web-practice/portfolio/css/styles.css를 엽니다. .hero 규칙 아래에 이어서 씁니다.
2. @keyframes 정의
/* web-practice/portfolio/css/styles.css */
/* ...기존 리셋·토큰·레이아웃·헤더·버튼·카드 규칙 유지... */
@keyframes hero-enter {
from {
opacity: 0;
transform: translateY(12px);
}
to {
opacity: 1;
transform: translateY(0);
}
}3. 히어로 자식 요소에 순서대로 적용
index.html의 히어로 영역은 h1 → p → 버튼(기본) → 버튼(보조) 순서로 되어 있습니다. :nth-child로 순서를 골라 지연 시간을 다르게 줍니다.
.hero > * {
animation: hero-enter 600ms ease-out both;
}
.hero > *:nth-child(2) {
animation-delay: 150ms;
}
.hero > *:nth-child(3) {
animation-delay: 300ms;
}
.hero > *:nth-child(4) {
animation-delay: 400ms;
}첫 번째 규칙이 네 요소 모두에게 같은 @keyframes와 지속시간을 주고, 이후 세 규칙은 지연 시간만 덮어써 시작 시점을 어긋나게 만듭니다. 이렇게 하면 요소가 동시에 뜨지 않고 위에서부터 순서대로 나타납니다.
4. 사용자 접근성 설정 존중
일부 사용자는 운영체제에서 “동작 줄이기”를 켜 둡니다. 이 설정을 존중하는 규칙을 추가합니다.
@media (prefers-reduced-motion: reduce) {
.hero > * {
animation: none;
}
}5. 실행
파일을 저장하고 index.html을 새로고침합니다. 캐시 때문에 애니메이션이 안 보이면 강력 새로고침(Ctrl + Shift + R)을 합니다.
6. 확인
- 페이지를 열면 히어로 제목이 먼저, 부제가 그다음, 버튼 두 개가 순서대로 아래에서 살짝 떠오르며 나타납니다.
- 전체 연출은 1초 안팎으로 끝나고, 끝난 뒤에는 요소들이 제자리에 고정되어 다시 사라지지 않습니다.
- 개발자 도구에서 운영체제의 “동작 줄이기”(macOS: 손쉬운 사용 > 디스플레이, Windows: 접근성 설정)를 켜고 새로고침하면 애니메이션 없이 바로 나타납니다.
projects.html·contact.html은 히어로 영역이 없으므로 이 애니메이션의 영향을 받지 않습니다.
직접 해보기
animation-delay값을 모두0ms로 바꿔 네 요소가 동시에 나타나는 것과 순서대로 나타나는 것의 느낌 차이를 비교해 보세요.@keyframes hero-enter에50%단계를 추가해 중간에 살짝 더 위로 올라갔다가 제자리로 내려오는 3단계 애니메이션을 만들어 보세요.
정답 확인
- 동시에 나타나면 “화면이 한 번에 켜지는” 느낌이고, 순서대로면 “요소가 하나씩 소개되는” 느낌입니다. 시안은 후자를 의도합니다.
- 예시:
@keyframes hero-enter {
0% {
opacity: 0;
transform: translateY(12px);
}
60% {
opacity: 1;
transform: translateY(-2px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}자주 하는 실수
| 증상 | 원인 | 고치는 법 |
|---|---|---|
| 애니메이션이 끝난 뒤 요소가 안 보인다 | animation-fill-mode를 생략해 재생 후 원래(opacity: 0) 스타일로 돌아갔다 | animation-fill-mode: both 또는 단축 속성에 both 포함 |
| 새로고침해도 애니메이션이 매번 안 보인다 | 브라우저 캐시가 이전 CSS를 그대로 쓰고 있다 | 강력 새로고침 또는 개발자 도구를 열어 두고 캐시 비활성화 옵션 사용 |
| 모든 요소가 동시에 나타난다 | animation-delay를 개별 요소에 다르게 주지 않았다 | :nth-child로 요소를 구분해 지연 시간을 다르게 지정 |
| 동작 줄이기 설정을 켜도 계속 움직인다 | prefers-reduced-motion 미디어 쿼리를 추가하지 않았다 | @media (prefers-reduced-motion: reduce)에서 animation: none 지정 |