66.1 왜 사용자 OS 설정부터 봐야 해요?
앞에서 버튼으로 다크모드를 켜고 껐죠. 그런데 생각해보면, 사용자는 이미 자기 기기에서 다크모드를 켜뒀을 수도 있어요. 폰이나 노트북 설정에서 "어두운 테마"를 골라둔 사람 많잖아요. 그런 사람이 우리 사이트에 왔는데 눈부신 하얀 화면이 뜨면, 배려가 없다는 인상을 줘요.
그래서 기본값은 사용자가 OS(운영체제)에서 이미 고른 취향을 따라가는 게 좋아요. 다행히 브라우저가 그 취향을 알려주는 창구를 열어뒀어요. CSS에선 미디어 쿼리로, 자바스크립트에선 matchMedia로 그 값을 읽을 수 있어요. 오늘은 이 시스템 설정과 손을 잡는 방법을 볼게요. 앞 편에서 만든 변수 구조 위에 그대로 얹으면 돼요.
66.2 CSS만으로 OS 다크모드를 어떻게 감지해요?
가장 간단한 방법부터요. CSS엔 사용자의 OS 테마 취향을 알아채는 미디어 쿼리가 있어요. 바로 prefers-color-scheme(선호하는 색 구성)이에요. 사용자가 OS에서 다크를 골랐다면 dark, 라이트를 골랐다면 light 값을 가져요.
상황: OS가 다크로 설정돼 있으면, 그 안의 변수 값을 어둡게 바꿔요.
@media (prefers-color-scheme: dark) {
:root {
--bg: #121212;
--text: #eaeaea;
--card: #1e1e1e;
}
}
이거 하나만 넣어도 절반은 끝나요. 사용자가 OS에서 다크를 켜둔 상태로 들어오면, 이 미디어 쿼리 안의 규칙이 켜지면서 변수 값이 어둡게 정해져요. 앞 편처럼 컴포넌트는 var()로 색을 참조만 하니까, 자바스크립트 한 줄 없이도 화면이 사용자 취향에 맞춰 어두워져요.
정말 가벼운 사이트라면 여기서 멈춰도 돼요. OS 설정을 그대로 따라가고, 굳이 사이트 안에 토글을 안 두겠다 하면 이 한 블록이 전부거든요. 하지만 사이트에 직접 전환 버튼도 두고 싶다면, 자바스크립트로 이 값을 읽어와야 해요. 그게 다음 얘기예요.
66.3 자바스크립트로는 어떻게 읽어요?
CSS 미디어 쿼리와 똑같은 질문을 자바스크립트로도 던질 수 있어요. 도구는 window.matchMedia예요. 미디어 쿼리 문자열을 넣으면, 그게 지금 맞는지를 알려주는 객체를 돌려줘요.
상황: 지금 OS가 다크 모드인지를 참거짓으로 물어봐요.
const mq = window.matchMedia('(prefers-color-scheme: dark)');
console.log(mq.matches); // OS가 다크면 true, 아니면 false
여기서 mq.matches가 핵심이에요. 이게 true면 지금 OS가 다크, false면 라이트예요. 이 값 하나로 페이지가 뜨는 순간 사용자 취향에 맞는 테마를 정해 붙일 수 있어요. 앞 편에서 만든 data-theme 방식과 연결하면 이렇게 돼요.
상황: 페이지가 열릴 때 OS 취향을 읽어 html에 테마를 붙여요.
const mq = window.matchMedia('(prefers-color-scheme: dark)');
const theme = mq.matches ? 'dark' : 'light';
document.documentElement.setAttribute('data-theme', theme);
mq.matches가 참이면 dark, 아니면 light를 html에 붙여요. 그러면 앞 편의 [data-theme="dark"] 규칙이 켜지면서 화면이 사용자 OS에 맞춰져요. CSS 미디어 쿼리와 자바스크립트, 둘 중 하나만 써도 되고 함께 써도 돼요. 버튼도 두고 싶다면 이 자바스크립트 방식이 다루기 편해요.
66.4 사용자가 OS 설정을 도중에 바꾸면요?
여기서 많이들 놓치는 지점이 있어요. 사용자가 우리 사이트를 열어둔 채로, OS 설정을 라이트에서 다크로 바꾸면 어떻게 될까요? 밤이 되면 폰이 자동으로 다크로 넘어가기도 하잖아요. 이때 페이지가 뜰 때 한 번만 읽었다면, 화면은 낮의 밝은 테마 그대로 멈춰 있어요.
그래서 한 번 읽고 끝내면 안 되고, 값이 바뀌는 걸 계속 지켜봐야 해요. 다행히 matchMedia가 돌려준 객체는 변화를 구독할 수 있어요. change 이벤트를 걸어두면, OS 설정이 바뀌는 순간 알려줘요.
상황: OS 테마가 바뀌는 순간을 구독해서, 화면을 곧바로 따라 바꿔요.
const mq = window.matchMedia('(prefers-color-scheme: dark)');
mq.addEventListener('change', (e) => {
const theme = e.matches ? 'dark' : 'light';
document.documentElement.setAttribute('data-theme', theme);
});
change가 울릴 때마다 넘어오는 e.matches가 바뀐 뒤의 상태예요. 사용자가 OS를 다크로 넘기는 순간 e.matches가 true가 되고, 우리는 data-theme을 dark로 갈아끼워요. 이렇게 구독을 걸어두면, 사용자가 폰 설정을 만지는 그 즉시 우리 화면도 스르륵 따라와요. 새로고침도 필요 없어요.
참고로 예전 브라우저는 addEventListener 대신 addListener라는 옛 방식을 썼어요. 요즘은 거의 다 addEventListener가 되니 이걸 기본으로 쓰되, 아주 오래된 환경까지 챙긴다면 옛 방식도 알아두면 좋아요.
66.5 그런데 사용자가 우리 사이트에서 고른 게 우선 아니에요?
맞아요. 바로 그 균형이 오늘의 마지막 포인트예요. 시스템 설정을 따르는 건 좋은 기본값이지만, 사용자가 우리 사이트 안에서 직접 "난 여기선 라이트로 볼래" 하고 골랐다면, 그 직접 선택이 더 우선이어야 해요. OS가 다크라고 해서 사용자의 명시적 선택을 무시하면 안 되죠.
그래서 순서를 이렇게 잡아요. 사용자가 우리 사이트에서 고른 값이 있으면 그걸 쓰고, 없을 때만 OS 취향으로 떨어져요. 사용자 선택은 다음 편에서 저장할 텐데, 미리 판단 뼈대만 그려두면 이래요.
상황: 저장된 사용자 선택이 있으면 그걸, 없으면 OS 취향을 따라요.
function resolveTheme(saved) {
if (saved === 'light' || saved === 'dark') {
return saved; // 사용자가 직접 고른 값 우선
}
const mq = window.matchMedia('(prefers-color-scheme: dark)');
return mq.matches ? 'dark' : 'light'; // 없으면 OS 취향
}
saved는 사용자가 예전에 고른 값이에요. 그게 light나 dark로 정해져 있으면 두말없이 그걸 쓰고, 비어 있을 때만 matchMedia로 OS 취향을 읽어요. 이렇게 우선순위를 잡아두면, OS 자동 전환의 편리함과 사용자 직접 선택의 존중이 부딪히지 않아요. 여기서 한 가지 더 챙기면 좋은 게, 사용자가 "시스템 따름"을 골랐을 땐 다시 OS 구독이 살아나야 한다는 점인데, 그 상태 관리가 다음 편의 주제예요.
66.6 오늘 배운 시스템 연동을 정리해볼게요
다크모드를 사용자 기기 취향과 손잡게 만들었어요.
CSS만으로는 @media (prefers-color-scheme: dark) 한 블록이면, 자바스크립트 없이도 OS 다크 설정을 따라가요. 사이트에 토글도 두고 싶다면 window.matchMedia로 그 값을 읽는데, mq.matches가 참이면 지금 OS가 다크예요. 중요한 건 한 번 읽고 끝내지 않는 것이에요. change 이벤트를 구독해두면, 사용자가 도중에 OS 설정을 바꾸는 순간 화면이 즉시 따라와요. 그리고 사용자가 사이트에서 직접 고른 값이 있으면 그게 OS 취향보다 우선이어야 하고요.
핵심은 "강요하지 않고 맞춰주는" 태도예요. 좋은 다크모드는 사용자에게 매번 고르라고 시키지 않아요. 이미 OS에 담긴 취향을 조용히 읽어 기본으로 깔아주고, 그 취향이 바뀌면 따라가고, 정 다르게 보고 싶다는 사람에겐 직접 고를 여지를 남겨요. 그 직접 고른 값을 어떻게 저장하고, 페이지가 뜰 때 깜빡임 없이 적용하는지가 마지막 걸음이에요.