July 13, 2025

사용자 선호도 무시: 시스템 설정(다크 모드, 모션 감소) 외면 🌗

CSS
웹표준
UX
아키텍처

Summary

운영체제 수준에서 설정된 다크 모드나 모션 감소와 같은 사용자 선호도를 무시하면 사용자 경험 저하 및 접근성 문제를 야기합니다. CSS의 prefers-color-scheme, prefers-reduced-motion 미디어 쿼리와 JavaScript의 window.matchMedia()를 활용하여 시스템 선호도에 맞춰 UI를 동적으로 조정해야 합니다. 이는 사용자의 기대치를 충족시키고 포용적인 웹을 만드는 데 필수적입니다.

Why Wrong?

사용자가 운영체제(OS) 수준에서 설정한 다크 모드, 고대비 모드, 모션 감소 등의 접근성 및 시각적 선호도를 웹 애플리케이션이 무시할 경우, 사용자 경험을 심각하게 저해하고 접근성 문제를 야기합니다. 예를 들어, 사용자가 시스템을 다크 모드로 설정했음에도 불구하고 웹사이트가 강제로 라이트 모드를 제공하면 눈의 피로를 유발하고, 과도한 애니메이션은 특정 사용자에게 멀미나 주의산만 등 물리적인 불편함을 줄 수 있습니다. 이는 사용자의 기대와 시스템 전반의 일관성을 깨뜨려 앱의 사용성을 크게 떨어뜨립니다.

How to Fix?

웹 애플리케이션은 사용자의 시스템 선호도를 존중하고 이에 따라 UI를 동적으로 조정해야 합니다. 이를 위해 CSS 미디어 쿼리(Media Query)인 prefers-color-schemeprefers-reduced-motion를 적극적으로 활용해야 합니다. JavaScript를 통해 동적인 로직이 필요한 경우 window.matchMedia()를 사용하여 현재 시스템 선호도를 감지하고 변경에 반응할 수 있습니다. 또한, 사용자가 시스템 설정을 일시적으로 재정의할 수 있도록 앱 내에서 테마 토글이나 애니메이션 제어 옵션을 제공하되, 초기값은 항상 시스템 설정을 따르도록 구현하는 것이 좋습니다. 이를 통해 모든 사용자에게 일관되고 편안한 경험을 제공할 수 있습니다.

Before Code (Bad)

/* 라이트 모드만 기본으로 제공하는 CSS (사용자 시스템 설정 무시) */
body {
  background-color: #ffffff; /* 항상 밝은 배경 */
  color: #333333; /* 항상 어두운 텍스트 */
  transition: background-color 0.3s ease, color 0.3s ease;
}

.animated-element {
  transform: translateX(0);
  transition: transform 0.5s ease-in-out; /* 항상 애니메이션 적용 */
}
.animated-element:hover {
  transform: translateX(20px);
}
```

```javascript
// JS에서 테마를 수동으로 토글하지만, 시스템 설정을 고려하지 않음
document.getElementById('theme-toggle').addEventListener('click', () => {
  document.body.classList.toggle('dark-theme');
  // 사용자가 시스템을 다크 모드로 설정했더라도, 여기서는 강제로 토글만 합니다.
});

After Code (Good)

/* 사용자 시스템 선호도(다크 모드, 모션 감소)를 존중하는 CSS */

/* 기본 (라이트 모드) */
body {
  background-color: #ffffff;
  color: #333333;
}

/* 다크 모드 선호 시 */
@media (prefers-color-scheme: dark) {
  body {
    background-color: #222222;
    color: #eeeeee;
  }
}

/* 애니메이션 요소 정의 */
.animated-element {
  transform: translateX(0);
  transition: transform 0.5s ease-in-out;
}
.animated-element:hover {
  transform: translateX(20px);
}

/* 모션 감소 선호 시 */
@media (prefers-reduced-motion: reduce) {
  .animated-element {
    /* 애니메이션 제거 또는 최소화 */
    transition: none; /* 모든 transition 제거 */
    animation: none;  /* 모든 animation 제거 */
    transform: none;  /* 기본 위치 유지 */
  }
}
```

```javascript
// JS에서 시스템 선호도를 감지하여 초기 테마 설정 및 동적 반응
function applySystemTheme() {
  if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
    document.body.classList.add('dark-theme');
  } else {
    document.body.classList.remove('dark-theme');
  }
}

// 초기 로드 시 시스템 테마 적용
applySystemTheme();

// 시스템 테마 변경 감지 및 반영
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', applySystemTheme);

// 앱 내 테마 토글 (옵션): 사용자 선택을 우선시하되, 초기값은 시스템을 따름
document.getElementById('theme-toggle').addEventListener('click', () => {
  const currentTheme = document.body.classList.contains('dark-theme') ? 'dark' : 'light';
  const newTheme = currentTheme === 'dark' ? 'light' : 'dark';

  if (newTheme === 'dark') {
    document.body.classList.add('dark-theme');
  } else {
    document.body.classList.remove('dark-theme');
  }
  localStorage.setItem('user-theme', newTheme); // 사용자 선택 저장
});

// `prefers-reduced-motion`을 JS로 감지하여 복잡한 애니메이션을 제어할 수도 있습니다.
const prefersReducedMotionQuery = window.matchMedia('(prefers-reduced-motion: reduce)');

function handleReducedMotionChange(event) {
  if (event.matches) {
    console.log('User prefers reduced motion. Disabling complex JS animations.');
    // JS 기반 복잡한 애니메이션 (예: 파티클 효과, 스크롤 인터랙션) 비활성화 로직
  } else {
    console.log('User does not prefer reduced motion. Enabling JS animations.');
    // JS 기반 복잡한 애니메이션 활성화 로직
  }
}

prefersReducedMotionQuery.addEventListener('change', handleReducedMotionChange);
handleReducedMotionChange(prefersReducedMotionQuery); // 초기 상태 적용