CSS 독학 가이드 7 - 애니메이션과 전환 효과

learning by Seven Fingers Studio 22분
CSS애니메이션transitionanimationtransform효과

애니메이션이 UX를 바꾼다

정적인 웹사이트와 움직이는 웹사이트의 차이를 아시나요? 버튼을 클릭했을 때 바로 변하는 것보다 부드럽게 전환되는 게 훨씬 자연스럽고 고급스러워 보여요.

저는 처음 CSS 애니메이션을 배웠을 때 “와, 이거 JavaScript 없이도 되네!” 하고 놀랐어요. 생각보다 훨씬 간단하거든요.

Transition (전환 효과)

A 상태에서 B 상태로 부드럽게 변하게 만들어요.

기본 문법

.button {
  background: blue;
  transition: background 0.3s;
}

.button:hover {
  background: red;
}

실행 결과:

마우스를 올리면 배경색이 부드럽게 변합니다

마우스를 올리면 파랑에서 빨강으로 0.3초 동안 부드럽게 바뀝니다!

속성 상세

.box {
  transition-property: background;     /* 전환할 속성 */
  transition-duration: 0.3s;           /* 지속 시간 */
  transition-timing-function: ease;    /* 속도 곡선 */
  transition-delay: 0.1s;              /* 딜레이 */
}

/* 축약형 */
.box {
  transition: background 0.3s ease 0.1s;
  /*         속성      시간  곡선 딜레이 */
}

여러 속성 동시 전환

.button {
  transition:
    background 0.3s ease,
    transform 0.3s ease,
    box-shadow 0.3s ease;
}

.button:hover {
  background: #007bff;
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(0,0,0,0.2);
}

모든 속성 전환

.box {
  transition: all 0.3s ease;
}

주의: 성능 이슈가 있을 수 있어요. 가능하면 구체적인 속성 지정을 추천!

Timing Functions (속도 곡선)

애니메이션의 속도 변화를 제어해요.

/* 기본 제공 */
transition-timing-function: linear;      /* 일정한 속도 */
transition-timing-function: ease;        /* 느리게-빠르게-느리게 (기본) */
transition-timing-function: ease-in;     /* 느리게 시작 */
transition-timing-function: ease-out;    /* 느리게 끝 */
transition-timing-function: ease-in-out; /* 느리게 시작하고 끝 */

실전 추천:

  • 대부분: ease 또는 ease-out
  • 드롭다운 등장: ease-out
  • 드롭다운 사라짐: ease-in

Cubic-bezier (커스텀 곡선)

.bounce {
  transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

cubic-bezier.com에서 직접 만들어보세요!

Transform (변형)

요소를 이동, 회전, 크기 변경, 기울이기 등을 할 수 있어요.

translate (이동)

.box {
  transform: translateX(100px);   /* 오른쪽으로 100px */
  transform: translateY(-50px);   /* 위로 50px */
  transform: translate(100px, -50px);  /* X, Y 동시 */
}

왜 margin 대신 transform을?

  • transform은 GPU 가속 사용 (더 부드러움)
  • 레이아웃에 영향 안 줌
  • 애니메이션 성능 좋음

scale (크기 조절)

.box {
  transform: scale(1.5);      /* 1.5배 확대 */
  transform: scale(0.8);      /* 0.8배 축소 */
  transform: scale(2, 0.5);   /* 가로 2배, 세로 0.5배 */
}

rotate (회전)

.box {
  transform: rotate(45deg);    /* 45도 회전 */
  transform: rotate(-90deg);   /* -90도 회전 */
}

여러 변형 동시 적용

.box {
  transform: translate(50px, 100px) rotate(45deg) scale(1.2);
}

순서 주의! 왼쪽부터 적용돼요.

실전 예제 1: 호버 효과

버튼 들어올리기

.button {
  background: #007bff;
  color: white;
  padding: 12px 24px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  transition: all 0.3s ease;
}

.button:hover {
  transform: translateY(-3px);
  box-shadow: 0 6px 20px rgba(0,123,255,0.4);
}

.button:active {
  transform: translateY(0);
}

실행 결과:

버튼이 들어올려지며 그림자가 생깁니다

카드 확대

.card {
  transition: transform 0.3s ease;
}

.card:hover {
  transform: scale(1.05);
}

이미지 줌인

.image-container {
  overflow: hidden;
}

.image-container img {
  transition: transform 0.5s ease;
}

.image-container:hover img {
  transform: scale(1.2);
}

실전 예제 2: 로딩 스피너

<div class="spinner"></div>
.spinner {
  width: 50px;
  height: 50px;
  border: 4px solid #f3f3f3;
  border-top: 4px solid #3498db;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

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

실행 결과:

로딩 스피너가 계속 회전합니다

Animation (@keyframes)

복잡한 애니메이션을 만들 때 사용해요. transition보다 강력!

기본 문법

@keyframes slide-in {
  from {
    transform: translateX(-100%);
    opacity: 0;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}

.box {
  animation: slide-in 1s ease;
}

여러 단계 애니메이션

@keyframes bounce {
  0% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-30px);
  }
  100% {
    transform: translateY(0);
  }
}

.ball {
  animation: bounce 1s ease infinite;
}

실행 결과:

공이 위아래로 튀어오릅니다

Animation 속성 상세

.box {
  animation-name: slide-in;           /* 애니메이션 이름 */
  animation-duration: 1s;             /* 지속 시간 */
  animation-timing-function: ease;    /* 속도 곡선 */
  animation-delay: 0.5s;              /* 딜레이 */
  animation-iteration-count: infinite; /* 반복 횟수 */
  animation-direction: alternate;     /* 방향 */
  animation-fill-mode: forwards;      /* 끝난 후 상태 */
}

/* 축약형 */
.box {
  animation: slide-in 1s ease 0.5s infinite alternate forwards;
}

iteration-count 옵션:

  • 1: 한 번만 (기본)
  • 3: 3번 반복
  • infinite: 무한 반복

direction 옵션:

  • normal: 정방향 (기본)
  • reverse: 역방향
  • alternate: 정방향 → 역방향 번갈아
  • alternate-reverse: 역방향 → 정방향 번갈아

fill-mode 옵션:

  • none: 끝나면 원래대로 (기본)
  • forwards: 끝난 상태 유지
  • backwards: 시작 전에 첫 프레임 적용
  • both: forwards + backwards

실전 예제 3: 페이드 인

@keyframes fade-in {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.content {
  animation: fade-in 0.8s ease forwards;
}

/* 순차적으로 나타나기 */
.item:nth-child(1) { animation-delay: 0.1s; }
.item:nth-child(2) { animation-delay: 0.2s; }
.item:nth-child(3) { animation-delay: 0.3s; }

실전 예제 4: 펄스 효과

@keyframes pulse {
  0%, 100% {
    transform: scale(1);
    opacity: 1;
  }
  50% {
    transform: scale(1.1);
    opacity: 0.7;
  }
}

.notification-badge {
  animation: pulse 2s ease-in-out infinite;
}

실전 예제 5: 타이핑 효과

@keyframes typing {
  from { width: 0; }
  to { width: 100%; }
}

@keyframes blink {
  50% { border-color: transparent; }
}

.typing {
  width: 0;
  overflow: hidden;
  border-right: 2px solid;
  white-space: nowrap;
  animation:
    typing 3s steps(30) forwards,
    blink 0.5s step-end infinite;
}

성능 최적화

GPU 가속 속성만 애니메이션

좋은 성능:

  • transform (translate, scale, rotate)
  • opacity

나쁜 성능:

  • width, height
  • margin, padding
  • top, left, right, bottom
/* ❌ 나쁜 예 */
.box {
  transition: width 0.3s;
}

.box:hover {
  width: 300px;
}

/* ✅ 좋은 예 */
.box {
  transition: transform 0.3s;
}

.box:hover {
  transform: scaleX(1.5);
}

will-change 힌트

.box {
  will-change: transform, opacity;
}

브라우저에게 “이거 애니메이션할 거야”라고 미리 알려줘요. 성능 향상!

주의: 모든 요소에 쓰면 오히려 성능 저하. 실제 애니메이션하는 요소에만!

JavaScript와 연동

애니메이션 끝 감지

const box = document.querySelector('.box');

box.addEventListener('animationend', () => {
  console.log('애니메이션 끝!');
});

box.addEventListener('transitionend', () => {
  console.log('전환 끝!');
});

클래스 토글로 애니메이션

.modal {
  opacity: 0;
  transform: scale(0.8);
  transition: all 0.3s ease;
}

.modal.active {
  opacity: 1;
  transform: scale(1);
}
const modal = document.querySelector('.modal');
modal.classList.add('active');  // 애니메이션 시작

자주 쓰는 애니메이션 라이브러리

직접 만들기 귀찮으면:

  • Animate.css: 즉시 사용 가능한 애니메이션
  • GSAP: 복잡한 애니메이션
  • Framer Motion: React 전용
  • AOS: 스크롤 애니메이션
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"/>

<h1 class="animate__animated animate__bounceIn">Hello!</h1>

접근성 고려사항

움직임에 민감한 사용자를 위해:

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

사용자가 시스템에서 “애니메이션 줄이기”를 설정하면 애니메이션이 거의 사라져요.

다음 단계

애니메이션을 마스터했으니 이제 고급 선택자와 가상 요소를 배울 차례예요. 더 정교한 스타일링과 특수 효과를 만들 수 있습니다!

다음 글에서는 자식 선택자, 가상 클래스, ::before/::after 등을 다룰 거예요.

실습 과제:

  • 버튼에 호버 효과 추가하기
  • 로딩 스피너 만들어보기
  • 카드가 페이드인하며 나타나는 효과 만들기
  • 자신만의 keyframes 애니메이션 만들어보기

애니메이션은 과하면 오히려 방해가 돼요. 적절한 타이밍과 속도로 자연스럽게!

시리즈 네비게이션

← 블로그 목록으로