지난 글에서는 루팅 · 탈옥 탐지, 앱 무결성 확인 등에 대해 다뤘다면, 이번 글에서는 스크린샷(캡쳐)및 화면 녹화 방지 기능을 Flutter 앱에서 어떻게 구현할 수 있는지 정리해보겠다.
우선 Flutter 앱에서 화면 캡처 및 녹화 방지(Screen Security)는 안드로이드(Android)와 iOS가 작동하는 방식이 근본적으로 다르기 때문에 플랫폼별로 접근해야 한다.
특히, iOS는 단순 감지만 가능하므로, 이를 '방지'로 바꾸기 위한 특별한 트릭이 필요하다.
약간 랩같기도함 '방지'는 안되고 '감지'만 돼.. ㅈㅅ

안드로이드는 상대적으로 간단하다!
안드로이드는 FLAG_SECURE라는 강력한 보안 플래그를 제공하고, 이는 앱의 내용을 스크린샷, 화면 녹화, 그리고 앱 스위처(최근 앱 목록) 미리보기에서 완전히 차단(방지)할 수 있다.
안드로이드의 MainActivity.kt 파일에서 FLAG_SECURE 플래그를 윈도우에 추가하면, 스크린샷·화면 녹화·앱 스위처 미리보기 모두 차단된다.
setFlags와 addFlags를 사용할 수 있는데 이건 기호에 맞게 사용하면 되지 않을까..
// MainActivity.kt
import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity
import android.view.WindowManager
class MainActivity : FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// addFlags로 설정하기
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
// setFlags로 설정하기
window.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE
)
}
}
적용 후 스크린샷 촬영 시 “스크린샷을 캡처할 수 없습니다” 라는 메시지가 뜨며 방지된다
먼저 알아야 할 점은, 안드로이드처럼 FLAG_SECURE와 같은 단일 옵션이 존재하지 않기 때문에
iOS는 시스템 차원에서 앱이 스크린샷을 찍히는 것을 막을 수 없다는것이다. 진짜 대곰탕

스크린샷/녹화 이벤트 감지: iOS에서는 UIApplication.userDidTakeScreenshotNotification 또는 UIScreen.capturedDidChangeNotification을 통해 “사용자가 지금 스크린샷을 찍었다/녹화가 시작되었다”는 사실만 알 수 있다.
NotificationCenter.default.addObserver(
forName: UIApplication.userDidTakeScreenshotNotification,
object: nil,
queue: .main
) { _ in
print("스크린샷이 감지되었습니다.")
}
NotificationCenter.default.addObserver(
forName: UIScreen.capturedDidChangeNotification,
object: nil,
queue: .main
) { _ in
if UIScreen.main.isCaptured {
print("화면녹화가 감지되었습니다.")
}
}
근데 이것은 앞서 알려준 패키지에서도 충분히 가능하는 것!
다만 이 알림을 받은 후 앱 개발자가 화면을 지우는 등의 로직을 처리해야 하는데 알림을 감지하는 순간과 화면을 가리는 로직이 실행되는 순간 사이에 캡처가 발생한다.
따라서 스크린샷에는 앱 내용이 모두 노출된다!!!!
차단이 불가능하다면, “사용자 눈에는 방지된 것처럼 보이게” 우회하는 방법을 쓰기로 했다.
UITextField의 isSecureTextEntry 속성은 비밀번호 입력 시 OS 레벨에서 캡처를 차단하는 특징이 있다.
이를 앱 전체에 적용하면, 캡처 시 검은 화면 또는 지정 로고만 찍히도록 할 수 있다.

// AppDelegate.swift
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
self.window.makeSecure() //스크린샷 방지
FirebaseApp.configure()
...생략
extension UIWindow {
func makeSecure() {
DispatchQueue.main.async {
let field = UITextField()
let view = UIView(frame: CGRect(x: 0, y: 0, width: field.frame.self.width, height: field.frame.self.height))
field.isSecureTextEntry = true
self.addSubview(field)
self.layer.superlayer?.addSublayer(field.layer)
field.layer.sublayers?.last!.addSublayer(self.layer)
field.leftView = view
field.leftViewMode = .always
}
}
}
이 방식은 실제 차단은 불가능하지만, 사용자 눈에는 방지된 것처럼 보이도록 만드는 효과가 있다.
그럼 여기까지 스크린샷 녹화 및 방지 처리 끝!
다음 글에서는 앱 스위처에서 화면을 가리는 방법을 라이프사이클을 통해 구현하는 방법을 자세히 다루겠다.....
오늘도 마무리는 용용이들로...
