icon
4장 : CSS 고급 기법

CSS 애니메이션과 트랜지션


3장까지 우리는 HTML로 웹 페이지의 구조를 잡고, CSS로 스타일과 레이아웃을 꾸미는 방법을 익혔습니다. 이제 여러분의 웹 페이지에 생명을 불어넣을 차례입니다. 단순히 정적인 정보를 보여주는 것을 넘어, 요소들이 부드럽게 움직이고 변화하며 사용자에게 시각적인 피드백을 제공한다면, 웹 페이지는 훨씬 더 매력적이고 사용자 친화적으로 변할 것입니다.

이번 장에서는 CSS만으로 웹 요소에 동적인 움직임을 부여하는 두 가지 강력한 기법인 CSS 트랜지션(Transition)CSS 애니메이션(Animation) 에 대해 학습합니다. 트랜지션은 속성값이 변할 때 부드러운 전환 효과를, 애니메이션은 여러 단계의 복잡한 움직임을 제어할 수 있게 해줍니다. 이들을 통해 웹 페이지의 사용자 경험(UX)을 크게 향상시키고, 시각적인 즐거움을 더할 수 있습니다.


CSS 트랜지션 (Transition)

트랜지션은 CSS 속성값이 변할 때, 그 변화가 즉시 일어나는 대신 지정된 시간 동안 점진적으로(부드럽게) 일어나도록 하는 기능입니다. 주로 사용자의 마우스 오버(:hover), 클릭(:active), 포커스(:focus) 등 특정 이벤트에 반응하여 요소의 상태가 변할 때 유용하게 사용됩니다.

트랜지션 관련 주요 속성

트랜지션을 적용하려면 주로 다음 네 가지 속성을 사용합니다.

transition-property: 트랜지션 효과를 적용할 CSS 속성을 지정합니다.

  • all (기본값): 모든 CSS 속성에 트랜지션 적용
  • width, height, background-color, transform, opacity 등 특정 속성 지정
  • 여러 속성 지정 시 쉼표(,)로 구분
.box {
    transition-property: width; /* 너비 변화에만 트랜지션 적용 */
    transition-property: background-color, transform; /* 배경색과 변형에 트랜지션 적용 */
    transition-property: all; /* 모든 속성 변화에 트랜지션 적용 */
}

transition-duration: 트랜지션이 완료되는 데 걸리는 시간을 지정합니다.

  • s (초) 또는 ms (밀리초) 단위를 사용합니다.
  • 0초는 기본값으로, 트랜지션 없이 즉시 변화합니다.
.box {
    transition-duration: 0.5s; /* 0.5초 동안 변화 */
    transition-duration: 500ms; /* 500밀리초 동안 변화 (0.5초와 동일) */
}

transition-timing-function: 트랜지션 속도의 변화 패턴을 지정합니다. (가속도 곡선)

  • ease (기본값): 느리게 시작 → 빠르게 → 느리게 끝남 (부드러운 효과)
  • linear: 일정한 속도
  • ease-in: 느리게 시작 → 점차 빨라짐
  • ease-out: 빠르게 시작 → 점차 느려짐
  • ease-in-out: 느리게 시작 → 빨라짐 → 느리게 끝남 (ease와 유사하나 시작/끝이 더 극적)
  • cubic-bezier(n, n, n, n): 자신만의 커스텀 가속도 곡선을 정의 (고급)
  • steps(n, direction): n개의 단계로 뚝뚝 끊어지는 변화 (애니메이션에 유용)
.box {
    transition-timing-function: ease-in-out; /* 시작과 끝이 부드러운 변화 */
}

transition-delay: 트랜지션이 시작되기 전까지 기다릴 시간을 지정합니다.

  • s (초) 또는 ms (밀리초) 단위를 사용합니다.
.box {
    transition-delay: 0.2s; /* 0.2초 후에 트랜지션 시작 */
}

transition 단축 속성

위 네 가지 속성을 한 줄에 요약하여 작성할 수 있습니다. 순서는 중요하지 않지만, 시간을 나타내는 값이 두 개일 경우 첫 번째는 duration, 두 번째는 delay로 인식됩니다.

.box {
    /* property | duration | timing-function | delay */
    transition: all 0.5s ease-in-out 0.1s;

    /* 배경색 변화에 0.3초 ease 효과 */
    transition: background-color 0.3s ease;

    /* 너비 변화에 0.5초, 배경색 변화에 0.3초 */
    transition: width 0.5s, background-color 0.3s;
}

트랜지션 활용 예시

간단한 버튼에 마우스 오버 효과를 적용해 봅시다.

<style>
    .my-button {
        background-color: #3498db;
        color: white;
        padding: 10px 20px;
        border: none;
        border-radius: 5px;
        cursor: pointer;
        font-size: 16px;

        /* 트랜지션 적용 */
        transition: background-color 0.3s ease-out, transform 0.3s ease-out;
    }

    .my-button:hover {
        background-color: #2980b9; /* 배경색 변경 */
        transform: translateY(-3px); /* 위로 살짝 이동 */
        box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); /* 그림자 추가 */
    }
</style>

<button class="my-button">마우스를 올려보세요</button>

CSS 애니메이션 (Animation)

트랜지션이 '속성 변화에 대한 부드러운 전환'이라면, 애니메이션은 '여러 단계에 걸쳐 다양한 속성을 원하는 시간 동안 반복적으로 제어'할 수 있는 훨씬 강력한 기능입니다. @keyframes 규칙을 사용하여 애니메이션의 각 단계를 직접 정의합니다.

@keyframes 규칙 정의

애니메이션의 이름과 각 단계(프레임)에서의 CSS 스타일을 정의합니다. 각 단계는 퍼센트(%)로 지정하며, 0% (또는 from)은 시작 상태, 100% (또는 to)는 끝 상태를 의미합니다. 중간 단계를 여러 개 지정할 수 있습니다.

@keyframes slideIn {
    0% { /* 시작 상태 */
        transform: translateX(-100%); /* 왼쪽으로 완전히 벗어남 */
        opacity: 0;
    }
    50% { /* 50% 지점 */
        opacity: 0.5;
    }
    100% { /* 끝 상태 */
        transform: translateX(0); /* 원래 위치로 돌아옴 */
        opacity: 1;
    }
}

@keyframes bounce {
    0%, 20%, 50%, 80%, 100% {
        transform: translateY(0);
    }
    40% {
        transform: translateY(-30px);
    }
    60% {
        transform: translateY(-15px);
    }
}

애니메이션 관련 주요 속성

정의된 @keyframes 애니메이션을 HTML 요소에 적용하려면 다음 속성들을 사용합니다.

animation-name: 적용할 @keyframes 애니메이션의 이름을 지정합니다. (필수)

animation-duration: 한 번의 애니메이션 주기가 완료되는 데 걸리는 시간을 지정합니다. (s 또는 ms, 필수)

animation-timing-function: 애니메이션 속도의 변화 패턴을 지정합니다. (트랜지션과 동일)

animation-delay: 애니메이션이 시작되기 전까지 기다릴 시간을 지정합니다.

animation-iteration-count: 애니메이션이 반복될 횟수를 지정합니다.

  • 1 (기본값): 한 번만 재생
  • infinite: 무한 반복
  • 특정 숫자: 지정된 횟수만큼 반복
.element {
animation-iteration-count: 3; /* 3번 반복 */
animation-iteration-count: infinite; /* 무한 반복 */
}

animation-direction: 애니메이션의 재생 방향을 지정합니다.

  • normal (기본값): 0% → 100% 순으로 재생
  • reverse: 100% → 0% 순으로 재생
  • alternate: 순방향 → 역방향 → 순방향 반복 (짝수 번째는 역방향)
  • alternate-reverse: 역방향 → 순방향 → 역방향 반복
.element {
    animation-direction: alternate; /* 왔다 갔다 반복 */
}

animation-fill-mode: 애니메이션이 끝난 후 또는 시작되기 전 요소의 스타일을 어떻게 유지할지 지정합니다.

  • none (기본값): 애니메이션 시작 전/후에 원래 스타일로 돌아감.
  • forwards: 애니메이션이 끝난 후 마지막 @keyframes 상태를 유지.
  • backwards: 애니메이션 시작 전 0% (또는 from) 상태를 유지. (딜레이가 있을 경우)
  • both: forwardsbackwards 모두 적용.
.element {
    animation-fill-mode: forwards; /* 애니메이션 끝난 상태 유지 */
}

animation-play-state: 애니메이션의 재생 상태를 제어합니다.

  • running (기본값): 재생 중
  • paused: 일시 정지
.element:hover {
    animation-play-state: paused; /* 마우스 오버 시 애니메이션 일시 정지 */
}

animation 단축 속성

위 모든 속성을 한 줄에 요약하여 작성할 수 있습니다. 순서가 중요하며, 필수 값인 animation-nameanimation-duration은 반드시 포함되어야 합니다.

.element {
    /* name | duration | timing-function | delay | iteration-count | direction | fill-mode | play-state */
    animation: slideIn 1s ease-out 0.5s infinite alternate forwards;
}

애니메이션 활용 예시

웹 페이지 로딩 시 나타나는 요소 또는 무한 반복되는 로딩 애니메이션을 만들어 봅시다.

<style>
    /* 애니메이션 정의 */
    @keyframes fadeInSlideUp {
        0% {
            opacity: 0;
            transform: translateY(20px);
        }
        100% {
            opacity: 1;
            transform: translateY(0);
        }
    }

    @keyframes rotate360 {
        0% { transform: rotate(0deg); }
        100% { transform: rotate(360deg); }
    }

    .fade-in-box {
        width: 200px;
        height: 100px;
        background-color: #2ecc71; /* 에메랄드 */
        color: white;
        display: flex;
        justify-content: center;
        align-items: center;
        margin: 50px auto;
        border-radius: 8px;
        font-size: 1.2em;

        /* 애니메이션 적용 */
        animation-name: fadeInSlideUp;
        animation-duration: 0.8s;
        animation-timing-function: ease-out;
        animation-fill-mode: backwards; /* 애니메이션 시작 전 0% 상태 유지 (투명, 아래로) */
        animation-delay: 0.5s; /* 0.5초 후에 시작 */
    }

    .spinner {
        width: 50px;
        height: 50px;
        border: 5px solid #f3f3f3; /* 연한 회색 테두리 */
        border-top: 5px solid #3498db; /* 파란색 상단 테두리 */
        border-radius: 50%; /* 원형 */
        margin: 30px auto;

        /* 애니메이션 적용 */
        animation: rotate360 1s linear infinite; /* 1초 동안 무한히 선형 회전 */
    }
</style>

<div class="fade-in-box">나타나는 박스</div>
<div class="spinner"></div>

트랜지션 vs 애니메이션: 언제 무엇을 쓸까?

  • 트랜지션

    • 간단한 상태 변화: 버튼 호버 효과, 메뉴 확장/축소, 탭 전환 등 두 가지 상태(시작-끝) 간의 부드러운 전환에 적합합니다.
    • 사용자 상호작용에 의해 트리거: :hover, :focus, :active, JavaScript로 클래스 추가/제거 등 이벤트에 의해 속성값이 변할 때 주로 사용합니다.
    • 코드가 비교적 간결합니다.
  • 애니메이션

    • 복잡한 다단계 움직임: 로딩 스피너, 슬라이드쇼, 특정 경로를 따라 움직이는 요소, 무한 반복되는 시각 효과 등 여러 프레임을 거치는 복잡한 움직임에 적합합니다.
    • 자동으로 시작/반복: 페이지 로딩 시 자동으로 시작되거나, 무한 반복되는 효과에 주로 사용됩니다.
    • @keyframes를 통해 각 프레임을 세밀하게 제어할 수 있습니다.

결론: 간단한 UI 인터랙션에는 트랜지션을, 복잡하거나 반복적인 시각 효과에는 애니메이션을 사용한다고 생각하면 됩니다.


실습: 웹 페이지에 동적인 효과 추가하기

배운 트랜지션과 애니메이션 속성들을 활용하여 실제 웹 페이지에 동적인 효과를 추가해 봅시다.

프로젝트 폴더 구조

dynamic_web.html
motion.css

dynamic_web.html 파일 작성

dynamic_web.html
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CSS 애니메이션과 트랜지션 실습</title>
    <link rel="stylesheet" href="css/motion.css">
</head>
<body>
    <header>
        <h1 class="page-title">동적인 웹 페이지 만들기</h1>
        <p class="subtitle">CSS Transition과 Animation의 힘!</p>
    </header>

    <main>
        <section class="transition-section">
            <h2>1. CSS 트랜지션 예시</h2>
            <div class="button-container">
                <button class="hover-button">마우스 오버해보세요</button>
                <div class="box-grow">크기/색깔 변환 박스</div>
            </div>
        </section>

        <section class="animation-section">
            <h2>2. CSS 애니메이션 예시</h2>
            <div class="loading-spinner"></div>
            <div class="bouncing-ball"></div>
            <div class="slide-in-text">짜잔! 등장하는 텍스트!</div>
        </section>
    </main>

    <footer>
        <p>&copy; 2025 CSS Motion Practice. 모든 권리 보유.</p>
    </footer>
</body>
</html>

css/motion.css 파일 작성

css/motion.css
/* 기본 설정 */
* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    background-color: #f0f2f5;
    color: #333;
    line-height: 1.6;
    padding-bottom: 50px;
}

header {
    background-color: #34495e;
    color: white;
    text-align: center;
    padding: 30px 20px;
    box-shadow: 0 3px 6px rgba(0,0,0,0.2);
    margin-bottom: 30px;
}
.page-title {
    font-size: 2.8em;
    margin-bottom: 5px;
}
.subtitle {
    font-size: 1.2em;
    font-style: italic;
}

main {
    max-width: 900px;
    margin: 0 auto;
    padding: 0 20px;
}

section {
    background-color: white;
    padding: 30px;
    margin-bottom: 30px;
    border-radius: 8px;
    box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
section h2 {
    color: #2980b9;
    font-size: 2em;
    margin-bottom: 25px;
    text-align: center;
}

/* ------------------------------------------- */
/* 1. CSS 트랜지션 예시 */
/* ------------------------------------------- */
.button-container {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 40px;
    flex-wrap: wrap;
    padding: 20px;
}

.hover-button {
    background-color: #2ecc71; /* 에메랄드 */
    color: white;
    padding: 15px 30px;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    font-size: 1.1em;
    font-weight: bold;
    outline: none; /* 포커스 시 아웃라인 제거 */

    /* 트랜지션 적용: 모든 변화에 0.3초 ease-out 효과 */
    transition: all 0.3s ease-out;
}

.hover-button:hover {
    background-color: #27ae60; /* 진한 에메랄드 */
    transform: scale(1.05) translateY(-5px); /* 확대 및 살짝 위로 이동 */
    box-shadow: 0 8px 15px rgba(0, 0, 0, 0.2); /* 그림자 진하게 */
}

.box-grow {
    width: 150px;
    height: 150px;
    background-color: #e74c3c; /* 벽돌색 */
    color: white;
    display: flex;
    justify-content: center;
    align-items: center;
    border-radius: 15px;
    font-size: 1.1em;
    cursor: pointer;

    /* 트랜지션 적용: 너비, 높이, 배경색, border-radius에 0.4초 ease 효과 */
    transition: width 0.4s ease, height 0.4s ease,
                background-color 0.4s ease, border-radius 0.4s ease;
}

.box-grow:hover {
    width: 200px; /* 너비 증가 */
    height: 200px; /* 높이 증가 */
    background-color: #c0392b; /* 배경색 변경 */
    border-radius: 50%; /* 원형으로 변경 */
}


/* ------------------------------------------- */
/* 2. CSS 애니메이션 예시 */
/* ------------------------------------------- */

/* 로딩 스피너 애니메이션 */
@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}

.loading-spinner {
    width: 60px;
    height: 60px;
    border: 6px solid #f3f3f3; /* 밝은 회색 테두리 */
    border-top: 6px solid #3498db; /* 파란색 상단 테두리 */
    border-radius: 50%;
    margin: 30px auto; /* 중앙 정렬 */

    /* 애니메이션 적용 */
    animation: spin 1.2s linear infinite; /* 1.2초 동안 선형 무한 회전 */
}

/* 통통 튀는 공 애니메이션 */
@keyframes bounce {
    0%, 100% { transform: translateY(0); } /* 시작과 끝은 제자리 */
    50% { transform: translateY(-50px); } /* 중간에 위로 50px */
}

.bouncing-ball {
    width: 50px;
    height: 50px;
    background-color: #f1c40f; /* 노란색 */
    border-radius: 50%;
    margin: 50px auto;
    box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);

    /* 애니메이션 적용 */
    animation: bounce 1.5s ease-in-out infinite alternate; /* 1.5초 동안 ease-in-out으로 무한히 왕복 */
}

/* 슬라이드인 텍스트 애니메이션 */
@keyframes slideInFromLeft {
    0% {
        transform: translateX(-100%); /* 왼쪽 밖에서 시작 */
        opacity: 0;
    }
    100% {
        transform: translateX(0); /* 원래 위치로 이동 */
        opacity: 1;
    }
}

.slide-in-text {
    font-size: 2.5em;
    font-weight: bold;
    color: #27ae60;
    text-align: center;
    margin-top: 50px;
    opacity: 0; /* 초기에는 투명하게 */

    /* 애니메이션 적용 */
    animation: slideInFromLeft 1s ease-out forwards; /* 1초 동안 ease-out으로 재생 후 마지막 상태 유지 */
    animation-delay: 0.5s; /* 0.5초 후에 애니메이션 시작 */
}


/* 푸터 */
footer {
    background-color: #34495e;
    color: #ecf0f1;
    text-align: center;
    padding: 20px 0;
    margin-top: 50px;
    font-size: 0.9em;
}

결과 확인

  • Live Server를 통해 dynamic_web.html 파일을 열어보세요.
  • 트랜지션 섹션
    • hover-button에 마우스를 올렸다 내렸다 하며 배경색 변화와 크기/위치/그림자 변화가 부드럽게 일어나는 것을 확인하세요.
    • box-grow에 마우스를 올렸다 내렸다 하며 너비, 높이, 배경색, 테두리 반경(원형으로)이 부드럽게 전환되는 것을 확인하세요.
  • 애니메이션 섹션
    • 페이지를 로드하자마자 loading-spinner가 계속 회전하는 것을 확인하세요.
    • bouncing-ball이 통통 튀어 오르는 동작을 무한 반복하는 것을 확인하세요.
    • 페이지 로드 후 잠시 뒤(animation-delay) slide-in-text가 왼쪽에서 부드럽게 나타나는 것을 확인하세요.

이번 장에서는 CSS만으로 웹 페이지에 동적인 효과를 부여하는 강력한 두 가지 기법인 CSS 트랜지션(Transition)CSS 애니메이션(Animation) 을 학습했습니다.

  • 트랜지션은 CSS 속성값이 변할 때 부드러운 전환 효과를 주는 데 사용되며, 주로 사용자 상호작용에 의해 트리거되는 간단한 상태 변화에 적합합니다. transition-property, transition-duration, transition-timing-function, transition-delay 속성들을 통해 전환 효과를 세밀하게 제어할 수 있습니다.
  • 애니메이션@keyframes 규칙을 통해 여러 단계의 복잡한 움직임을 정의하고, animation-name, animation-duration, animation-iteration-count, animation-direction, animation-fill-mode 등 다양한 속성으로 애니메이션의 재생 방식을 제어할 수 있습니다. 이는 로딩 효과나 반복적인 시각 효과 등 더 복잡한 움직임을 구현하는 데 적합합니다.

이 두 가지 기법을 적절히 활용함으로써 여러분의 웹 페이지는 단순한 정보 전달을 넘어 사용자에게 시각적인 즐거움과 더 나은 인터랙션을 제공하는 동적인 경험을 선사할 수 있게 될 것입니다.