모션 문법: 애니메이션이 프레임 값을 할 때
대부분의 인터페이스 애니메이션은 기능이라는 배지를 달고 있는 장식입니다. 제가 적용하는 테스트는 무딥니다. 그 애니메이션이 사용자에게 무엇을 알려 주는지 설명할 수 없다면, 프레임 값을 하지 못하는 것이니 지우십시오. 이 테스트에서 살아남는 것은 결국 아주 작은 문법으로 정리됩니다. 지속 시간 네 구간, 이징 규칙 두 가지, 그리고 정적인 변화보다 모션이 확실히 더 잘 해내는 몇 안 되는 역할입니다. 나머지는 전부 디자이너가 16밀리초씩 사용자에게 비용을 떠넘기며 스스로를 즐겁게 하는 일에 지나지 않습니다. {.answer-block}
TL;DR
- 모션은 정보이거나, 아니면 소음입니다. 정당한 역할은 네 가지입니다. 무언가가 어디에서 왔고 어디로 갔는지 보여 주는 일, 동작이 접수되었음을 확인시켜 주는 일, 변화가 일어나는 동안 주의를 이끄는 일, 그리고 피할 수 없는 지연을 가려 주는 일입니다. 그 밖의 것은 잘라 내십시오.
- 네 개의 지속 시간 구간이 인터페이스 전체를 덮습니다. 누름과 토글은 약 100ms, 호버와 페이드는 150~200ms, 펼침/접힘은 250~300ms, 페이지 전환과 모달은 300~400ms입니다.
- 두 개의 이징 규칙이 등장과 퇴장을 덮습니다. 들어오는 것에는 ease-out, 나가는 것에는 ease-in을 씁니다. 화면에 머문 채 움직이는 요소에는 ease-in-out을 쓰고, linear는 진행 표시줄과 순수한 페이드의 몫입니다.
- 삭제 테스트가 취향 테스트보다 우선합니다. “느낌이 좋은가”는 나쁜 애니메이션을 살려 두지만, “사용자에게 무엇을 알려 주는가”는 그것을 죽입니다.
prefers-reduced-motion은 나중에 덧붙이는 항목이 아니라 일급 요구 사항으로 다루십시오. 이 문법은 즉각적인 상태 변화로 축소되어도 여전히 말이 되어야 합니다.
모션은 분위기가 아니라 문장입니다
인터페이스 애니메이션은 인과에 대한 주장입니다. 이 패널은 저 버튼에서 나왔다, 이 항목은 저 목록으로 갔다, 이 변화는 당신이 조작했기 때문에 일어났다. 모션이 이런 주장 가운데 하나를 담고 있을 때 사용자의 심성 모형은 공짜로 갱신됩니다. 읽을 필요도, 추론할 필요도 없습니다. 아무것도 담고 있지 않을 때 사용자는 안무가 끝나야 다음으로 넘어갈 수 있고, 그 기다림 하나하나가 신뢰에 매기는 작은 세금이 됩니다.
이 관점에서 모션이 실제로 맡는 역할의 실무 목록이 나옵니다.
- 공간적 연속성 — 이것이 어디에서 왔고 어디로 갔는가. 펼쳐지는 카드, 트리거에서 솟아오르는 시트, 보관함 아이콘 쪽으로 접히며 사라지는 삭제된 행이 그렇습니다.
- 접수 확인 — 당신의 누름이 접수되었습니다. 100ms의 버튼 눌림, 토글의 넘어감, 체크박스의 체크 표시가 그렇습니다.
- 주의 유도 — 상태가 바뀔 때, 움직인 그 하나가 바로 바뀐 그 하나입니다. 모션은 인터페이스가 낼 수 있는 가장 강한 주의 신호에 속하며, 값을 하지 못하는 모션이 그토록 비싼 이유도 정확히 여기에 있습니다.
- 지연 가리기 — 스켈레톤의 반짝임, 낙관적 교체, 400ms의 페치를 고장이 아니라 의도로 느끼게 만드는 점진적 노출이 그렇습니다.
화면의 애니메이션이 이 네 가지 역할 가운데 어느 것도 하고 있지 않다면, 그것은 다섯 번째 역할, 즉 과시를 하고 있는 것입니다.
네 개의 지속 시간 구간
지속 시간은 취향이 아니라 주의에 맞춘 물리입니다. 제가 모든 인터페이스에 적용하는 구간은 다음과 같습니다.
| 지속 시간 | 용도 |
|---|---|
| 약 100ms | 버튼 누름, 토글, 체크박스 — 접수 확인 |
| 150~200ms | 호버 효과, 페이드, 툴팁 |
| 250~300ms | 펼침/접힘, 아코디언, 슬라이드인 패널 |
| 300~400ms | 페이지 전환, 모달, 화면 전체의 변화 |
그 밑에 깔린 패턴은 이렇습니다. 지속 시간은 변화의 크기에 비례합니다. 누름을 받아들이는 컨트롤은 거의 즉각적이어야 하며, 약 150ms를 넘어가면 접수 확인은 지연으로 읽힙니다. 전체 화면 전환에는 시간을 더 줍니다. 사용자가 방향을 다시 잡으려면 그 시간이 필요하기 때문입니다. 가장 흔한 실패는 이 관계가 뒤집히는 경우입니다. 400ms 동안 튀는 버튼(즐거움으로 위장한 지연)과 100ms 만에 튕겨 나오는 모달(속도로 위장한 혼란)이 그렇습니다.
400ms를 넘어서면 인터페이스 모션에는 존재해야 할 예외적인 이유가 필요합니다. 온보딩의 순간이나 축하 상태는 이따금 500~600ms를 얻어 냅니다. 휴대폰 크기의 내비게이션은 거의 그렇지 못하지만, 큰 화면의 컨테이너 변형은 정직한 예외입니다. 태블릿과 데스크톱 화면은 시선이 이동할 거리가 더 길기 때문에 정당하게 500ms 쪽으로 늘어납니다. 사용자는 당신의 전환을 수천 번 실행합니다. 데모에서 매력적이던 안무가 3주 차에는 저주받는 마찰이 됩니다.
스프링 기반 시스템(SwiftUI, Framer Motion)은 타이밍을 고정된 지속 시간이 아니라 강성과 감쇠로 표현하지만, 구간은 그대로 적용됩니다. 그 값들이 결국 당신이 맞춰 가는 체감상의 안정 시간을 기술하기 때문입니다. 표기법이 바뀌어도 문법은 살아남습니다.
두 개의 이징 규칙
이징은 애니메이션이 물리를 얻는 지점이며, 두 가지 규칙이 거의 전부를 덮습니다.
들어오는 것에는 ease-out. 화면에 도착하는 요소는 빠르게 시작해 감속하며 제자리에 놓입니다. 착지하는 것입니다. ease-out은 움직임을 앞쪽에 몰아 주기 때문에 눈이 목적지를 일찍 포착하고, 요소는 반응이 빠르게 느껴집니다.
나가는 것에는 ease-in. 떠나는 요소는 가속하며 멀어집니다. 의도를 가지고 퇴장하는 것입니다. 느린 시작은 무언가가 떠나고 있다는 사실을 눈이 등록할 한 박자를 내어 줍니다.
linear 이징은 기계적으로 읽힙니다. 물리 세계에서 그렇게 움직이는 것은 없기 때문입니다. 일정한 속도 자체가 정보인 진행 표시, 그리고 이동하는 것이 없어 곡선이 다듬을 움직임 자체가 존재하지 않는 순수한 불투명도나 색상 페이드에만 아껴 두십시오. 그리고 대칭적인 ease-in-out은 두 규칙이 일부러 비워 둔 세 번째 경우의 몫입니다. 행이 순서를 바꾸거나 패널이 크기를 바꾸듯, 화면에 머문 채 움직이는 요소가 여기에 해당합니다. 이들은 착지하지도 퇴장하지도 않으므로 정지에서 가속했다가 다시 정지로 잦아듭니다. ease-in-out이 오명을 얻는 자리는 등장과 퇴장에 아무 검토 없이 쓰이는 기본값일 때이며, 그때 도착은 굼뜨고 퇴장은 갑작스러워집니다.
삭제 테스트의 실제
모션 시스템을 정직하게 유지하는 검토 절차는 다음과 같습니다.
- 화면의 모든 애니메이션을 목록으로 만드십시오. 아무도 일부러 고르지 않은 프레임워크 기본값까지 포함해야 합니다.
- 각각에 대해 “이 모션은 사용자에게 ___를 알려 준다”는 문장을 완성하십시오. 공간적 출처, 접수 확인, 주의, 지연 가운데 하나를 평범한 말로 채워야 합니다.
- 빈칸으로 남은 것은 논의를 거쳐도 그대로 빈칸입니다. “완성도가 올라간다”, “고급스럽게 느껴진다”는 어휘만 그럴듯한 빈칸입니다. 그 모션을 지우고 하루 동안 화면을 들여다보십시오. 모션 안에만 존재하던 완성도는 애초에 완성도가 아니었습니다.
- 살아남은 것을 구간에 맞춰 측정하고, 이징을 고치고, 모션 축소 경로를 시험하십시오.
prefers-reduced-motion은 즉각적인 상태 변화만으로도 완전히 일관된 인터페이스를 만들어 내야 합니다. 애니메이션을 없앴을 때 이해가 무너진다면, 그 애니메이션은 정적으로도 존재했어야 할 정보를 나르고 있었다는 뜻이며, 이는 그 자체로 하나의 발견입니다.
성숙한 제품에서 이 절차를 돌리면 대개 목록의 절반이 죽습니다. 한 차례 검토에서 나온 대표적인 제거 목록은 이렇습니다. 카드의 400ms 호버 리프트(커서 변화가 이미 전하지 않은 정보를 사용자에게 조금도 더 주지 않습니다), 이동할 때마다 다시 재생되는 시차 목록 등장(행이 있다고 알리는 데 600ms), 그리고 깜박이는 저장 아이콘(주의는 요구하지만 정작 주의를 기울일 변화는 없습니다). 아무도 이것들을 그리워하지 않습니다. 남은 것은 더 빨라지고 더 일관되며, 역설적으로 더 눈에 띕니다. 모션이 배경음이기를 그만두는 순간 신호로서의 가치를 되찾기 때문입니다.
시스템의 관점
디자인 시스템에서 모션은 여백이나 색과 똑같이 토큰에 속합니다. 이름 붙은 지속 시간(--motion-press: 100ms, --motion-surface: 300ms), 이름 붙은 이징(--ease-enter, --ease-exit), 그리고 인라인으로 타이밍을 지어내지 않고 토큰을 가져다 쓰는 컴포넌트가 그것입니다. 체계화되지 않은 모션의 실패 양상은 흉함이 아니라 드리프트입니다. 다섯 개의 모달에 다섯 개의 지속 시간이 있고 저마다 나름의 근거가 있지만, 모아 놓으면 앞뒤가 맞지 않습니다. 여백 스케일을 다스리는 규율이 모션 스케일도 그대로 다스립니다. 모든 값은 시스템에서 오거나, 문서화된 이유를 달고 있어야 합니다.
모션은 디자인 시스템에서 가장 잘 상하는 층이기도 합니다. 기여자들이 가장 먼저 제멋대로 손대는 자리인데, 일회성 250ms 트윈 하나쯤은 해로울 것이 없어 보이기 때문입니다. 실제로 열한 번까지는 해롭지 않습니다. 그러다 제품은 동기 없는 움직임의 열한 가지 억양으로 깜박이기 시작합니다. 이 문법은 삭제 테스트가 계속 돌아가는 동안에만 유지됩니다.
자주 묻는 질문
UI 애니메이션은 얼마나 길어야 합니까
지속 시간을 변화의 크기에 맞추십시오. 컨트롤의 접수 확인(누름, 토글)은 약 100ms, 호버와 페이드는 150~200ms, 펼침/접힘은 250~300ms, 페이지 전환과 모달은 300~400ms입니다. 400ms를 넘기려면 예외적인 정당화가 필요합니다. 사용자는 인터페이스 전환을 수천 번 반복하기 때문입니다.
UI 애니메이션에는 어떤 이징을 써야 합니까
들어오는 요소에는 ease-out(빠르게 시작해 감속하며 제자리로), 나가는 요소에는 ease-in(가속하며 멀어짐), 화면에 머문 채 움직이는 요소에는 ease-in-out을 씁니다. linear는 진행 표시와 순수한 불투명도나 색상 페이드에 아껴 두십시오. 등장에서 linear는 기계적으로 읽히고 ease-in-out은 도착을 굼뜨게 만듭니다. 여기서의 규칙은 ease-out입니다.
인터페이스는 대체 언제 애니메이션을 써야 합니까
정적인 변화가 나를 수 없는 정보를 모션이 나를 때입니다. 공간적 연속성(무언가가 어디에서 왔고 어디로 갔는지), 입력의 접수 확인, 바뀐 것으로 주의를 이끄는 일, 또는 피할 수 없는 지연을 가리는 일이 그렇습니다. 어떤 역할을 하는지 말할 수 없는 애니메이션은 제거하십시오. 장식적인 모션은 자신이 닿는 모든 상호작용에 세금을 매깁니다.
prefers-reduced-motion은 모션 시스템에 어떻게 들어갑니까
일급 요구 사항으로 들어갑니다. 축소된 경로는 움직임을 즉각적인 상태 변화로 대체하며, 그래도 인터페이스는 완전히 이해 가능해야 합니다. 애니메이션을 없앴을 때 이해가 무너진다면, 그 모션은 정적인 등가물이 필요한 정보를 나르고 있었던 것입니다. 접근성의 발견인 동시에 디자인의 발견이기도 합니다.