개요
지금까지 iOS를 부랴부랴 정리했던 것은 iOS에서 웹뷰를 사용하기 위함이였다! 그래서 iOS에서 강력하게 사용하기를 주장하는 WKWebView에 대해 정리를 해보겠다.
1. WKWebView란?
WebKit 프레임워크에 포함된 클래스로, iOS 앱 내에서 웹 콘텐츠를 보여주기 위한 뷰이다.
UIWebView
원래는 UIWebView라는 것이 있었는데...
1) 성능 문제
-> 메모리 사용량이 많고, JavaScript 실행 성능이 좋지 않음.
2) 보안 취약점
-> 보안 이슈가 존재했다고 함
3) 기능 제한
-> 최신 웹 표준을 지원하지 않거나, 네이티브 - 웹 간의 상호작용 기능이 부족함
이러한 문제들 + iOS 8에서 WKWebView를 도입하면서 UIWebView를 deprecated 시켰고,
iOS 12부터는 금지됨! 앱스토어에 UIWebView 코드가 있다면 바로 거부 때린다고 한다.
WKWebView 특징
그렇다면, WKWebView는 어떤 점이 좋아졌을까?
1) 성능 향상
-> Safari와 동일한 WebKit 엔진을 사용하기 때문에 빠르고 효율적인 JavaScript 실행 성능을 가지며, 메모리 관리도 뛰어나다.
2) 최신 웹 표준 지원
-> HTML5, CSS3, JavaScript 최신 표준을 완벽히 지원하기 때문에 복잡한 웹 애플리케이션도 표시할 수 있다.
3) 보안 강화
-> 안전한 웹 샌드박싱 환경을 제공한다.
* 웹 샌드박싱
-> 웹 콘텐츠(HTML, CSS, JavaScript)를 격리되어 있는 안전한 공간에서 실행하는 보안 모델이라고 한다.
-> 웹 페이지에서 실행되는 코드가 컴퓨터 및 스마트폰의 중요 시스템에 접근해서 문제를 일으키지 않도록 원천적으로 차단하는 기술이라고 한다.
즉, iOS 앱에서 웹을 띄우기 위해서는 WKWebView를 사용해야 한다!!
2. WKWebView 구성 요소
WKWebView에서는 여러 보조 클래스와 프로토콜이 긴밀하게 연결되어 동작한다. 하나하나씩 살펴보자
1) WKWebView
-> 화면에 웹 콘텐츠를 직접 렌더링하고 표시하는 UIView의 서브클래스이다.
-> HTML, CSS, JavaScript를 해석해 웹 페이지를 시각적으로 보여준다.
-> load(_:)를 통한 URL 로드
-> goBack(), goForward(), reload(), stopLoading() 등의 네비게이션 제어
-> scrollView 속성을 통한 스크롤 뷰 접근
2) WKUserContentController (JavaScript & Native 통신 관리)
-> JavaScript에서 보낸 메시지를 네이티브 코드가 수신, 네이티브 코드에서 웹뷰에 JavaScript를 주입해서 실행하는 중개 역할이다.
-> add(_:name:)을 통해 JavaScript에서 특정 이름으로 메시지를 보낼 핸들러(네이티브)를 등록한다.
-> addUserScript(_:)를 통해 웹뷰가 로드될 때 자동으로 실행될 JavaScript 코드를 주입한다.
3) WKNavigationDelegate (네비게이션 및 로딩 상태 관리)
-> WKWebView의 페이지 로딩 상태 변화(시작, 완료, 실패)와 웹 페이지 간의 URL 이동을 가로채고 제어할 수 있는 프로토콜 이다.
: webView(_:didStartProvisionalNavigation:)
-> 웹 로딩 시작
: webView(_:didFinish:)
-> 웹 로딩 완료
: webView(_:didFailProvisionalNavigation:withError:)
-> 웹 로딩 실패
: webView(_:decidePolicyFor:decisionHandler:)
-> 특정 URL로의 이동을 허용, 취소 할지 결정.
4) WKUIDelegate (웹뷰 UI 이벤트 처리)
-> WKWebView 내에서 발생하는 UI 관련 이벤트( alert(), confirm(), promot())를 네이티브 코드에서 처리할 수 있는 프로토콜이다.
: webView(_:createWebViewWith:for:windowFeatures:)
-> 새 창(팝업) 요청 처리.
: webView(_:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:)
-> JavaScript alert()대화 상자 처리.
: webView(_:runJavaScriptConfirmPanelWithMessage:...)
-> JavaScript confirm() 대화 상자 처리.
: webView(_:runJavaScriptTextInputPanelWithPrompt:...)
-> JavaScript prompt() 대화 상자 처리.
5) WKWebsiteDataStore (웹 데이터 관리)
-> WKWebView가 사용하는 캐시, 쿠키, 로컬 스토리지, 세션 스토리지, 웹 SQL 데이터베이스 등 웹사이트 관련 데이터를 관리한다.
이렇게 핵심 구성요소들에 대한 내용은 정리했고, 코드를 통해 보면 더 이해가 쉬울 것 같다!
3. WKWebView 코드 적용
3-1. WKWebView 인스턴스 생성 및 초기 설정
import UIKit
import WebKit // WebKit 프레임워크 임포트
class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {
var webView: WKWebView! // WKWebView 인스턴스 선언
override func viewDidLoad() {
super.viewDidLoad()
// 1. WKWebViewConfiguration 생성
let webConfiguration = WKWebViewConfiguration()
// JavaScript 활성화 (기본값은 true이지만 명시적으로 설정 가능)
webConfiguration.preferences.javaScriptEnabled = true
// 2. WKWebView 초기화
// CGRect는 웹뷰의 크기와 위치를 지정합니다.
// webConfiguration을 사용하여 초기화할 수 있습니다.
webView = WKWebView(frame: .zero, configuration: webConfiguration)
// 3. Auto Layout을 사용하여 웹뷰의 크기를 뷰 전체로 설정
// 웹뷰를 뷰 계층에 추가합니다.
view.addSubview(webView)
webView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
webView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
webView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
webView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor)
])
// 4. WKUIDelegate 및 WKNavigationDelegate 설정
// 웹뷰에서 발생하는 UI 관련 이벤트 (예: alert, confirm)를 처리합니다.
webView.uiDelegate = self
// 웹뷰의 탐색 관련 이벤트 (예: 페이지 로드 시작/완료)를 처리합니다.
webView.navigationDelegate = self
// 5. 웹 페이지 로드
// 로드할 웹 페이지의 URL을 생성합니다.
if let url = URL(string: "https://www.google.com") {
// URLRequest를 생성하여 웹뷰에 로드합니다.
let request = URLRequest(url: url)
webView.load(request)
}
// 6. JavaScript 메시지 핸들러 추가 (선택 사항)
// 웹뷰의 JavaScript 코드에서 Swift로 메시지를 보낼 수 있도록 설정합니다.
// "iOSHandler"라는 이름으로 JavaScript에서 window.webkit.messageHandlers.iOSHandler.postMessage("Hello from JS!")와 같이 호출할 수 있습니다.
webView.configuration.userContentController.add(self, name: "iOSHandler")
}
}
3-2. WKUIDelgate (UI 관련 이벤트 처리)
extension ViewController: WKUIDelegate {
// JavaScript alert() 처리
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
let alertController = UIAlertController(title: "알림", message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "확인", style: .default, handler: { _ in
completionHandler() // JavaScript alert() 동작 완료
}))
present(alertController, animated: true, completion: nil)
}
// JavaScript confirm() 처리
func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
let alertController = UIAlertController(title: "확인", message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "취소", style: .cancel, handler: { _ in
completionHandler(false) // JavaScript confirm() 동작 완료 (취소)
}))
alertController.addAction(UIAlertAction(title: "확인", style: .default, handler: { _ in
completionHandler(true) // JavaScript confirm() 동작 완료 (확인)
}))
present(alertController, animated: true, completion: nil)
}
// JavaScript prompt() 처리
func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
let alertController = UIAlertController(title: prompt, message: nil, preferredStyle: .alert)
alertController.addTextField { textField in
textField.text = defaultText // 기본 텍스트 설정
}
alertController.addAction(UIAlertAction(title: "취소", style: .cancel, handler: { _ in
completionHandler(nil) // JavaScript prompt() 동작 완료 (취소)
}))
alertController.addAction(UIAlertAction(title: "확인", style: .default, handler: { _ in
let input = alertController.textFields?.first?.text
completionHandler(input) // JavaScript prompt() 동작 완료 (입력 값 전달)
}))
present(alertController, animated: true, completion: nil)
}
}
-> WKUIDelegate 프로토콜의 메서드를 구현해 웹뷰에서 발생하는 표준 JavaScript UI 다이얼로그에 대한 응답을 iOS 네이티브를 통해 처리한다.
-> 각 메서드에서 해당 JavaScript 함수가 호출되었을 때 실행되고, completionHandler 클로저를 호출해 JavaScript 코드의 실행 흐름을 제어한다.
3-3. WKNavigationDelegate (탐색 관련 이벤트 처리)
extension ViewController: WKNavigationDelegate {
// 웹 페이지 로드를 시작하기 전에 호출됩니다. (탐색 허용/차단 결정)
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
// 특정 URL로의 이동을 허용하거나 차단할 수 있습니다.
if let url = navigationAction.request.url, url.host == "blocked-site.com" {
decisionHandler(.cancel) // 로드 취소
print("Blocked navigation to: \(url.absoluteString)")
return
}
decisionHandler(.allow) // 로드 허용
}
// 웹 페이지 로드가 시작될 때 호출됩니다. (실제 데이터 수신 시작 전)
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
print("페이지 로드 시작: \(webView.url?.absoluteString ?? "N/A")")
}
// 웹 페이지 로드가 완료되었을 때 호출됩니다.
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print("페이지 로드 완료: \(webView.url?.absoluteString ?? "N/A")")
// 웹 페이지 로드 완료 후 JavaScript 실행 예시
webView.evaluateJavaScript("document.title") { (result, error) in
if let title = result as? String {
print("현재 페이지 제목: \(title)")
}
}
}
// 웹 페이지 로드 중 오류가 발생했을 때 호출됩니다.
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
print("페이지 로드 중 오류 발생: \(error.localizedDescription)")
}
}
-> 웹뷰에서 페이지 로드, 이동 허용, 오류 발생에 대한 이벤트를 처리하는 코드이다.
3-4. WKScriptMessageHandler (JavaScript & 네이티브 통신)
extension ViewController: WKScriptMessageHandler {
// JavaScript에서 Swift로 메시지를 보낼 때 호출됩니다.
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
// "iOSHandler"라는 이름으로 설정된 메시지를 처리합니다.
if message.name == "iOSHandler" {
print("JavaScript로부터 메시지 수신: \(message.body)")
// JavaScript로 응답 보내기 예시
// 웹뷰 내의 JavaScript 함수 `receiveMessageFromiOS`를 호출하여 Swift에서 메시지를 전달합니다.
webView.evaluateJavaScript("receiveMessageFromiOS('iOS에서 메시지를 받았습니다: \(message.body)')") { (result, error) in
if let error = error {
print("JavaScript 응답 전송 중 오류: \(error.localizedDescription)")
}
}
}
}
}
일단 이렇게 예시로 정리해보고, 실제 프로젝트에 적용하면서 더 공부한 내용들을 따로 정리해보겠다!
'iOS > iOS' 카테고리의 다른 글
| [iOS] Alamofire 서버통신 (1) | 2025.06.22 |
|---|---|
| [iOS] 앱 생명주기 (2) | 2025.06.19 |
| [iOS] 코코아 & 코코아 터치 프레임워크 (2) | 2025.06.19 |