결제 내역이나 일회용 비밀번호 같은 화면을 다루다 보면, 이걸 그대로 스크린샷으로 남겨도 되나 싶은 순간이 온다. 금융·헬스케어 쪽 앱 심사에서는 아예 민감한 화면 캡처 방지를 요구하기도 한다. 직접 네이티브 모듈을 건드리자니 Android는 FLAG_SECURE, iOS는 화면 녹화 감지 API를 따로 다뤄야 해서 부담스러웠는데, expo-screen-capture가 이걸 한 번에 감싸준다.
설치
npx expo install expo-screen-capture
개발 빌드나 EAS Build가 필요하다. Expo Go에서는 네이티브 플래그가 동작하지 않으니 참고.
특정 화면에서만 캡처 막기
가장 깔끔한 방법은 훅이다. 화면이 포커스될 때 막고, 벗어나면 자동으로 푼다.
import { usePreventScreenCapture } from 'expo-screen-capture';
function PaymentScreen() {
usePreventScreenCapture();
return <ReceiptDetail />;
}
이 한 줄이면 Android에서는 스크린샷·최근 앱 미리보기·화면 녹화가 다 막히고, iOS에서는 녹화 중일 때 화면이 가려진다. 컴포넌트가 언마운트되면 알아서 해제되니까 "다른 화면 가니 캡처가 계속 막혀 있더라" 같은 실수가 안 생긴다.
전역으로 켜고 끄고 싶으면 함수형 API도 있다.
import {
preventScreenCaptureAsync,
allowScreenCaptureAsync,
} from 'expo-screen-capture';
await preventScreenCaptureAsync();
// ...민감한 작업...
await allowScreenCaptureAsync();
막지 말고 "감지"만 하기
iOS 스크린샷은 OS 차원에서 완전히 막을 수 없다. 대신 캡처가 일어난 직후에 이벤트를 받을 수 있어서, 경고를 띄우거나 서버에 로그를 남기는 식으로 대응한다. 권한을 먼저 요청해야 콜백이 온다.
import { useEffect } from 'react';
import * as ScreenCapture from 'expo-screen-capture';
import { Alert } from 'react-native';
function useScreenshotWarning() {
useEffect(() => {
let sub: ScreenCapture.Subscription;
(async () => {
const { status } = await ScreenCapture.requestPermissionsAsync();
if (status !== 'granted') return;
sub = ScreenCapture.addScreenshotListener(() => {
Alert.alert('안내', '이 화면은 캡처 공유를 삼가주세요.');
});
})();
return () => sub?.remove();
}, []);
}
정리
- 민감한 화면만 보호하고 싶으면
usePreventScreenCapture()한 줄이 제일 안전하다. 해제를 깜빡할 일이 없다. - Android는 캡처 자체를 막을 수 있지만, iOS 스크린샷은 못 막으니
addScreenshotListener로 감지해 안내하는 쪽으로 설계하자. - 리스너는 권한 요청이 선행돼야 하고, 언마운트 때
remove()로 꼭 정리할 것.
캡처를 100% 봉쇄하는 기능이 아니라, 플랫폼이 허용하는 선에서 막고 나머지는 감지해 대응하는 도구라고 생각하면 기대치가 맞다.