https://developer.apple.com/documentation/webkit/wknavigationdelegate
WKNavigationDelegate | Apple Developer Documentation
Methods for accepting or rejecting navigation changes, and for tracking the progress of navigation requests.
developer.apple.com
웹뷰의 탐색(네비게이션) 이벤트를 처리하는 프로토콜
페이지 로드 시작, 완료, 실패 등의 이벤트를 감지하고 제어
구현 메서드
Allowing or denying navigation requests
webView(_:decidePolicyFor:preferences:decisionHandler:)
- iOS 14부터 추가된 메서드
- 기본 decidePolicyFor 메서드와 비슷하지만, 추가로 preferences값을 조정할 수 있음
func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
preferences: WKWebpagePreferences,
decisionHandler: @escaping (WKNavigationActionPolicy, WKWebpagePreferences) -> Void) {
preferences.preferredContentMode = .desktop // 데스크톱 모드 강제 적용
decisionHandler(.allow, preferences) // 탐색 허용
}
webView(_:decidePolicyFor:decisionHandler:)
- 특정 요청을 허용할지, 취소할지 결정
- 사용자가 링크를 클릭하거나, 폼을 제출할 때 호출
- 특정 URL로 이동할지 허용(.allow) 또는 차단(.cancel)할 수 있음
- decisionHandler(.allow): 탐색 허용
- decisionHandler(.cancel): 탐색 차단
func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if let url = navigationAction.request.url?.absoluteString, url.contains("blockedsite.com") {
print("차단된 사이트로의 이동 시도: \(url)")
decisionHandler(.cancel) // 탐색 차단
} else {
decisionHandler(.allow) // 탐색 허용
}
}
webView(_:decidePolicyFor:decisionHandler:)
- 웹 탐색을 요청했을 때, 서버의 응답을 확인 후 이동을 허용할지 결정
- 웹 탐색 요청을 보낸 후, 서버 응답을 받은 후 호출됨
- 특정 응답을 허용(.allow)또는 차단(.cancel) 가능
func webView(_ webView: WKWebView,
decidePolicyFor navigationResponse: WKNavigationResponse,
decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
if let mimeType = navigationResponse.response.mimeType, mimeType.contains("application/pdf") {
print("PDF 다운로드 요청 차단")
decisionHandler(.cancel) // PDF 다운로드 차단
} else {
decisionHandler(.allow) // 탐색 허용
}
}
Tracking the load progress of a request
webView(_:didStartProvisionalNavigation:)
- 웹 페이지 로드 시작 시 호출
- 웹 페이지의 탐색이 시작되었을 때
- 네트워크 요청만 보내고, 서버의 응답을 받지 않은 상태
- 로딩 인디케이터를 표시할 수 있는 시점
func webView(
_ webView: WKWebView,
didStartProvisionalNavigation navigation: WKNavigation!
) {
print("웹 페이지 로딩 시작")
showLoadingIndicator() // 로딩 인디케이터 표시
}
webView(_:didReceiveServerRedirectForProvisionalNavigation:)
- 서버에서 리디렉션(HTTP 301, HTTP 302)이 발생했을 때 호출
- 특정 페이지로 리디렉션될 경우 감지 가능
func webView(
_ webView: WKWebView,
didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!
) {
print("서버 리디렉션 감지")
}
webView(_:didCommit:)
- 웹 콘텐츠가 표시되기 시작할 때 호출
- HTML 콘텐츠가 WKWebView에 실제로 렌더링되기 시작할 때
- HTML, CSS, JavaScript가 실행되기 시작함
func webView(
_ webView: WKWebView,
didCommit navigation: WKNavigation!
) {
print("웹 페이지 렌더링 시작")
}
webView(_:didFinish:)
- 웹 페이지가 정상적으로 로드되었을 때 호출
- 모든 리소스(HTML, CSS, JS, 이미지)가 성공적으로 로드됨
- 로딩 인디케이터를 숨길 수 있는 시점
func webView(
_ webView: WKWebView,
didFinish navigation: WKNavigation!
) {
print("웹 페이지 로드 완료")
hideLoadingIndicator() // 로딩 인디케이터 숨기기
}
Responding to authentication challenges
웹 서버나 네트워크 요청에서 인증이 필요한 경우
webView(_:didReceive:completionHandler:)
- WKWebView가 웹 페이지를 로드할 때 Basic Authentication (기본 인증), NTLM 인증, SSL/TLS 인증 문제 등이 발생하면, 다음 메서드가 호출
- 서버가 SSL/TLS 인증서 확인을 요구할 때 (NSURLAuthenticationMethodServerTrust)
- 인증서를 신뢰하도록 설정 (.useCredential)
- HTTP 기본 인증 (Basic Authentication)이 필요한 경우
- 사용자 이름과 비밀번호를 제공하여 인증
- 인증을 거부해야 하는 경우
- .cancelAuthenticationChallenge를 반환
- 서버의 인증서가 신뢰할 수 없는 경우, iOS 기본적으로 차단할 수 있음
- NSURLAuthenticationMethodServerTrust를 자동으로 승인하는 건 보안상 위험할 수 있음 (MITM 공격 위험)
func webView(_ webView: WKWebView,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
let protectionSpace = challenge.protectionSpace
// 1. SSL/TLS 인증서 관련 문제 처리
if protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
if let serverTrust = protectionSpace.serverTrust {
let credential = URLCredential(trust: serverTrust)
completionHandler(.useCredential, credential) // 인증서 신뢰
return
}
}
// 2. HTTP Basic Authentication (사용자 이름 & 비밀번호 요청)
if protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic {
let username = "testUser"
let password = "testPassword"
let credential = URLCredential(user: username, password: password, persistence: .forSession)
completionHandler(.useCredential, credential) // 입력한 자격 증명 사용
return
}
// 3. 인증 거부 (기본 설정)
completionHandler(.cancelAuthenticationChallenge, nil)
}
webView(_:authenticationChallenge:shouldAllowDeprecatedTLS:)
- 보안에 취약한 TLS 버전 대응
- 일부 웹 서버가 사용할 수도 있는 TLS1.0 또는 TLS1.1을 사용한다면 대응
- 웹 서버가 취약한 TLS 1.0, 1.2 버전을 사용할 경우 차단할지 결정
- decisionHandler(true): 해당 연결 허용
- decisionHandler(true): 연결 차단
- iOS는 기본적으로 TLS1.2 이상 요구
func webView(_ webView: WKWebView,
authenticationChallenge challenge: URLAuthenticationChallenge,
shouldAllowDeprecatedTLS decisionHandler: @escaping (Bool) -> Void) {
let protectionSpace = challenge.protectionSpace
// 1. TLS 버전 확인
if protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
if let serverTrust = protectionSpace.serverTrust {
var isTLSDeprecated = false
if #available(iOS 15.0, *) {
let secTrustResult = SecTrustEvaluateWithError(serverTrust, nil)
isTLSDeprecated = !secTrustResult
} else {
var secResult = SecTrustResultType.invalid
let status = SecTrustEvaluate(serverTrust, &secResult)
isTLSDeprecated = (status != errSecSuccess)
}
if isTLSDeprecated {
print("Deprecated TLS version detected")
decisionHandler(false) // 보안이 취약한 TLS는 차단
return
}
}
}
decisionHandler(true) // 최신 TLS 버전이면 허용
}
Responding to navigataion errors
웹 페이지를 로드하는 중에 네트워크 오류 발생 혹은 WebKit 프로세스가 강제 종료된다면 호출되는 메서드
webView(_:didFailProvisionalNavigation:withError:)
- 웹 페이지 로드가 시작되기도 전에 네트워크 문제, 잘못된 URL, DNS 오류 등으로 인해 실패했을 때 호출
- 인터넷 연결이 끊어짐, 도메인 이름이 잘못됨, SSL 인증서 오류 등 발생 시
func webView(_ webView: WKWebView,
didFailProvisionalNavigation navigation: WKNavigation?,
withError error: Error) {
print("웹 페이지 로드 실패: \(error.localizedDescription)")
}
webView(_:didFail:withError:)
- 웹 페이지를 로드하는 도중에 오류가 발생하면 호출
- 이미 일부 콘텐츠를 로드한 상태지만, 네트워크 문제, 잘못된 리소스 등으로 인해 완료되지 못했을 때 발생
- 페이지가 일부 로드된 후, 추가적인 요청(이미지, 스크립트, API 등)에서 오류가 발생할 때
- 일부 리소스가 정상적으로 로드되지 못함
- 서버에서 404 또는 500과 같은 HTTP 오류 발생
- 사용자가 페이지를 로드하는 도중 뒤로 가기 버튼을 눌러 취소
func webView(_ webView: WKWebView,
didFail navigation: WKNavigation?,
withError error: Error) {
print("웹 페이지 로드 중 오류 발생: \(error.localizedDescription)")
}
webViewWebContentProcessDidTerminate(_:)
- WebKit의 렌더링 프로세스(WebContent 프로세스)가 iOS 시스템에 의해 강제 종료되었을 때 호출
- 메모리 부족으로 인해 WKWebView가 갑자기 꺼질 때
- 메모리 부족(OOM): WKWebView는 별도의 프로세스에서 실행되는데, 메모리가 부족하면 iOS가 이를 강제 종료
- 큰 이미지나 복잡한 웹 애니메이션을 로드할 때 발생할 수 있음
- 여러 개의 WebView 인스턴스를 동시에 사용하는 경우
func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
print("WebView 프로세스가 강제 종료됨! 다시 로드합니다.")
webView.reload() // 웹뷰 강제 종료 시 자동으로 다시 로드
}
✅ WKWebView 로드 성공 시나리오
- 인터넷이 정상적으로 연결된 상태에서 WKWebView가 정상적인 페이지 로드
- webView(_:didStartProvisionalNavigation:)
- webView(_:didReceiveServerRedirectForProvisionalNavigation:) (서버 리디렉트 발생 시)
- webView(_:didCommit:)
- 콘텐츠가 로드되기 시작
- webView(_:didFinish:)
- 웹 페이지 로드 완료
❌ WKWebView 로드 실패 시나리오
네트워크가 끊긴 상태에서 페이지를 요청하는 경우
사용자가 WKWebView로 페이지를 로드하려 하지만 인터넷이 끊어진 상태
- webView(_:didStartProvisionalNavigation:)
- 페이지 로드가 시작됨
- webView(_:didFailProvisionalNavigation:withError:)
- 인터넷 끊김으로 인해 로드 시작 실패
- Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline."
존재하지 않는 URL을 요청한 경우 (잘못된 도메인)
- 존재하지 않는 도메인에 접속
- DNS 조회 실패
- webView(_:didStartProvisionalNavigation:)
- webView(_:didFailProvisionalNavigation:withError:)
- 존재하지 않는 도메인으로 인해 로드 실패
- Error Domain=NSURLErrorDomain Code=-1003 "A server with the specified hostname could not be found."
서버에서 HTTP500 에러를 반환한 경우
- 웹 서버에서 내부 서버 오류(500) 발생
- webView(_:didStartProvisionalNavigation:)
- webView(_:didCommit:)
- 일부 콘텐츠가 로드되기 시작
- webView(_:didFail:withError:)
- 서버에서 HTTP 500 오류 반환
사용자가 로딩 중 뒤로 가기 버튼을 눌러 취소하는 경우
- 페이지가 로딩되는 중간에 사용자가 다른 페이지로 이동
- 현재 페이지가 중단됨
- webView(_:didStartProvisionalNavigation:)
- webView(_:didCommit:)
- 콘텐츠가 로드되기 시작
- webView(_:didFail:withError:)
- 사용자가 중단 → Error Domain=WebKitErrorDomain Code=102 "프레임 로드 중단됨"
WebKit 프로세스가 메모리 부족(OOM)으로 종료되는 경우
- 사용자가 리소스가 많은 웹 페이지를 로드
- iOS가 WebKit 프로세스를 강제 종료
- webView(_:didStartProvisionalNavigation:)
- webView(_:didCommit:)
- 콘텐츠가 로드되기 시작
- WebKit 프로세스 강제 종료 → webViewWebContentProcessDidTerminate(_:) 호출
"프레임 로드 중단됨 (WebKitErrorDomain Code=102)" 발생 원인 및 시나리오 분석
- 오류 코드: WebKitErrorDomain Code=102
- 오류 설명: "프레임 로드 중단됨" (Frame Load Interrupted)
- 발생 URL: https:// 생략
- 추가 정보:
- _WKRecoveryAttempterErrorKey 포함 → WebKit이 복구 시도를 지원하는 경우
- NSErrorFailingURLKey, NSErrorFailingURLStringKey 포함 → 실패한 요청의 URL 표시
사용자가 WKWebView 로딩 도중 다른 요청을 보냄
- 사용자가 웹 페이지를 로딩하는 도중 다른 URL을 요청하면, WebKit이 기존 요청을 중단
- 예제 상황:
- webView.load(URLRequest(url: URL(string: "https://")!)) 실행
- 웹 페이지가 완전히 로드되기 전에
- webView.load(URLRequest(url: URL(string: "https://")!)) 실행
- 이전 로드가 중단되면서 "프레임 로드 중단됨" 오류 발생
실행 메서드 순서
- webView(_:didStartProvisionalNavigation:) (첫 번째 요청 시작)
- webView(_:didFailProvisionalNavigation:withError:) (Frame Load Interrupted 오류 발생)
- webView(_:didStartProvisionalNavigation:) (새로운 요청 시작)
서버에서 리디렉션 도중 프레임이 중단됨
- WKWebView가 요청을 보냈는데, 서버에서 리디렉션을 하면서 WebKit이 기존 프레임을 중단하는 경우
- 예제 상황:
- https:// 1번 요청
- 서버에서 302 리디렉트 → https:// 2번
- WebKit이 새로운 페이지로 이동하려고 기존 프레임을 중단
- "프레임 로드 중단됨" 오류 발생
실행 메서드 순서:
- webView(_:didStartProvisionalNavigation:) (로그인 페이지 로드 시작)
- webView(_:didReceiveServerRedirectForProvisionalNavigation:) (서버 리디렉션 감지)
- webView(_:didFailProvisionalNavigation:withError:) (Frame Load Interrupted 오류 발생)
- webView(_:didStartProvisionalNavigation:) (리디렉트된 페이지 로드 시작)
자바스크립트 또는 네이티브 코드에서 WKWebView.stopLoading() 호출
- WKWebView.stopLoading()이 실행되면 현재 로드 중이던 페이지가 강제로 중단되면서 오류가 발생할 수 있음
- 예제 상황:
- WKWebView가 페이지 로드 중
- stopLoading()이 호출됨
- "프레임 로드 중단됨" 오류 발생
실행 메서드 순서
- webView(_:didStartProvisionalNavigation:) (페이지 로드 시작)
- stopLoading() 호출됨
- webView(_:didFailProvisionalNavigation:withError:) (Frame Load Interrupted 오류 발생)
WebKit 프로세스가 강제 종료됨 (OOM, Crash 등)
- iOS가 WebKit 프로세스를 종료할 때도 발생 가능
- 특히 메모리가 부족한 경우(OOM) 또는 WKWebView 프로세스가 충돌한 경우
실행 메서드 순서:
- webView(_:didStartProvisionalNavigation:) (페이지 로드 시작)
- webViewWebContentProcessDidTerminate(_:) (WebKit 프로세스 종료)
- webView(_:didFailProvisionalNavigation:withError:) (Frame Load Interrupted 오류 발생)
대용량 리소스 로드 중단
- 원인: 대용량 리소스를 로드할 때, 메모리 부족 문제나 시간 초과로 인해 로드가 중단될 수 있습니다. 예를 들어, 이미지나 비디오 등의 대용량 파일을 로드할 때
- 상황 예시: 웹 페이지에서 비디오 파일이나 이미지 등의 대용량 리소스를 요청하고, 이 리소스가 제대로 로드되지 않아 "프레임 로드 중단됨" 오류가 발생
실행 메서드 순서:
- webView(_:didStartProvisionalNavigation:) (페이지 로드 시작)
- webView(_:didFailProvisionalNavigation:withError:) (메모리 부족 또는 타임아웃 발생)
- "프레임 로드 중단됨" 오류 발생
보안 정책 (CORS, TLS, 인증서 오류 등)
- 원인: CORS(교차 출처 리소스 공유) 정책을 위반하거나, HTTPS 인증서 오류 등이 발생할 경우
- 상황 예시: 서버에서 CORS 정책을 위반하거나 인증서가 유효하지 않아 페이지가 로드되지 않는 경우
실행 메서드 순서:
- webView(_:didStartProvisionalNavigation:) (페이지 로드 시작)
- webView(_:didFailProvisionalNavigation:withError:) (CORS 오류, 인증서 오류 등 발생)
- "프레임 로드 중단됨" 오류 발생
iOS 시스템 문제
- 원인: iOS 자체에서 시스템적인 문제나 오류가 발생할 수 있다.
- 예를 들어, iOS의 웹 엔진(WebKit)에 관련된 버그나 이슈로 인해 로드 중단이 발생
- 상황 예시: iOS 버전의 웹 엔진에서 문제가 발생한 경우, 해당 오류가 "프레임 로드 중단됨"
실행 메서드 순서:
- webView(_:didStartProvisionalNavigation:) (페이지 로드 시작)
- webView(_:didFailProvisionalNavigation:withError:) (iOS 시스템 문제 발생)
- "프레임 로드 중단됨" 오류 발생
'iOS' 카테고리의 다른 글
[iOS] App Life Cycle (0) | 2025.03.25 |
---|---|
[iOS] How to Play Video: AVFoundation, HLS, DRM, FPS, Chromecast (0) | 2025.03.23 |
[iOS] WebKit - WKScriptMessageHandler, WKScriptMessageHandlerWithReply (0) | 2025.02.22 |
[Swift] Ping 로직 (0) | 2024.05.10 |
메모리 최적화 - iOS Memory Deep Dive (1) | 2024.02.23 |