[iOS] 웹뷰(WebView)기반 앱 개발 시 주의할 점

2025. 1. 30. 12:10iOS

반응형

iOS 앱을 웹뷰(WebView) 기반으로 만들 때 신경 써야 할 주요 사항들을 정리해봅니다.


1. WKWebView vs UIWebView

  • UIWebView는 iOS 12부터 deprecated 되었고, iOS 14부터는 완전히 제거되었습니다.
  • WKWebView는 성능, 보안, 메모리 관리 측면에서 훨씬 뛰어나므로 반드시 WKWebView를 사용해야 합니다.

2. 네이티브와 웹 간의 통신

웹과 iOS 네이티브 코드가 데이터를 주고받아야 할 경우, WKWebView의 WKScriptMessageHandler를 사용해 JavaScript와 Swift 간의 브릿지를 만들어야 합니다.

2.1. 웹에서 네이티브로 데이터 전달

웹에서 window.webkit.messageHandlers.핸들러이름.postMessage(data)를 호출하면 네이티브에서 받을 수 있습니다.

class WebViewController: UIViewController, WKScriptMessageHandler {
    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let contentController = WKUserContentController()
        contentController.add(self, name: "nativeHandler")

        let config = WKWebViewConfiguration()
        config.userContentController = contentController
        
        webView = WKWebView(frame: view.bounds, configuration: config)
        view.addSubview(webView)

        let url = URL(string: "https://example.com")!
        webView.load(URLRequest(url: url))
    }

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if message.name == "nativeHandler", let body = message.body as? String {
            print("웹에서 받은 메시지: \(body)")
        }
    }
}

2.2. 네이티브에서 웹으로 데이터 전달

네이티브에서 웹의 JavaScript를 실행하려면 evaluateJavaScript를 사용하면 됩니다.

let script = "window.someJavascriptFunction('Hello from Swift!')"
webView.evaluateJavaScript(script, completionHandler: nil)

3. 파일 업로드 / 다운로드

3.1. 파일 업로드 지원

WKWebView는 기본적으로 파일 업로드 기능이 제한적이므로, UIDocumentPickerViewController를 사용해서 파일을 선택한 후 JavaScript에 전달하는 방식으로 구현해야 합니다.

let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: [.image, .pdf, .plainText])
documentPicker.delegate = self
present(documentPicker, animated: true)

3.2. 파일 다운로드 처리

파일 다운로드를 지원하려면 WKNavigationDelegate의 decidePolicyFor에서 직접 다운로드 요청을 감지하고 처리해야 합니다.

func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
    if let url = navigationResponse.response.url, let mimeType = navigationResponse.response.mimeType {
        if mimeType.contains("application/pdf") { 
            downloadFile(from: url)
            decisionHandler(.cancel)
            return
        }
    }
    decisionHandler(.allow)
}

4. 쿠키 / 세션 유지

웹뷰는 기본적으로 WKWebsiteDataStore를 이용해서 쿠키와 세션을 관리하지만, 쿠키를 직접 조작하려면 HTTPCookieStorage와 WKHTTPCookieStore를 활용해야 합니다.

4.1. 웹에서 쿠키 가져오기

if let cookies = HTTPCookieStorage.shared.cookies {
    for cookie in cookies {
        print("\(cookie.name): \(cookie.value)")
    }
}

4.2. 네이티브에서 쿠키를 웹에 주입하기

let script = "document.cookie = 'sessionId=abc123';"
webView.evaluateJavaScript(script, completionHandler: nil)

5. iOS 백그라운드 세션 유지

웹뷰는 앱이 백그라운드로 가면 세션이 끊길 수 있습니다. 이를 방지하려면 앱이 백그라운드에서 돌아왔을 때 WKWebView를 다시 로드하거나, 쿠키를 저장하고 복원하는 방식으로 해결해야 합니다.

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    webView.reload()
}

6. 네트워크 연결 상태 확인

웹이 정상적으로 로드되지 않거나 인터넷이 끊길 경우, 사용자가 알 수 있도록 네트워크 상태를 감지해야 합니다.

let monitor = NWPathMonitor()
let queue = DispatchQueue.global(qos: .background)

monitor.pathUpdateHandler = { path in
    if path.status == .satisfied {
        print("인터넷 연결됨")
    } else {
        print("인터넷 연결 안됨")
    }
}
monitor.start(queue: queue)

7. WebView 캐시 관리

캐시를 삭제해야 하는 경우 WKWebsiteDataStore.default()를 사용해서 삭제할 수 있습니다.

let websiteDataTypes = WKWebsiteDataStore.allWebsiteDataTypes()
WKWebsiteDataStore.default().removeData(ofTypes: websiteDataTypes, modifiedSince: Date.distantPast) {
    print("웹뷰 캐시 삭제 완료")
}

8. 다크모드 대응

웹뷰가 다크모드에서 올바르게 보이도록 하려면 CSS와 WKWebView 설정을 신경 써야 합니다.

if traitCollection.userInterfaceStyle == .dark {
    let script = "document.body.style.backgroundColor = 'black'; document.body.style.color = 'white';"
    webView.evaluateJavaScript(script, completionHandler: nil)
}

9. 보안 고려사항

9.1. HTTP 대신 HTTPS 사용

iOS에서는 보안 정책(ATS, App Transport Security) 때문에 HTTPS가 아닌 HTTP는 기본적으로 차단됩니다. 만약 HTTP 요청을 사용해야 한다면 Info.plist에 예외 설정을 추가해야 합니다.

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

하지만 보안 문제 때문에 가능하면 HTTPS를 사용하는 게 좋습니다.

9.2. 자바스크립트 실행 제한

웹뷰에서 악성 JavaScript가 실행되지 않도록 WKPreferences에서 javaScriptEnabled를 적절히 설정해야 합니다.

let preferences = WKPreferences()
preferences.javaScriptEnabled = false
config.preferences = preferences

9.3. 사용자 데이터 보호

웹뷰에서 민감한 데이터를 다룬다면, Private Browsing Mode(비공개 모드)를 사용할 수도 있습니다.

config.websiteDataStore = WKWebsiteDataStore.nonPersistent()

10. 뒤로 가기 / 앞으로 가기 기능

웹뷰 내에서 사용자가 뒤로 가기 버튼을 누를 수 있도록 네비게이션 기능을 구현할 수 있습니다.

@objc func goBack() {
    if webView.canGoBack {
        webView.goBack()
    }
}

@objc func goForward() {
    if webView.canGoForward {
        webView.goForward()
    }
}

11. 웹뷰 퍼포먼스 최적화

웹뷰가 무거워지는 것을 방지하려면 다음을 고려해야 합니다.

  • WKProcessPool 공유: 여러 개의 WKWebView를 사용할 때 세션을 공유할 수 있음.
  • 메모리 사용량 모니터링: 너무 많은 메모리를 사용할 경우 강제로 reload 호출.
  • 불필요한 자바스크립트 줄이기: WKUserScript로 최소화된 스크립트만 로드.

결론

웹뷰 기반 앱을 만들 때는 성능, 보안, 네이티브 연동 등을 신경 써야 해. 특히 WKWebView를 올바르게 활용하고, 웹과의 데이터 통신 및 세션 관리에 주의하면 더 좋은 사용자 경험을 제공할 수 있습니다. 🚀

반응형

'iOS' 카테고리의 다른 글

[Swift] Optional  (0) 2025.02.01
[iOS] HTTPCookieStorage와 WKHTTPCookieStore 차이  (0) 2025.01.30
[iOS] SwiftUI와 Combine (2)  (0) 2025.01.28
[iOS] SwiftUI와 Combine  (0) 2025.01.28
[iOS] SwiftUI와 MVVM  (0) 2025.01.27