화면 꺼짐을 방지해 지도, 영상, 운동/학습, 키오스크 등 장시간 상호작용이 필요한 React 앱에서 브라우저 Wake Lock API를 활용하는 방법을 안내합니다. 실무에서 바로 적용 가능한 훅과 UI 패턴, 비지원 브라우저 대안, 배포 체크리스트를 제공드립니다.
1. 지원 현황과 주의점
- 지원: Chromium 기반 브라우저(Chrome, Edge, Opera, Samsung Internet)에서 Screen Wake Lock을 지원합니다. 안드로이드와 데스크톱 일부 환경에서 동작하며 iOS Safari는 아직 미지원입니다.
- 조건: HTTPS(secure context)와 사용자 제스처(클릭 등)로 요청해야 실패율이 낮습니다.
- 수명: 탭이 백그라운드로 가거나 시스템 정책(배터리 절약 모드 등)으로 해제될 수 있어 재요청 로직이 필요합니다.
- iframe: 교차 출처 iframe에서는 Permissions-Policy로 허용돼야 합니다.
2. 최소 구현 체크리스트
- 사용자 버튼으로 활성화/해제 제공(토글 UI).
- document.visibilitychange에서 복귀 시 재요청.
- sentinel.release 이벤트 처리로 상태 동기화.
- isSupported 분기와 SSR 환경에서 안전한 navigator 검사.
- 미지원 브라우저(iOS Safari 등)는 대안 로직 적용.
3. React 훅 구현: useWakeLock
실사용 가능한 훅입니다. 사용자 제스처로 request를 호출하도록 UI에서 연결합니다.
import { useEffect, useRef, useState, useCallback } from 'react';
export function useWakeLock() {
const [isSupported, setIsSupported] = useState(false);
const [active, setActive] = useState(false);
const [error, setError] = useState(null);
const sentinelRef = useRef(null);
useEffect(() => {
setIsSupported(typeof navigator !== 'undefined' && 'wakeLock' in navigator);
}, []);
const release = useCallback(async () => {
try {
if (sentinelRef.current) {
await sentinelRef.current.release();
sentinelRef.current = null;
}
setActive(false);
} catch (e) {
setError(e);
}
}, []);
const request = useCallback(async () => {
if (!isSupported) return;
try {
// 사용자 제스처(클릭)로 호출해야 성공률이 높습니다.
const sentinel = await navigator.wakeLock.request('screen');
sentinelRef.current = sentinel;
setActive(true);
setError(null);
const onRelease = () => {
// 시스템 또는 브라우저가 해제했을 때 동기화
setActive(false);
};
sentinel.addEventListener('release', onRelease, { once: true });
} catch (e) {
setError(e);
setActive(false);
}
}, [isSupported]);
useEffect(() => {
const onVisibilityChange = () => {
// 탭 복귀 시 활성 상태면 재요청
if (document.visibilityState === 'visible' && active) {
request().catch(() => {});
}
};
document.addEventListener('visibilitychange', onVisibilityChange);
return () => {
document.removeEventListener('visibilitychange', onVisibilityChange);
};
}, [active, request]);
useEffect(() => () => { release(); }, [release]);
return { isSupported, active, error, request, release };
}
4. 예시 컴포넌트: 토글 버튼 UI
사용자 제스처로 request를 호출하고 상태를 안내합니다.
import React from 'react';
import { useWakeLock } from './useWakeLock';
export function KeepScreenAwake() {
const { isSupported, active, error, request, release } = useWakeLock();
const handleToggle = async () => {
if (active) {
await release();
} else {
await request();
}
};
return (
<div>
<button onClick={handleToggle} disabled={!isSupported}>
{active ? '화면 깨우기 해제' : '화면 꺼짐 방지 활성화'}
</button>
{!isSupported && <p>브라우저가 Wake Lock API를 지원하지 않습니다.</p>}
{error && <p>오류: {String(error.message || error)}</p>}
{active && <p>활성화되었습니다. 장시간 작업을 안전하게 진행하세요.</p>}
</div>
);
}
5. 비지원 브라우저(iOS Safari 등) 대안
완벽하진 않지만, 무음/인라인 loop 비디오를 재생해 화면 꺼짐을 늦추는 방법이 있습니다. 사용자 경험에 미치는 영향을 고려해 조건부로만 사용하세요.
import { useEffect, useRef, useState } from 'react';
function isIOS() {
if (typeof navigator === 'undefined') return false;
return /iPhone|iPad|iPod/i.test(navigator.userAgent);
}
export function useIOSWakeFallback() {
const videoRef = useRef(null);
const [active, setActive] = useState(false);
const enable = async () => {
if (!isIOS() || active) return;
const v = document.createElement('video');
v.setAttribute('playsinline', '');
v.muted = true;
v.loop = true;
v.src = 'data:video/mp4;base64,AAAA'; // 아주 짧은 무음 비디오 소스 제공 필요
v.style.position = 'fixed';
v.style.width = '1px';
v.style.height = '1px';
v.style.opacity = '0';
document.body.appendChild(v);
videoRef.current = v;
try {
await v.play();
setActive(true);
} catch (e) {
console.warn('iOS fallback 실패', e);
}
};
const disable = async () => {
const v = videoRef.current;
if (!v) return;
try { v.pause(); } catch {}
v.remove();
videoRef.current = null;
setActive(false);
};
useEffect(() => () => { disable(); }, []);
return { active, enable, disable };
}
6. 통합: 지원 여부에 따라 Wake Lock 또는 대안
브라우저 지원에 따라 우선적으로 Wake Lock을 사용하고, iOS 등에서는 대안을 활성화합니다.
import React from 'react';
import { useWakeLock } from './useWakeLock';
import { useIOSWakeFallback } from './useIOSWakeFallback';
export function ScreenAwakeToggle() {
const { isSupported, active, request, release } = useWakeLock();
const ios = useIOSWakeFallback();
const enabled = isSupported ? active : ios.active;
const onClick = async () => {
if (enabled) {
isSupported ? await release() : await ios.disable();
} else {
isSupported ? await request() : await ios.enable();
}
};
return (
<button onClick={onClick}>
{enabled ? '화면 깨우기 해제' : '화면 꺼짐 방지 활성화'}
</button>
);
}
7. 서버 설정: Permissions-Policy로 iframe 허용
교차 출처 iframe에서 Wake Lock을 사용하려면 서버에서 권한 정책을 설정합니다.
// Express 예시
app.use((req, res, next) => {
res.setHeader('Permissions-Policy', 'screen-wake-lock=(self)');
next();
});
8. PWA 및 모바일 고려사항
- PWA 설치 상태든 브라우저 탭이든 동일하게 사용자 제스처가 중요합니다.
- 배터리 절약 모드, 저전력 상태, 화면 밝기 제한 등 운영체제 정책이 우선할 수 있습니다.
- 앱이 백그라운드로 가면 Wake Lock이 해제될 수 있어 복귀 시 재요청 로직이 필요합니다.
- 긴 세션에서는 배터리 안내 문구와 손쉬운 해제 버튼을 제공하세요.
9. 에러 처리와 로깅 패턴
- NotAllowedError: 사용자 제스처 없이 호출했을 가능성이 높습니다. 버튼 클릭과 연결하세요.
- AbortError: 시스템 정책으로 거부. 사용자에게 배터리 설정을 안내합니다.
- isSupported false: 기능 안내와 대안을 제시하세요.
- 로그: wakeLock_request, wakeLock_release, wakeLock_error 이벤트를 분석에 기록해 UX를 개선합니다.
10. 테스트 체크리스트
- Android Chrome 실제 기기에서 화면 꺼짐 방지 확인.
- 탭 전환 후 복귀 시 재요청 동작 확인.
- 배터리 절약 모드에서 거부/해제 처리 확인.
- iOS Safari에서 대안 적용 및 UX 문제 없는지 확인.
- iframe, HTTPS, 서버 헤더 설정 검증.
11. 결론
Wake Lock API는 React 앱에서 화면 꺼짐을 방지하는 가장 표준화된 방법입니다. 사용자 제스처 기반 토글, 가시성 복귀 재요청, 명확한 상태 안내, 미지원 브라우저 대안을 함께 구성하면 실무 품질을 확보할 수 있습니다. 위 훅과 예시 컴포넌트를 프로젝트에 적용해 장시간 상호작용 UX를 개선하세요.
'React' 카테고리의 다른 글
| React 앱에서 폴더 구조 자동화와 절대 경로(alias) 설정하기 (0) | 2026.07.27 |
|---|---|
| React에서 실시간 협업 에디터 구현하기 (0) | 2026.07.27 |
| React에서 OpenAPI 기반 API 클라이언트 자동 생성하기 (1) | 2026.07.26 |
| React 앱에서 OTP(일회용 비밀번호) 입력 UI 구현하기 (0) | 2026.07.24 |
| React에서 Canvas 기반 이미지 크롭 편집기 구현하기 (0) | 2026.07.24 |