Animations/Using CSS animations

김동현·2026년 3월 18일

mdn 학습 번역 - CSS

목록 보기
38/190

CSS 애니메이션 사용하기 (Using CSS animations)

안녕하세요! 프론트엔드 개발의 세계에 오신 것을 환영합니다. 오늘 저와 함께 살펴볼 내용은 웹 페이지에 생동감을 불어넣어 줄 CSS 애니메이션입니다.

CSS 애니메이션을 사용하면 CSS 스타일이 하나의 설정(상태)에서 다른 설정으로 부드럽게 전환되는 멋진 애니메이션을 만들 수 있어요. 애니메이션은 크게 두 가지 요소로 이루어집니다. 첫 번째는 애니메이션의 설정을 지정하는 'CSS 스타일'이고, 두 번째는 애니메이션의 시작과 끝 상태, 그리고 중간중간 어떻게 변해야 하는지(경유지)를 알려주는 '키프레임(keyframes) 세트'입니다.

기존에 JavaScript로 애니메이션을 구현하던 방식에 비해, CSS 애니메이션은 다음과 같은 세 가지 아주 강력한 장점이 있습니다:

  1. JavaScript 코드를 단 한 줄도 작성하지 않고, CSS 몇 줄만으로 아주 기본적인 애니메이션을 뚝딱 만들 수 있습니다.
  2. 시스템에 어느 정도 부하가 있는 상황에서도 애니메이션이 꽤 부드럽게 실행됩니다. JavaScript로 간단한 애니메이션을 만들면 종종 화면이 끊기거나 버벅거릴 수 있지만, 브라우저의 렌더링 엔진은 프레임 건너뛰기(frame-skipping) 같은 최적화 기술을 사용해서 최대한 매끄러운 성능을 유지해 준답니다.
  3. 브라우저가 애니메이션의 흐름을 직접 제어하게 놔두면, 브라우저 스스로 성능과 효율성을 똑똑하게 최적화할 수 있어요. 예를 들어, 사용자가 지금 보고 있지 않은 탭(숨겨진 탭)에서 실행 중인 애니메이션의 업데이트 주기를 줄여서 시스템 리소스를 아끼는 식이죠.

💡 강사님의 꿀팁: 실무에서도 복잡한 로직이 필요한 게 아니라면, UI의 시각적인 전환 효과는 대부분 CSS 애니메이션으로 처리하는 것이 성능 면에서 훨씬 유리합니다! JavaScript는 데이터를 다루거나 복잡한 상태를 제어하는 데 양보하고, 시각적인 요소는 CSS에 맡겨주세요.


애니메이션 설정하기 (Configuring an animation)

CSS 애니메이션 시퀀스를 만들려면, 애니메이션을 적용하고 싶은 요소에 animation 속성이나 그 하위 속성(sub-properties)들을 적용해야 합니다. 이 속성들을 통해 애니메이션이 진행되는 타이밍, 지속 시간, 그리고 여러 진행 방식에 대한 세부 사항들을 세팅할 수 있어요.

단, 여기서 주의할 점은 이 속성들이 애니메이션의 실제 시각적인 모습(어떤 색으로 변할지, 얼마나 이동할지 등)을 설정하는 것은 아니라는 점입니다. 실제 외형의 변화는 아래 키프레임을 사용하여 애니메이션 시퀀스 정의하기 섹션에서 배울 @keyframes 앳룰(@-rule)을 통해 작성한답니다.

animation 속성의 하위 속성들은 다음과 같습니다:

  • animation-composition
    여러 개의 애니메이션이 동시에 같은 속성에 영향을 줄 때, 이를 어떻게 섞을지 합성 연산(composite operation) 방식을 지정합니다. (참고로 이 속성은 animation 단축 속성에 포함되지 않습니다.)
  • animation-delay
    요소가 로드된 후 애니메이션이 실제로 시작될 때까지의 지연(대기) 시간을 지정합니다. 애니메이션이 처음부터 시작할지, 아니면 일정 시간 지난 중간 지점부터 시작할지도 결정할 수 있습니다.
  • animation-direction
    애니메이션이 처음 실행될 때 앞으로 재생할지, 뒤로 재생할지 지정합니다. 또한 반복 재생될 때마다 방향을 번갈아 바꿀지(예: 1회차는 앞으로, 2회차는 뒤로), 아니면 매번 시작점으로 돌아가서 똑같이 반복할지 설정합니다.
  • animation-duration
    애니메이션이 한 사이클을 완전히 끝내는 데 걸리는 시간을 지정합니다.
  • animation-fill-mode
    애니메이션이 실행되기 전과 실행이 끝난 후에 해당 요소에 스타일을 어떻게 적용해 둘지 지정합니다.

    참고: 애니메이션을 forwards 모드로 채우게 되면, 애니메이션 된 속성들은 마치 will-change 속성에 값이 포함된 것처럼 동작합니다. 만약 애니메이션 중에 새로운 쌓임 문맥(stacking context)이 만들어졌다면, 대상 요소는 애니메이션이 완전히 끝난 후에도 그 쌓임 문맥을 계속 유지하게 됩니다.

  • animation-iteration-count
    애니메이션을 총 몇 번 반복할지 횟수를 지정합니다.
  • animation-name
    애니메이션의 키프레임들을 정의해 둔 @keyframes 앳룰의 이름을 지정합니다.
  • animation-play-state
    애니메이션 시퀀스를 일시 정지할지, 다시 재생할지 지정합니다. 마우스 호버(hover) 시 애니메이션을 멈추게 할 때 유용해요.
  • animation-timeline
    CSS 애니메이션의 진행률을 제어하는 타임라인을 지정합니다. (예: 스크롤 기반 애니메이션 등)
  • animation-timing-function
    가속도 곡선(acceleration curves)을 설정해서, 애니메이션이 키프레임 사이를 넘어갈 때 속도가 어떻게 변할지 지정합니다. (예: 처음엔 느리게 가다가 중간에 빨라지고 끝에 다시 느려지게)

💡 강사님의 꿀팁: 현업에서 특히 animation-fill-mode: forwards는 무조건 외워두셔야 합니다! 팝업창이 서서히 나타나는 애니메이션을 만들었는데 끝나고 나서 다시 팝업이 휙! 사라져 버린다면, 대부분 이 속성을 forwards로 주지 않아서 그래요. 끝난 상태를 그대로 유지하라는 뜻이랍니다. 그리고 타이밍 함수(timing-function)를 잘 활용하면 정말 생동감 넘치는 찰진 움직임을 만들 수 있어요!


키프레임을 사용하여 애니메이션 시퀀스 정의하기 (Defining an animation sequence using keyframes)

애니메이션의 타이밍과 진행 방식을 다 설정했다면, 이제 애니메이션의 뼈대이자 외형을 정의해야 합니다. 이는 @keyframes 앳룰을 사용해서 하나 이상의 '키프레임(keyframe)'을 만들어주면 된답니다. 각각의 키프레임은 애니메이션이 진행되는 과정 중 특정 시점에 요소가 어떻게 렌더링 되어야 하는지를 설명해 줍니다.

애니메이션의 전체 소요 시간은 위에서 설명한 CSS 스타일(animation-duration 등)에서 이미 지정했기 때문에, 키프레임 내부에서는 <percentage>(퍼센트)를 사용해서 애니메이션 전체 시퀀스 중 어느 시점인지를 나타냅니다.

  • 0%는 애니메이션 시퀀스의 맨 처음 시작 순간을 나타냅니다.
  • 100%는 애니메이션의 맨 마지막 최종 상태를 나타냅니다.

이 두 시점은 너무나도 중요하기 때문에 특별한 별칭(alias)을 가지고 있어요. 바로 from(0%와 동일)과 to(100%와 동일)입니다. 이 두 값은 선택 사항(optional)이에요. 만약 from이나 0%, 또는 to100%를 따로 지정하지 않으면 브라우저는 해당 요소가 원래 가지고 있던 계산된 속성 값들을 시작점이나 끝점으로 알아서 설정해 줍니다.

물론 시작과 끝 사이의 중간 단계를 표현하고 싶다면 0%부터 100% 사이의 퍼센트 값을 가진 추가 키프레임들을 원하는 만큼 얼마든지 넣으실 수 있어요.

💡 강사님의 꿀팁: 저는 개인적으로 from, to보다는 0%, 100%로 작성하는 습관을 들이시는 걸 추천해요. 처음엔 시작과 끝만 있다가도 나중에 기획이 바뀌어서 50% 지점에 튕기는 효과를 넣어달라고 할 수도 있거든요! 숫자로 통일해두면 유지보수하기 훨씬 편해집니다.


animation 단축 속성 사용하기 (Using the animation shorthand)

animation 단축 속성(shorthand)을 사용하면 코드를 길게 나열할 필요 없이 아주 깔끔하게 줄일 수 있어서 유용합니다. 예를 들어, 이 글을 읽으며 배운 여러 속성들을 이렇게 길게 쓸 수 있죠:

p {
  animation-duration: 3s;
  animation-name: slide-in;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}

이 코드는 animation 단축 속성을 써서 아래처럼 딱 한 줄로 줄일 수 있습니다!

p {
  animation: 3s infinite alternate slide-in;
}

이 단축 속성을 사용할 때 각 애니메이션 속성 값들을 어떤 순서로 적어야 하는지 자세히 알고 싶으시다면 animation 레퍼런스 페이지를 확인해 보세요.

💡 강사님의 꿀팁: 단축 속성에서 정말 많은 분이 헷갈려 하는 부분이 바로 '시간'을 나타내는 값의 순서입니다. 예를 들어 animation: 2s 1s slide-in; 처럼 시간이 두 번 나오면, 항상 첫 번째 시간 값이 duration(재생 시간)이고, 두 번째 시간 값이 delay(지연 시간)로 인식됩니다! 이것만 기억하셔도 큰 실수를 줄일 수 있어요.


다중 애니메이션 속성 값 설정하기 (Setting multiple animation property values)

CSS 애니메이션의 각 개별(longhand) 속성들은 쉼표(,)로 구분해서 여러 개의 값을 받을 수 있습니다. 이 기능은 하나의 요소에 하나의 CSS 규칙으로 여러 개의 애니메이션을 동시에 적용하고 싶을 때 사용해요. 각각의 애니메이션마다 지속 시간, 반복 횟수 등을 다 다르게 설정할 수 있거든요. 어떻게 매칭되는지 몇 가지 간단한 예시를 통해 설명해 드릴게요.

첫 번째 예시에서는 지속 시간(duration)이 3개, 반복 횟수(iteration-count)가 3개 주어졌습니다. 각각의 값은 animation-name에 나열된 순서와 동일한 위치의 값과 짝이 지어집니다.
따라서 fadeInOut 애니메이션은 2.5s의 시간과 2번의 반복 횟수를 가지고, bounce 애니메이션은 1s의 시간과 5번의 반복 횟수를 갖게 됩니다.

animation-name: fadeInOut, moveLeft300px, bounce;
animation-duration: 2.5s, 5s, 1s;
animation-iteration-count: 2, 1, 5;

두 번째 예시에서는 애니메이션 이름은 3개가 설정되었는데, 지속 시간과 반복 횟수는 딱 1개씩만 적혀 있네요. 이 경우에는 세 개의 애니메이션 모두가 동일하게 하나의 지속 시간(3s)과 반복 횟수(1)를 나눠 쓰게 됩니다.

animation-name: fadeInOut, moveLeft300px, bounce;
animation-duration: 3s;
animation-iteration-count: 1;

세 번째 예시를 볼까요? 애니메이션 이름은 3개인데, 지속 시간과 반복 횟수는 2개뿐입니다. 이렇게 리스트의 값 개수가 부족해서 각각의 애니메이션에 하나씩 매칭해 줄 수 없는 상황이 오면, 브라우저는 리스트의 첫 번째 값부터 마지막 값까지 순서대로 할당하다가 값이 끝나면 다시 첫 번째 값으로 돌아가서 재사용(cycle)합니다.

즉, fadeInOut은 첫 번째 값인 2.5s를 받고, moveLeft300px은 두 번째 값이자 마지막 값인 5s를 받습니다. 이제 지속 시간 값이 떨어졌죠? 그러면 다시 처음으로 돌아가서, 세 번째 애니메이션인 bounce는 다시 첫 번째 값인 2.5s를 받게 되는 식입니다. 반복 횟수 값이나 그 외 다른 속성 값들도 이와 똑같은 방식으로 할당됩니다.

animation-name: fadeInOut, moveLeft300px, bounce;
animation-duration: 2.5s, 5s;
animation-iteration-count: 2, 1;

만약 반대로 애니메이션 이름보다 값이 더 많다면 어떻게 될까요? 예를 들어 애니메이션 이름은 3개인데 animation-duration 값이 5개나 있다면, 남아도는 여분의 두 animation-duration 값은 어떤 애니메이션에도 적용되지 않고 그냥 무시됩니다.


예제 (Examples)

이제부터 배운 내용을 바탕으로 여러 가지 예제를 직접 만들어볼까요?

브라우저 창을 가로지르는 텍스트 슬라이드 만들기 (Making text slide across the browser window)

가장 기본적인 이 예제는 translatescale 속성을 활용해서, <p> 요소의 텍스트가 브라우저 창 오른쪽 바깥에서 안쪽으로 스르륵 미끄러져 들어오게 만드는 스타일입니다.

Play (Run example in MDN Playground)

p {
  animation-duration: 3s;
  animation-name: slide-in;
}

@keyframes slide-in {
  from {
    translate: 150vw 0;
    scale: 200% 1;
  }

  to {
    translate: 0 0;
    scale: 100% 1;
  }
}

이 예제에서 <p> 요소의 스타일을 보면, animation-duration 속성을 사용해서 애니메이션이 시작부터 끝까지 총 3초(3s) 동안 실행되도록 지정했습니다. 그리고 이 애니메이션 시퀀스의 키프레임을 정의한 @keyframes 앳룰의 이름이 slide-in이라고 알려주고 있죠.

키프레임은 딱 두 개를 만들었습니다. 첫 번째 키프레임은 0% 지점(from 별칭 사용)입니다. 여기서 요소의 translate150vw로 설정했어요 (즉, 텍스트가 컨테이닝 블록의 오른쪽 경계를 훨씬 넘어간 화면 바깥에 위치합니다). 동시에 scale200%로 주어서 글자 폭이 기본보다 두 배로 넓어지게 만들었죠. 이렇게 하면 애니메이션의 첫 프레임에서 문단이 브라우저 창 오른쪽 바깥으로 벗어난 상태로 시작하게 됩니다.

두 번째 키프레임은 100% 지점(to 별칭 사용)입니다. translate0%로 돌아오고 요소의 scale도 기본 크기인 1(100%)로 돌아옵니다. 그 결과, 헤더 텍스트가 원래 있어야 할 제자리(콘텐츠 영역 왼쪽)에 예쁘게 안착하면서 애니메이션이 끝나게 됩니다.

Play (Run example in MDN Playground)

참고: 페이지를 새로고침하면 애니메이션이 실행되는 걸 확인하실 수 있습니다.

또 다른 키프레임 애니메이션 추가하기 (Adding another keyframe animation)

방금 만든 애니메이션에 키프레임을 하나 더 추가해 보겠습니다. 이번엔 문단 속 'Alice'라는 이름만 콕 집어서, 오른쪽에서 왼쪽으로 미끄러져 들어올 때 글씨 색이 분홍색으로 변하면서 커졌다가 원래 크기와 색상으로 돌아오게 만들고 싶어요.

이때 font-size를 변경해서 글씨를 키울 수도 있겠지만, 폰트 사이즈처럼 박스 모델(box model) 자체를 건드리는 속성들을 애니메이션 하면 성능이 심각하게 저하될 수 있어요(리플로우 발생). 그 대신 앨리스의 이름만 태그로 감싼 뒤, 해당 <span>에 따로 스케일(scale)과 색상을 부여하는 방식을 써보겠습니다. 이렇게 하려면 <span>에만 영향을 주는 두 번째 애니메이션을 만들어야겠죠?

Play (Run example in MDN Playground)

@keyframes grow-shrink {
  25%,
  75% {
    scale: 100%;
  }

  50% {
    scale: 200%;
    color: magenta;
  }
}

전체 코드는 이제 이렇게 됩니다:

Play (Run example in MDN Playground)

p {
  animation-duration: 3s;
  animation-name: slide-in;
}
p span {
  display: inline-block;
  animation-duration: 3s;
  animation-name: grow-shrink;
}

@keyframes slide-in {
  from {
    translate: 150vw 0;
    scale: 200% 1;
  }

  to {
    translate: 0 0;
    scale: 100% 1;
  }
}

@keyframes grow-shrink {
  25%,
  75% {
    scale: 100%;
  }

  50% {
    scale: 200%;
    color: magenta;
  }
}

그리고 HTML에서는 "Alice" 양옆에 을 씌워줍니다:

Play (Run example in MDN Playground)

<p>
  애벌레와 <span>Alice</span>는 한동안 말없이 서로를 쳐다보았습니다: 마침내 애벌레가 입에서 
  물담배를 빼고는 나른하고 졸린 목소리로 그녀에게 말을 걸었습니다.
</p>

이 코드는 브라우저에게 "처음 25%와 마지막 25%(75%~100%) 동안에는 이름이 정상 크기(100%)로 보이다가, 중간 지점인 50%일 때는 크기가 커지고(200%) 색깔도 마젠타(magenta)로 변하게 해 줘!"라고 명령하는 것입니다. 참고로 span 태그의 display 속성을 inline-block으로 바꾼 걸 볼 수 있는데, 이건 매우 중요한 포인트예요! transform 계열의 속성(scale, translate 등)들은 inline 요소(비치환 인라인 요소)에는 먹히지 않기 때문에 꼭 바꿔주어야 합니다.

참고: 페이지를 새로고침하면 애니메이션이 어떻게 변했는지 확인하실 수 있습니다.

애니메이션 반복하기 (Repeating the animation)

한 번 끝나고 마는 게 아쉬운가요? 애니메이션을 계속해서 스스로 반복하게 만들려면, animation-iteration-count 속성에 반복할 횟수를 지정하면 됩니다. 끝없이 영원히 움직이게 하고 싶다면 infinite 값을 쿨하게 넣어주세요!

Play (Run example in MDN Playground)

p {
  animation-duration: 3s;
  animation-name: slide-in;
  animation-iteration-count: infinite;
}

애니메이션이 왔다 갔다 하게 만들기 (Making the animation move back and forth)

이제 애니메이션이 무한 반복되긴 하는데, 매번 시작할 때마다 글자가 오른쪽 끝으로 휙! 점프해서 다시 나타나니까 좀 부자연스럽죠? 화면의 양쪽을 탁구공처럼 자연스럽게 왔다 갔다(back and forth)하게 만들고 싶으실 거예요. 아주 간단합니다! animation-directionalternate로 설정해 주면 끝이에요.

Play (Run example in MDN Playground)

p {
  animation-duration: 3s;
  animation-name: slide-in;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}

애니메이션 이벤트 사용하기 (Using animation events)

애니메이션이 시작되고, 중간에 반복되고, 끝나는 각각의 시점을 우리가 포착할 수 있다면 어떨까요? JavaScript의 애니메이션 이벤트(animation events)를 활용하면 애니메이션에 대한 추가적인 제어 권한과 유용한 정보들을 얻을 수 있습니다.

AnimationEvent 객체로 대표되는 이 이벤트들은 언제 애니메이션이 시작(start)하고, 끝(finish)나고, 새 사이클을 시작(begin a new iteration)하는지 정확히 감지해 냅니다. 게다가 각 이벤트 객체 안에는 이벤트가 발생한 정확한 시간(time)과 이벤트를 발생시킨 애니메이션의 이름(name) 정보까지 꾹꾹 담겨 있어요.

이 이벤트들이 실제로 어떻게 동작하는지 엿보기 위해서, 아까 만든 텍스트 슬라이드 예제에 JavaScript를 얹어 애니메이션 이벤트 정보들을 화면에 출력해 보겠습니다.

CSS에는 앞서 만든 것과 동일한 키프레임 애니메이션을 준비합니다. 이 애니메이션("slide-in")은 3초 동안 진행되고, 총 3번 반복하며, 매번 번갈아 가면서(alternate) 방향을 바꿀 거예요.

Play (Run example in MDN Playground)

.slide-in {
  animation-duration: 3s;
  animation-name: slide-in;
  animation-iteration-count: 3;
  animation-direction: alternate;
}

애니메이션 이벤트 리스너 달아주기 (Adding the animation event listeners)

JavaScript 코드를 통해 애니메이션과 관련된 세 가지 이벤트(animationstart, animationend, animationiteration)를 모두 귀 기울여 듣게(listen) 만들 겁니다. 문서가 처음 로드될 때 이 코드를 호출해서 세팅을 마칩니다.

Play (Run example in MDN Playground)

const element = document.getElementById("watch-me");
element.addEventListener("animationstart", listener);
element.addEventListener("animationend", listener);
element.addEventListener("animationiteration", listener);

element.className = "slide-in";

코드가 아주 기본적이죠? 이벤트 등록에 대한 자세한 내용은 eventTarget.addEventListener() 문서를 참고하시면 됩니다. 이 코드의 마지막 줄을 보시면, 이벤트 리스너를 다 등록한 후에야 요소의 class를 "slide-in"으로 바꿔주고 있어요. 바로 이 순간이 애니메이션이 실제로 시작되도록 방아쇠를 당기는(start) 시점입니다.

왜 이렇게 했을까요? 만약 HTML에 처음부터 클래스를 달아두었다면, 우리 JavaScript 코드가 실행되기도 전에 이미 브라우저가 애니메이션을 시작해 버려서 animationstart 이벤트를 영영 놓치고 말았을 테니까요! 그래서 리스너를 먼저 쫙 깔아두고 우리가 원하는 타이밍에 클래스를 추가해 준 것입니다.

이벤트 받아오기 (Receiving the events)

이벤트가 발생하면 아까 등록해 둔 listener() 함수로 이벤트 정보가 쏙 전달됩니다. 코드는 아래와 같습니다.

Play (Run example in MDN Playground)

function listener(event) {
  const l = document.createElement("li");
  switch (event.type) {
    case "animationstart":
      l.textContent = `Started: elapsed time is ${event.elapsedTime}`;
      break;
    case "animationend":
      l.textContent = `Ended: elapsed time is ${event.elapsedTime}`;
      break;
    case "animationiteration":
      l.textContent = `New loop started at time ${event.elapsedTime}`;
      break;
  }
  document.getElementById("output").appendChild(l);
}

이 코드도 아주 명확하죠. 건네받은 event.type을 살펴보고 "시작했네? 끝났네? 반복하네?"를 파악한 다음, 로그를 보여줄 <ul>(순서 없는 목록)에 메시지가 담긴 리스트 아이템을 착착 붙여주는 역할입니다.

결과적으로 출력을 보면 대략 이런 모습일 거예요:

  • Started: elapsed time is 0
  • New loop started at time 3.01200008392334
  • New loop started at time 6.00600004196167
  • Ended: elapsed time is 9.234000205993652

시간(time)을 잘 보시면, 우리가 CSS에서 딱 3초(3s)로 설정해 두었지만 실제로는 소수점 아래로 미세하게 오차가 있는 걸 알 수 있습니다. 이는 시스템 성능에 따라 약간의 차이가 생길 수 있다는 의미예요. 그리고 또 한 가지! 3번째 반복을 모두 마치고 애니메이션이 끝날 때는 animationiteration 이벤트 대신 animationend 이벤트가 딱 한 번 발생하며 대장정을 마무리합니다.

완성을 위해 텍스트 로그를 출력할 <ul> 리스트가 포함된 HTML 마크업도 보여드릴게요:

Play (Run example in MDN Playground)

<h1 id="watch-me">Watch me move</h1>
<p>
  이 예제는 CSS 애니메이션을 사용하여 <code>H1</code> 요소가 페이지를 
  가로질러 이동하게 만드는 방법을 보여줍니다.
</p>
<p>
  게다가 애니메이션 이벤트가 실행될 때마다 텍스트가 출력되므로 
  이벤트가 언제 일어나는지 눈으로 직접 볼 수 있죠.
</p>
<ul id="output"></ul>

참고: 페이지를 새로고침하면 애니메이션을 볼 수 있습니다.

display 및 content-visibility 애니메이션 처리하기 (Animating display and content-visibility)

마지막 팁입니다! 이 예제는 display 속성과 content-visibility 속성도 애니메이션으로 처리할 수 있다는 걸 보여줍니다. "어? display: none은 애니메이션이 안 먹히는 거 아니었나요?" 하셨을 텐데, 요즘 브라우저들은 꽤 발전했거든요! 요소를 DOM에서 숨길 때(display: none), 바로 뚝 끊어지며 사라지는 게 아니라 opacity(투명도)를 이용해서 스르륵 부드럽게 페이드 아웃(fade out) 되도록 만드는 진입/퇴장(entry/exit) 애니메이션에 아주 유용하게 쓰입니다.

이 기능을 지원하는 브라우저들은 displaycontent-visibility불연속 애니메이션(discrete animation type)의 변형된 방식으로 처리합니다. 불연속 애니메이션이란, 0에서 1로 스르륵 변하는 게 아니라, 애니메이션 진행률 딱 50% 지점에서 값이 A에서 B로 휙! 하고 뒤집히는(flip) 걸 말해요.

하지만, 예외가 하나 있습니다. 바로 display: none이나 content-visibility: hidden 상태에서 눈에 보이는(visible) 상태로 애니메이션 할 때, 혹은 그 반대의 경우예요. 이때 브라우저는 애니메이션 지속 시간 내내 텍스트나 요소가 화면에 '보이도록' 알아서 값을 영리하게 뒤집어줍니다.

구체적으로 예를 들어 볼게요:

  • displaynone에서 block(또는 눈에 보이는 값)으로 애니메이션 할 때는, 애니메이션 지속 시간의 0%(시작 즉시) 지점에 값이 block으로 휙 바뀝니다. 그래서 애니메이션이 진행되는 내내 내용이 눈에 보입니다.
  • 반대로 displayblock에서 none으로 숨길 때는, 애니메이션 지속 시간의 마지막인 100%(끝날 때) 지점에 값이 none으로 바뀝니다. 덕분에 서서히 투명해지면서 페이드 아웃 되는 시간 내내 내용이 화면에 잘 보일 수 있는 것이죠.

HTML

HTML에는 두 개의 <p> 태그가 있고 그 사이에 우리가 display: none에서 block으로 애니메이션 할 <div>가 숨어있습니다.

Play (Run example in MDN Playground)

<p>
  화면 아무 곳이나 클릭하거나 아무 키나 눌러서 <code>&lt;div&gt;</code>를 숨김(hidden)과 표시(showing) 상태로 전환해보세요.
</p>

<div><code>&lt;div&gt;</code> 요소는 <code>display: none; opacity: 0</code><code>display: block; opacity: 1</code> 사이를 자연스럽게 애니메이션합니다. 참 깔끔하죠?
</div>

<p>
  위의 <code>&lt;div&gt;</code> 요소에 <code>display: none;</code>이 제대로 
  적용되었다가 지워지는지 보여주기 위한 단락입니다. 만약 <code>opacity</code>만 
  바꾸었다면, 안 보일 때도 DOM 상에 텅 빈 공간을 차지하고 있었을 테니까요!
</p>

CSS

Play (Run example in MDN Playground)

html {
  height: 100vh;
}

div {
  font-size: 1.6rem;
  padding: 20px;
  border: 3px solid red;
  border-radius: 20px;
  width: 480px;
  opacity: 0;
  display: none;
}

/* Animation classes */

div.fade-in {
  display: block;
  animation: fade-in 0.7s ease-in forwards;
}

div.fade-out {
  animation: fade-out 0.7s ease-out forwards;
}

/* Animation keyframes */

@keyframes fade-in {
  0% {
    opacity: 0;
    display: none;
  }

  100% {
    opacity: 1;
    display: block;
  }
}

@keyframes fade-out {
  0% {
    opacity: 1;
    display: block;
  }

  100% {
    opacity: 0;
    display: none;
  }
}

가장 핵심은 키프레임 애니메이션 안에 display 속성을 함께 넣어준 것입니다! 옛날 같았으면 상상도 못 했을 방식이죠.

JavaScript

마지막으로, 애니메이션을 제어할 이벤트 리스너를 달아주는 작은 JavaScript 코드를 추가합니다. 사용자가 화면을 클릭하거나 키보드를 누르면, <div>가 나타나길 원할 땐 fade-in 클래스를 붙여주고, 사라지게 하고 싶을 땐 fade-out 클래스로 바꿔치기하는 식입니다.

Play (Run example in MDN Playground)

const divElem = document.querySelector("div");
const htmlElem = document.querySelector(":root");

htmlElem.addEventListener("click", showHide);
document.addEventListener("keydown", showHide);

function showHide() {
  if (divElem.classList[0] === "fade-in") {
    divElem.classList.remove("fade-in");
    divElem.classList.add("fade-out");
  } else {
    divElem.classList.remove("fade-out");
    divElem.classList.add("fade-in");
  }
}

결과 (Result)

이 코드가 완성되면 화면에서 클릭할 때마다 뚝 끊기지 않고 부드럽게 요소가 나타났다가, 다시 서서히 투명해지며 완전히 DOM 영역에서 사라지는 우아한 트랜지션을 볼 수 있습니다.


참고 자료 (See also)

더 깊이 파고들고 싶은 개발자분들은 아래 문서들을 곁들여 읽어보세요:


MDN 개선에 도움을 주세요 (Help improve MDN)

이 문서가 학습에 도움이 되셨나요? (Was this page helpful to you?)
[Yes][No]

문서 기여 방법 알아보기: Learn how to contribute
최종 수정일: 2025년 12월 15일 (MDN contributors 작성)
이 문서를 GitHub에서 보기 | 문서의 문제점 신고하기

profile
프론트에_가까운_풀스택_개발자

0개의 댓글