풀스크린(전체화면)은 미디어 감상, 프레젠테이션, 데이터 대시보드 등 몰입형 UX에 필수입니다. React에서 브라우저 Fullscreen API를 안전하게 제어하는 방법과 실무 팁을 정리합니다.
1. Fullscreen API 개요
핵심 메서드와 속성입니다.
- element.requestFullscreen(options?)
- document.exitFullscreen()
- document.fullscreenElement
- fullscreenchange, fullscreenerror 이벤트
주의: 사용자 제스처(클릭 등) 안에서만 진입이 가능합니다. iOS Safari는 일반 요소에 대해 Fullscreen API를 지원하지 않고, video 요소에 제한적으로 동작합니다.
2. 가장 빠른 토글 예제
특정 컨테이너를 전체화면으로 전환/해제하는 기본 React 컴포넌트입니다.
import React, { useRef, useState } from "react";
export default function FullscreenBox() {
const boxRef = useRef(null);
const [isFullscreen, setIsFullscreen] = useState(false);
const enter = async () => {
const el = boxRef.current;
if (!el) return;
// 벤더 프리픽스 대응
const req = el.requestFullscreen || el.webkitRequestFullscreen || el.msRequestFullscreen;
if (!req) {
alert("이 브라우저는 Fullscreen API를 지원하지 않습니다.");
return;
}
try {
await req.call(el, { navigationUI: "hide" }); // 지원 브라우저에서 상단 UI 숨김
} catch (e) {
console.error("fullscreen enter error", e);
}
};
const exit = async () => {
const doc = document;
const ex = doc.exitFullscreen || doc.webkitExitFullscreen || doc.msExitFullscreen;
if (!ex) return;
try {
await ex.call(doc);
} catch (e) {
console.error("fullscreen exit error", e);
}
};
const toggle = () => {
const doc = document;
const cur = doc.fullscreenElement || doc.webkitFullscreenElement || doc.msFullscreenElement;
if (cur) exit();
else enter();
};
React.useEffect(() => {
const onChange = () => {
const cur = document.fullscreenElement || document.webkitFullscreenElement || document.msFullscreenElement;
setIsFullscreen(Boolean(cur));
};
document.addEventListener("fullscreenchange", onChange);
document.addEventListener("webkitfullscreenchange", onChange);
document.addEventListener("MSFullscreenChange", onChange);
return () => {
document.removeEventListener("fullscreenchange", onChange);
document.removeEventListener("webkitfullscreenchange", onChange);
document.removeEventListener("MSFullscreenChange", onChange);
};
}, []);
return (
<div>
<div
ref={boxRef}
role="region"
aria-label="전체화면 영역"
style={{
width: 320,
height: 180,
background: "#111",
color: "#fff",
display: "flex",
alignItems: "center",
justifyContent: "center",
borderRadius: 8,
}}
>
{isFullscreen ? "전체화면 모드" : "일반 모드"}
</div>
<button onClick={toggle} aria-pressed={isFullscreen} style={{ marginTop: 12 }}>
{isFullscreen ? "전체화면 종료" : "전체화면 진입"}
</button>
</div>
);
}
3. 재사용 가능한 커스텀 훅: useFullscreen
컴포넌트 복잡도를 줄이기 위해 훅으로 캡슐화합니다. onChange 콜백으로 상태 변화를 감지할 수 있습니다.
import { useCallback, useEffect, useState } from "react";
function getFullscreenElement(doc) {
return doc.fullscreenElement || doc.webkitFullscreenElement || doc.msFullscreenElement || null;
}
function getRequest(el) {
return el.requestFullscreen || el.webkitRequestFullscreen || el.msRequestFullscreen;
}
function getExit(doc) {
return doc.exitFullscreen || doc.webkitExitFullscreen || doc.msExitFullscreen;
}
export function useFullscreen(targetRef, options = {}) {
const { onChange } = options;
const [isFullscreen, setIsFullscreen] = useState(false);
const enter = useCallback(async (opts) => {
if (typeof document === "undefined") return; // SSR 안전장치
const el = (targetRef && targetRef.current) ? targetRef.current : document.documentElement;
const req = el ? getRequest(el) : null;
if (!req) {
console.warn("Fullscreen API not supported");
return;
}
try {
await req.call(el, opts);
} catch (e) {
console.error("enter fullscreen failed", e);
}
}, [targetRef]);
const exit = useCallback(async () => {
if (typeof document === "undefined") return;
const ex = getExit(document);
if (!ex) return;
try {
await ex.call(document);
} catch (e) {
console.error("exit fullscreen failed", e);
}
}, []);
const toggle = useCallback(() => {
const cur = typeof document !== "undefined" ? getFullscreenElement(document) : null;
if (cur) exit(); else enter({ navigationUI: "hide" });
}, [enter, exit]);
useEffect(() => {
if (typeof document === "undefined") return;
const handler = () => {
const active = Boolean(getFullscreenElement(document));
setIsFullscreen(active);
if (typeof onChange === "function") onChange(active);
};
const errHandler = (e) => console.error("fullscreen error", e);
document.addEventListener("fullscreenchange", handler);
document.addEventListener("webkitfullscreenchange", handler);
document.addEventListener("MSFullscreenChange", handler);
document.addEventListener("fullscreenerror", errHandler);
return () => {
document.removeEventListener("fullscreenchange", handler);
document.removeEventListener("webkitfullscreenchange", handler);
document.removeEventListener("MSFullscreenChange", handler);
document.removeEventListener("fullscreenerror", errHandler);
};
}, [onChange]);
return { isFullscreen, enter, exit, toggle };
}
// 사용 예시
import React, { useRef } from "react";
export function GalleryFullscreen() {
const ref = useRef(null);
const { isFullscreen, toggle, exit } = useFullscreen(ref, {
onChange: (active) => console.log("fullscreen:", active),
});
return (
<section>
<div ref={ref} style={{ width: 640, height: 360, background: "#222", color: "#fff" }}>
<p style={{ textAlign: "center", paddingTop: 140 }}>이미지/그래프/지도 영역</p>
</div>
<div style={{ marginTop: 12 }}>
<button onClick={toggle}>{isFullscreen ? "종료" : "전체화면"}</button>
{isFullscreen && <button onClick={exit} style={{ marginLeft: 8 }}>나가기</button>}
</div>
</section>
);
}
4. 오류 처리와 브라우저 제약
- 사용자 제스처 의존: 클릭/키보드 이벤트 핸들러 안에서 requestFullscreen을 호출해야 거절되지 않습니다.
- iOS Safari: video 요소 외 일반 요소는 미지원입니다. 필요 시 <video playsInline> 정책, 또는 대체 레이아웃을 제공하세요.
- 보안 컨텍스트: 일부 기능은 HTTPS에서만 동작합니다.
- 프레임 내 전체화면: iframe에 allow="fullscreen" 속성이 필요합니다.
// iframe 예시 (React)
<iframe src="https://example.com" allow="fullscreen" style={{ width: "100%", height: 400 }} />
5. 접근성/UX 가이드
- 명확한 토글 버튼 레이블: 전체화면 진입/종료를 상태 기반으로 바꿉니다.
- 키보드: 스크린리더 사용자도 인지할 수 있도록 aria-pressed, aria-label을 사용합니다.
- 포커스 관리: 전체화면 진입 후 주요 컨트롤에 포커스를 이동하면 접근성이 좋아집니다.
- ESC 안내: 대부분 브라우저는 ESC로 종료됩니다. 툴팁/토스트로 안내하세요.
6. Next.js/SSR 주의
서버 렌더링 환경에서는 document가 없습니다. 훅/핸들러에서 typeof document 체크를 넣어야 합니다. 또한 전체화면 관련 코드는 클라이언트 사이드에서만 실행되도록 보장하세요.
// 안전 가드 예시
if (typeof document === "undefined") {
// SSR: 아무 것도 하지 않음
}
7. 테스트 체크리스트
- 데스크톱: Chrome, Edge, Firefox, Safari 동작 확인
- 모바일: Android Chrome(OK), iOS Safari(제약) 동작 확인
- ESC/제스처 종료 시 상태 동기화 여부
- iframe 내부/외부 권한 및 이벤트 동작
8. 자주 겪는 문제 해결
- 버튼 눌러도 안 됨: 클릭 핸들러 안에서 호출했는지 확인, 팝업 차단/자동재생 정책 영향 확인
- 상태가 꼬임: fullscreenchange 이벤트 기반으로 상태를 동기화하세요. DOM만 믿지 말고 이벤트를 청취합니다.
- 스타일 깨짐: 전체화면 시 크기 강제. 컨테이너에 width/height 100% 처리 및 스크롤 대응 필요
9. 확장 팁
- 옵션 사용: requestFullscreen({ navigationUI: 'hide' })로 상단 UI 숨김(지원 브라우저 한정).
- 비디오/지도: 미디어 컨트롤 옆에 전체화면 버튼 노출. iOS는 video 전용 전체화면을 고려.
- 가로모드 UX: 전체화면 진입 시 화면 회전 안내(Orientation API는 권한/지원 이슈 존재).
10. 마무리
Fullscreen API는 간단해 보이지만 브라우저/플랫폼 제약이 많습니다. 재사용 가능한 useFullscreen 훅으로 상태와 이벤트를 일관되게 관리하고, iOS 등 예외 케이스를 대비하면 안정적인 UX를 제공할 수 있습니다.
'React' 카테고리의 다른 글
| React 앱에서 사용자 입력 데이터 자동 저장(Auto Save) 기능 구현하기 (0) | 2026.06.22 |
|---|---|
| React 앱에서 지연 로딩(Lazy Loading) 비디오 구현하기 (0) | 2026.06.19 |
| React에서 클라이언트 사이드 데이터 암호화/복호화 처리하기 (0) | 2026.06.19 |
| React 앱에서 Device Orientation API 활용하기 (0) | 2026.06.18 |
| React에서 스크롤 기반 Parallax 효과 구현하기 (0) | 2026.06.18 |