한 줄 요약 —
transition은 호버처럼 값이 바뀔 때 그 변화가 툭 끊기지 않고 정해진 시간에 걸쳐 부드럽게 이어지게 하는 CSS다. 사무실 유리문 위에 달린 도어 클로저가 문을 쾅 닫지 않고 천천히 닫아 주듯,transition은 색·크기·위치가 변할 때 그 사이의 중간 모습을 자동으로 채워 준다. 무엇을(속성)·얼마 동안(시간)·어떤 속도 곡선으로(timing-function)·언제부터(지연) 변할지, 이 네 가지를 정한다.
학습 목표#
transition이 값이 바뀌는 순간을 부드럽게 이어 준다는 것을 이해할 수 있다.transition의 네 부분(속성·시간·속도 곡선·지연)을 구분해 쓸 수 있다.transition: all의 단점을 알고, 바뀔 속성만 콕 집어 쓸 수 있다.ease·linear·cubic-bezier의 차이를 알고 자연스러운 속도 곡선을 고를 수 있다.- 버튼 호버에 색·그림자·아주 살짝의 이동을 한꺼번에 부드럽게 줄 수 있다.
오늘의 비유 — 문을 쾅 닫지 않고 천천히 닫는 도어 클로저#
사무실이나 상가의 유리문 위쪽을 보면, 문과 문틀을 잇는 길쭉한 장치가 하나 달려 있다. 도어 클로저다. 이게 없으면 문은 손을 떼는 순간 쾅 닫힌다. 도어 클로저가 있으면 문이 열린 상태에서 닫힌 상태로 가는 그 사이를 천천히, 끝에서는 더 살살 채워 준다. 문이 가야 할 위치(열림 → 닫힘)는 똑같은데, 그 사이를 어떻게 건너갈지를 장치가 맡는 셈이다.
transition이 바로 이 도어 클로저다. CSS에서 :hover 같은 상태가 바뀌면 값은 곧장 새 값으로 점프하려 한다 — 문이 쾅 닫히듯. transition을 달아 두면 브라우저가 그 사이의 중간 값들을 자동으로 만들어, 0.2초든 0.3초든 정해 준 시간에 걸쳐 부드럽게 건너가게 한다. 우리가 정하는 건 딱 네 가지다. 어느 문에 장치를 달지, 닫히는 데 몇 초를 줄지, 어떤 속도로 닫을지, 손을 뗀 뒤 얼마 있다 닫기 시작할지.
핵심 개념#
transition의 네 부분#
transition은 네 가지를 한 줄에 이어 적는 줄임 표기다.
.box {
transition: background-color 0.2s ease 0s;
/* ┗ 속성 ┗시간 ┗곡선 ┗지연 */
}
- 속성: 어떤 값이 바뀔 때 부드럽게 이을지.
background-color,transform등. - 시간(duration): 건너가는 데 걸리는 시간.
0.2s,200ms. - 속도 곡선(timing-function): 그 시간 동안 빠르기를 어떻게 배분할지.
ease,linear등. - 지연(delay): 변화가 시작되기까지 기다리는 시간. 보통
0s라 생략한다.
지연이 0이면 마지막 값을 빼고 transition: background-color 0.2s ease처럼만 적어도 된다.
어디에 transition을 다는가 — :hover가 아니라 평소 상태에#
가장 헷갈리는 지점이다. transition은 변화가 시작되는 평소 상태(기본 선택자)에 달아야, 들어갈 때도 나올 때도 부드럽다.
.btn {
background-color: #2d6cdf;
transition: background-color 0.2s ease;
}
.btn:hover {
background-color: #1b4fb3;
}
도어 클로저는 문이 닫힐 때만이 아니라 열릴 때도 같이 작동하는 장치다. 그래서 문 자체(.btn)에 달지, "닫힌 상태"(:hover)에만 달지 않는다. :hover에만 transition을 적으면 마우스를 올릴 때만 부드럽고 뗄 때는 툭 끊긴다.
속도 곡선 — 끝에서 살살 닫히게#
linear는 처음부터 끝까지 같은 속도다. 문이 일정 속도로 닫히다 그대로 멈추는 느낌이라 어딘가 기계적이다. 실제 도어 클로저는 끝으로 갈수록 속도를 줄여 살며시 닫힌다. 이 "끝에서 감속"이 기본값인 ease이고, 그래서 대부분의 호버에는 ease가 무난하다.
.a { transition: transform 0.2s linear; } /* 일정 속도 */
.b { transition: transform 0.2s ease; } /* 끝에서 감속(기본값) */
더 세밀하게 곡선을 직접 그리고 싶으면 cubic-bezier(x1, y1, x2, y2)를 쓴다. 네 숫자로 가속·감속 곡선을 정의하는데, 손으로 딱 맞추기는 어렵다. MDN의 큐빅 베지어 편집기에서 곡선을 끌어 보며 값을 뽑는 게 보통이다. 끝에서 살짝 통통 튀는 느낌이 필요할 때 자주 꺼내 쓴다.
delay — 잠깐 기다렸다 닫히기#
transition-delay는 변화가 곧장이 아니라 잠깐 있다가 시작되게 한다. 메뉴에 올렸던 마우스가 살짝 벗어나도 바로 닫히지 않게, 약간의 "유예"를 줄 때 쓴다.
.menu { transition: opacity 0.2s ease 0.1s; } /* 0.1초 기다렸다 시작 */
함께 따라하기 — 색·그림자·살짝의 이동을 한꺼번에#
버튼에 마우스를 올리면 색이 진해지고, 그림자가 깊어지고, 살짝 떠오르는 효과를 한 번에 부드럽게 줘 본다. (떠오르는 이동은 지난 회차의 transform을 쓴다.)
<button class="btn" type="button">시작하기</button>
.btn {
padding: 12px 24px;
border: none;
border-radius: 8px;
color: #fff;
background-color: #2d6cdf;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
transition: background-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
}
.btn:hover {
background-color: #1b4fb3;
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25);
transform: translateY(-2px);
}
저장하고 버튼에 마우스를 올리면, 세 변화가 동시에 0.2초에 걸쳐 부드럽게 일어나 버튼이 살짝 떠오른다. 마우스를 치우면 도어 클로저가 문을 되돌리듯 같은 시간에 걸쳐 천천히 원래대로 돌아온다. transition은 이렇게 쉼표로 속성을 나열하면 여러 변화를 한꺼번에 다룰 수 있다.
흔한 실수 3가지#
1. transition: all로 모든 속성을 한꺼번에 보간한다#
바뀔 속성을 일일이 적기 귀찮아서 all로 퉁치는 경우다.
/* (의도치 않은 속성까지 따라 움직이고, 매 변화를 다 감시해 무거워집니다) */
.btn {
transition: all 0.2s ease;
}
all은 "이 요소에서 바뀌는 모든 속성"에 도어 클로저를 단다. 색만 부드럽게 하고 싶었는데 나중에 추가한 padding이나 width까지 같이 느릿느릿 움직여 예상 밖 결과가 나온다. 브라우저도 변할 수 있는 모든 속성을 지켜봐야 해 더 무겁다. 바꿀 속성만 콕 집어 쓰는 게 낫다.
.btn {
transition: background-color 0.2s ease; /* 부드럽게 할 속성만 */
}
2. easing 없이 linear로만 줘서 어색하다#
속도 곡선을 신경 쓰지 않고 linear로 두는 경우다.
/* (처음부터 끝까지 같은 속도라 기계적으로 보입니다) */
.card {
transition: transform 0.25s linear;
}
linear는 도어 클로저 없이 문이 일정 속도로 닫히다 그대로 멈추는 느낌이라 딱딱하다. 자연스러운 움직임은 끝으로 갈수록 속도가 줄어든다. 특별한 이유가 없으면 기본값 ease를 쓰거나, 아예 곡선을 적지 않아 ease가 적용되게 둔다.
.card {
transition: transform 0.25s ease; /* 끝에서 살며시 감속 */
}
3. duration을 너무 길게 줘서 답답하다#
부드러움을 강조하려고 시간을 길게 주는 경우다.
/* (반응이 굼떠서 버튼이 고장 난 것처럼 느껴집니다) */
.btn {
transition: background-color 1s ease;
}
문이 1초 넘게 천천히 닫히면 부드러운 게 아니라 답답하다. 호버처럼 즉각 반응해야 하는 UI는 보통 0.15s~0.3s 사이가 적당하다. 0.4초를 넘기면 사용자는 "왜 이렇게 느리지" 하고 느낀다. 큰 패널이 슬며시 나타나는 정도가 아니라면 짧게 잡는다.
.btn {
transition: background-color 0.2s ease; /* 즉각 반응하는 길이 */
}
오늘 배운 것 체크리스트#
-
transition은 값이 바뀔 때 그 사이를 정해진 시간에 걸쳐 채워 준다. -
transition은 변화가 시작되는 평소 상태에 달아야 들어갈 때·나올 때 모두 부드럽다. -
transition: all대신 바뀔 속성만 콕 집어 쓴다. - 자연스러운 움직임은
ease(끝에서 감속)가 기본, 필요하면cubic-bezier로 곡선을 직접 만든다. - 호버 길이는 보통 0.15s~0.3s가 무난하고, 0.4s를 넘기면 답답해진다.
자주 묻는 질문#
Q. transition: all은 왜 쓰지 말라고 하나요?
A. all은 그 요소에서 바뀌는 모든 속성에 전환을 겁니다. 지금은 색 하나만 바뀌어도, 나중에 padding·width 같은 속성을 추가하면 그것까지 의도치 않게 따라 움직이고, 브라우저도 변할 수 있는 모든 속성을 감시해야 해 무거워집니다. transition: background-color 0.2s ease처럼 부드럽게 할 속성만 명시하면 결과가 예측 가능하고 가볍습니다.
Q. cubic-bezier 값은 어떻게 정하나요?
A. 네 숫자를 손으로 맞추기는 어려워서, MDN의 transition-timing-function 문서에 있는 큐빅 베지어 편집기에서 곡선을 직접 끌어 보며 값을 뽑는 게 일반적입니다. 대부분의 호버는 기본값 ease로 충분하고, 끝에서 살짝 통통 튀는 느낌이 필요할 때만 cubic-bezier를 꺼내 쓰면 됩니다.
Q. transition-delay는 언제 쓰나요?
A. 변화가 곧장 시작되지 않고 잠깐 기다렸다 시작하게 할 때 씁니다. 예를 들어 메뉴에 올렸던 마우스가 실수로 살짝 벗어나도 바로 닫히지 않도록 0.1s 정도의 유예를 줄 때 유용합니다. 보통은 0s라 생략하고, 필요할 때만 마지막 값으로 더해 줍니다.
다음 시간 예고#
내일은 @keyframes로 만드는 첫 무빙 UI를 다룬다. transition이 두 상태 사이를 잇는 일이라면, animation은 여러 장면을 순서대로 이어 스스로 움직이는 로딩 스피너 같은 UI를 만드는 일이다. 모션을 불편해하는 사용자를 위한 prefers-reduced-motion까지 함께 짚는다.