Reduce Motion — 전정 장애가 있는 사용자의 불편을 막기 위해 인터페이스 애니메이션을 비활성화하거나 줄이는 운영 체제와 브라우저의 접근성 기능입니다. iOS에서 이 설정은 Settings > Accessibility > Motion > Reduce Motion에 있으며 시차 효과, 화면 전환 애니메이션, 아이콘 효과를 비활성화합니다. Android에서는 동일한 옵션인 “애니메이션 제거”가 Settings > Accessibility에 있습니다. W3C WCAG 2.2에 따르면 과도한 애니메이션은 전정 장애가 있는 사람들의 15~35%에서 메스꺼움, 어지러움, 편두통을 유발할 수 있습니다.
핵심 요점
Reduce Motion — 운영 체제, 브라우저, 앱에 애니메이션, 움직임, 확대·축소, 시차 효과를 최소화하거나 완전히 비활성화하도록 지시하는 시스템 설정입니다. 이 기능은 전정 장애가 있는 사람을 위해 만들어졌으며, 이들에게 움직이는 인터페이스 요소는 메스꺼움, 어지러움, 방향 감각 상실, 편두통을 유발합니다.
내이의 전정 기관은 균형 감각과 공간 지향을 담당합니다. 눈이 화면에서 움직임을 볼 때 전정 기관이 신체의 해당 이동을 감지하지 못하면 감각 충돌이 발생합니다. 이 충돌은 자율 신경 반응을 촉발합니다: 메스꺼움, 식은땀, 어지러움입니다. 전정 장애 협회에 따르면 40세 이상의 약 35%가 인터페이스의 모션 효과로 불편을 경험합니다.
Reduce Motion과 단순한 애니메이션 비활성화의 핵심 차이는 시스템 수준입니다. 활성화하면 이 설정은 OS API 또는 CSS 미디어 기능 prefers-reduced-motion을 통해 모든 앱과 웹사이트에 전달됩니다. 개발자는 사용자가 애니메이션을 원하는지 추측할 필요가 없습니다 — OS가 알려줍니다. 이것은 점진적 향상 접근 방식의 일부입니다: 애니메이션은 개선 사항이지 필수 요소가 아닙니다.
Reduce Motion은 시차 효과(기기를 기울일 때 배경이 밀림), 화면 전환 애니메이션(push, slide, fade), 탭 시 아이콘 확대, 메시지 효과(SMS 풍선), 로딩 애니메이션(스피너, 스켈레톤, 진행률 표시줄 애니메이션), 게임의 시차, 웹의 시차 스크롤 효과에 영향을 줍니다. 정적 이미지와 동영상은 영향을 받지 않습니다.
| 애니메이션 유형 | iOS Reduce Motion | Android Remove Animation | CSS prefers-reduced-motion |
|---|---|---|---|
| 배경화면 시차 | 비활성화 | 비활성화 | N/A |
| 화면 전환 | 페이드로 대체 | 비활성화 | CSS transitions |
| 아이콘 애니메이션 | 비활성화 | 영향 없음 | CSS animations |
| 로딩 스피너 | 영향 없음 | 비활성화 | CSS animations |
| 스켈레톤 화면 | 영향 없음 | 정적으로 대체 | CSS animations |
Reduce Motion의 주 대상은 전정 장애(메니에르병, 미로염, BPPV), 편두통(특히 전조가 있는 경우), 운동병(교통수단에서의 멀미), 특정 형태의 간질이 있는 사람들입니다. 이 기능은 고령 사용자에게도 유용합니다 — 전정 기관의 노화 관련 변화가 움직임에 대한 민감도를 높이기 때문입니다 — 그리고 ADHD 사용자에게도 유용합니다. 움직이는 요소가 읽기를 방해하기 때문입니다.
iOS에서 Reduce Motion은 Settings > Accessibility > Motion에 있으며 세 가지 옵션이 있습니다: Reduce Motion(기본 스위치), Auto-Play Message Effects(iMessage 효과 자동 재생), Auto-Play Video Previews(App Store에서 동영상 자동 재생). Reduce Motion이 가장 강력합니다: 배경화면 시차, 앱 열기 애니메이션(줌), 화면 전환(push가 cross-fade로 대체), 홈 화면의 아이콘 효과를 비활성화합니다.
기술적으로 iOS는 UIKit 수준에서 UIView animations와 UIViewControllerAnimatedTransitioning을 통해 Reduce Motion을 구현합니다. 설정이 활성화되면 duration > 0인 UIView.animate가 0.01초로 줄어들고 UIViewController.transitionDuration은 0을 반환합니다. 시차를 위해 iOS는 UIWindow에서 UIInterpolatingMotionEffect를 비활성화합니다. 개발자는 UIAccessibility.isReduceMotionEnabled로 상태를 확인할 수 있습니다.
개발자는 iOS에서 애니메이션을 Reduce Motion에 맞게 조정할 수 있습니다: 복잡한 전환을 페이드 효과로 대체하고, 사용자 정의 UI 구성 요소에서 시차를 비활성화하고, 애니메이션 시간을 단축합니다. SwiftUI는 reduceMotion이 활성화되면 특수 .animation(nil) 수정자를 제공합니다.
import UIKit
class MotionManager {
// Reduce Motion 상태 확인
static var isReduceMotionEnabled: Bool {
return UIAccessibility.isReduceMotionEnabled
}
// Reduce Motion을 고려한 안전한 애니메이션
static func animate(
duration: TimeInterval,
delay: TimeInterval = 0,
options: UIView.AnimationOptions = [],
animations: @escaping () -> Void,
completion: ((Bool) -> Void)? = nil
) {
let adjustedDuration = isReduceMotionEnabled
? 0.01
: max(duration, 0.01)
UIView.animate(
withDuration: adjustedDuration,
delay: delay,
options: options,
animations: animations,
completion: completion
)
}
}
// SwiftUI — Reduce Motion용 수정자
import SwiftUI
struct MotionAwareView: View {
@Environment(\.accessibilityReduceMotion)
private var reduceMotion
@State private var isAnimating = false
var body: some View {
Circle()
.scaleEffect(isAnimating ? 1.2 : 1.0)
.animation(
reduceMotion
? nil
: .easeInOut(duration: 1.0).repeatForever(),
value: isAnimating
)
.onAppear { isAnimating = true }
}
}
// Reduce Motion 변경 구독
NotificationCenter.default.addObserver(
self,
selector: #selector(motionStatusChanged),
name: UIAccessibility.reduceMotionStatusDidChangeNotification,
object: nil
)MotionManager 클래스는 isReduceMotionEnabled를 확인하고 애니메이션 시간을 조정합니다: Reduce Motion이 활성화되면 시간이 0.01초(실질적으로 즉시)로 줄어듭니다. SwiftUI의 .animation(nil) 수정자는 reduceMotion == true일 때 애니메이션을 완전히 비활성화합니다. reduceMotionStatusDidChangeNotification 구독을 통해 앱을 다시 시작하지 않고도 애니메이션을 동적으로 전환할 수 있습니다.
iOS의 별도 옵션 — Auto-Play Message Effects — 는 iMessage에서 효과 재생을 제어합니다: 풍선 애니메이션, 색종이, 레이저, 불꽃놀이입니다. Reduce Motion이 활성화되면 iMessage 효과가 자동으로 비활성화됩니다. 개발자는 UIAccessibility.isReduceMotionEnabled로 이 옵션을 확인할 수 있습니다 — Message Effects용 별도 API는 없으며 공통 설정에 연결되어 있습니다.
Android에서 “애니메이션 제거” 설정은 두 곳에 있습니다: Settings > Accessibility > 애니메이션 제거(접근성 설정용)와 개발자 옵션 > 애니메이션 배율(개발자용)입니다. 접근성 버전은 모든 시스템 애니메이션을 비활성화합니다(화면 전환, 앱 열기 애니메이션, 로딩 효과 포함). 개발자 옵션은 배율을 0.5x, 0.1x로 줄이거나 완전히 비활성화할 수 있습니다.
기술적으로 Android는 시스템 속성을 통해 애니메이션 비활성화를 구현합니다: window_animation_scale, transition_animation_scale, animator_duration_scale입니다. 접근성 설정에서는 세 값 모두 0이 됩니다. 시스템 런처와 시스템 앱은 이 값을 확인합니다. 타사 앱의 경우 Android는 AnimationScaleObserver를 통해 시스템 설정을 자동으로 읽는 Jetpack Compose와 View 시스템 사용을 권장합니다.
개발자는 Android에서 Settings.Global을 통해 애니메이션 상태를 확인하고 UI를 조정할 수 있습니다. Jetpack Compose는 자동 조정을 위한 AnimationScaleObserver를 제공합니다. 핵심 값은 animator_duration_scale입니다: 0이면 애니메이션이 완전히 비활성화된 것입니다.
import android.content.Context
import android.provider.Settings
import android.database.ContentObserver
import android.os.Handler
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.runtime.getValue
class AndroidMotionHelper {
// 애니메이션 비활성화 확인
fun isAnimationDisabled(context: Context): Boolean {
return try {
Settings.Global.getFloat(
context.contentResolver,
Settings.Global.ANIMATOR_DURATION_SCALE
) == 0f
} catch (e: Settings.SettingNotFoundException) {
false
}
}
// 애니메이션 변경 관찰(ContentObserver)
fun observeAnimationScale(
context: Context,
onChange: (Boolean) -> Unit
): ContentObserver {
val observer = object : ContentObserver(Handler()) {
override fun onChange(selfChange: Boolean) {
onChange(isAnimationDisabled(context))
}
}
context.contentResolver.registerContentObserver(
Settings.Global.getUriFor(
Settings.Global.ANIMATOR_DURATION_SCALE
),
false,
observer
)
return observer
}
}
// Jetpack Compose — 설정을 고려한 애니메이션
@Composable
fun MotionAwareAnimation(enabled: Boolean) {
val context = androidx.compose.ui.platform.LocalContext.current
val noMotion = AndroidMotionHelper().isAnimationDisabled(context)
val scale by animateFloatAsState(
targetValue = if (enabled) 1f else 0f,
animationSpec = if (noMotion) {
androidx.compose.animation.core.tween(durationMillis = 0)
} else {
androidx.compose.animation.core.spring()
},
label = "scale"
)
// 애니메이션이 있는 UI
}
AndroidMotionHelper 클래스는 Settings.Global을 통해 ANIMATOR_DURATION_SCALE을 확인하고, 변경 사항을 추적하는 ContentObserver를 제공하며, Jetpack Compose 애니메이션이 적응할 수 있게 합니다: 애니메이션이 비활성화되면 durationMillis = 0(즉시 적용)입니다. View 시스템의 경우 애니메이션 시작 전에 ViewPropertyAnimator.withLayer()를 사용하고 isAnimationDisabled()를 확인하세요.
Android의 “애니메이션 제거” 접근성 설정은 일반 사용자가 애니메이션을 비활성화하는 유일한 방법입니다. 개발자 옵션은 개발자만을 위한 것이며 숨겨져 있을 수 있습니다. 접근성 설정은 시스템 API를 통해 모든 앱에 확실하게 전달됩니다. 개발자는 개발자 옵션보다 AccessibilityManager에 의존해야 합니다.
CSS 미디어 기능 prefers-reduced-motion은 사용자가 운영 체제에서 Reduce Motion을 활성화했을 때 웹 개발자가 애니메이션을 비활성화하거나 대체할 수 있게 합니다. 시스템 설정이 활성화되면 브라우저는 reduce 값을 전달하고, 설정이 변경되지 않으면 no-preference를 전달합니다. 2020년부터 모든 최신 브라우저에서 지원됩니다: Chrome 74+, Safari 10.3+, Firefox 64+, Edge 79+.
MDN과 W3C의 권장 사항 — 애니메이션과 함께 항상 prefers-reduced-motion: reduce를 지정하는 것입니다. Reduce Motion을 활성화한 사용자는 맥동, 시차, 자동 재생 캐러셀, 스켈레톤 로딩을 보지 않아야 합니다. 애니메이션 대신 정적 콘텐츠를 표시하거나 최소 시간의 CSS transition을 사용할 수 있습니다. 아래는 전체 적응의 예입니다.
prefers-reduced-motion: reduce에서 웹 페이지는: CSS 애니메이션(@keyframes)을 비활성화하고, CSS transitions를 즉시 변경으로 대체하고, JavaScript 애니메이션(requestAnimationFrame)을 중지하고, 시차 스크롤을 비활성화하고, 캐러셀과 슬라이더를 중지하고, 스켈레톤 효과를 비활성화하고, 스피너를 정적 표시기로 대체해야 합니다.
/* 기본 애니메이션(no-preference) */
.spinner {
width: 32px;
height: 32px;
border: 3px solid #e0e0e0;
border-top-color: #0066CC;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
.skeleton {
background: linear-gradient(
90deg,
#f0f0f0 25%,
#e8e8e8 37%,
#f0f0f0 63%
);
background-size: 400% 100%;
animation: shimmer 1.4s ease infinite;
}
.parallax-bg {
transform: translateZ(-1px) scale(1.5);
transition: transform 0.3s ease-out;
}
/* Reduce Motion: 모든 것 비활성화 */
@media (prefers-reduced-motion: reduce) {
.spinner {
animation: none;
border-top-color: #0066CC;
}
.skeleton {
animation: none;
background: #f0f0f0;
}
.parallax-bg {
transform: none;
transition: none;
}
.carousel {
scroll-behavior: auto;
overflow-x: auto;
}
.fade-in {
opacity: 1;
transform: none;
}
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
@keyframes spin {
to { transform: rotate(360deg); }
}
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}CSS 예제에서는 prefers-reduced-motion: reduce 시 애니메이션의 전역 리셋을 사용합니다: 모든 애니메이션과 전환이 duration 0.01ms와 iteration-count 1을 받습니다. 스피너(정적 테두리), 스켈레톤(깜빡임 없는 회색 배경), 시차(비활성화), 캐러셀(smooth 대신 scroll-behavior: auto)이 개별적으로 재정의됩니다. 보편 선택자 *는 모든 요소에 대한 안전망입니다.
JavaScript에서는 matchMedia('(prefers-reduced-motion: reduce)').matches를 사용할 수 있습니다. true인 경우 — requestAnimationFrame, setInterval 또는 Web Animations API를 통해 애니메이션을 실행하지 마세요. 예: reduceMotion에서 슬라이더는 자동으로 회전하지 않고 버튼으로만 작동합니다. GSAP, Framer Motion, Anime.js 같은 라이브러리는 접근성 옵션을 통해 이 확인을 지원합니다.
WCAG 2.2(2023)는 새로운 기준 2.3.3 Animation from Interactions(수준 AAA)을 도입했습니다: 애니메이션이 사용자 상호작용(클릭, 스크롤, 호버)으로 시작되면 비활성화할 수 있어야 하거나 5초를 초과하지 않아야 합니다. 상호작용과 관련 없는 애니메이션(로딩 스피너, 진행률 표시줄)은 이 기준에 해당하지 않습니다. 수준 AA에서는 애니메이션이 정보 전달의 유일한 수단이 아니면 충분합니다.
기준 2.3.3은 시차 스크롤과 “당겨서 새로고침” 애니메이션으로 인한 멀미에 대한 수많은 불만 이후 추가되었습니다. W3C는 모든 사이트와 앱에 권장합니다: 요소를 5초 이상 애니메이션하지 말고, 애니메이션을 비활성화하는 스위치를 제공하고(제어판 또는 prefers-reduced-motion 통해), 맥동 및 깜박이는 요소를 사용하지 마십시오(간질에 위험).
WCAG 2.2를 준수하려면 개발자는: prefers-reduced-motion을 확인하고 설정이 활성화되면 애니메이션을 비활성화하고, 애니메이션 최대 시간을 5초로 설정하고(비활성화할 수 없는 경우), 앱 설정에서 애니메이션 UI 스위치를 제공하고, 시차 효과를 유일한 탐색 방법으로 사용하지 말고, 모든 브라우저와 모든 기기에서 Reduce Motion을 활성화한 상태로 테스트해야 합니다.
Reduce Motion으로 앱 동작을 테스트하려면 사용하세요: iOS Simulator(Settings > Accessibility에서 Reduce Motion 활성화), Android Emulator(“애니메이션 제거” 활성화), Chrome DevTools(Rendering > Emulate CSS media feature prefers-reduced-motion), Safari 웹 인스펙터(macOS System Preferences > Accessibility > Display에서 Reduce Motion 활성화). 콘텐츠가 애니메이션 없이도 접근 가능하고 읽을 수 있는지 확인하세요.
자주 묻는 질문
Reduce Motion — 전정 장애가 있는 사람을 위해 인터페이스 애니메이션(시차, 전환, 아이콘 확대)을 비활성화하는 OS 접근성 기능입니다. Settings > Accessibility에서 활성화합니다. 시스템은 UIAccessibility(iOS), Settings.Global(Android) 또는 prefers-reduced-motion(CSS)을 통해 앱에 신호를 보내며, 개발자는 애니메이션을 조정할 수 있습니다.
iPhone에서 Reduce Motion은 Settings > Accessibility > Motion > Reduce Motion에서 활성화합니다. 활성화하면 배경화면 시차, iMessage 효과, 앱 열기 애니메이션, 화면 전환이 비활성화됩니다(cross-fade로 대체). UIKit 시스템 애니메이션은 재생되지 않습니다. 이 설정은 기기를 다시 시작하지 않고 모든 앱에 적용됩니다.
Android에서 애니메이션은 두 가지 방법으로 비활성화됩니다: Settings > Accessibility > 애니메이션 제거(사용자에게 권장) 또는 개발자 옵션 > 애니메이션 배율 — 끄기. 접근성 방법은 모든 앱이 신호를 받도록 보장합니다. 개발자 옵션은 시스템 애니메이션에만 영향을 주며 일부 기기에서는 숨겨져 있을 수 있습니다.
CSS 미디어 기능 prefers-reduced-motion: reduce는 스타일 수준에서 애니메이션을 비활성화할 수 있게 합니다. 보편적 리셋을 설정하는 것이 좋습니다: 모든 요소에 animation-duration: 0.01ms, transition-duration: 0.01ms, animation-iteration-count: 1. 특정 구성 요소(스피너, 스켈레톤, 시차)는 개별적으로 재정의하세요. JavaScript는 matchMedia('(prefers-reduced-motion: reduce)')로 확인됩니다.
WCAG 2.2 기준 2.3.3(AAA)은 사용자 상호작용으로 시작된 애니메이션이 비활성화 가능하거나 5초를 초과하지 않도록 요구합니다. AA에서는 애니메이션이 정보 전달의 유일한 수단이 아니면 충분합니다. 항상 prefers-reduced-motion을 확인하고 앱 설정에서 애니메이션 UI 스위치를 제공하는 것이 좋습니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.