티스토리 뷰

iOS/WKWebView

WKWebView Cookie 관리

[Derek] 2018. 7. 4. 16:27

iOS12부터 UIWebView가  Deprecated가 되면서  WKWebView에 대해서 정리해 보았다.


WKWebView의 가장 큰 단점은 쿠키를 직접 관리 해야한다는데에 있다.

(그리고 현재 POST 방식의 load에 이슈가 있어서 사용이 불가능하다.)


대부분의 웹앱의 경우 API 통신하는 부분과 웹의 SESSID 쿠키를 맞춰서 작업해야 한다.

SESSID가 틀리면 API 통신하는 네트워크 부분 혹은 웹에서 로그인 이슈 등이 생길 수 있다.


웹에서 자동으로 만들어지는 대부분의 쿠키는 한번 만들어지면 앱에서 수정할 수가 없다.

그래서 웹이 로드되기 전에 원하는 미리 만들고 싶은 쿠키를 넣어놔서 다시 생성 안되게 해야한다.

부득이 하게 웹에서 해당 쿠키가 변경되게 될 경우는 웹에서 messageHandler를 통해서 앱에 이런 쿠키가 바뀌었다고 알려주고 앱에서는 네트워크의 쿠키를 변경해야 한다.

(웹에서 만들어진 쿠키는 앱에서 변경이 불가능, 네크워크의 쿠키는 앱에서 변경 가능)



구글링을 해보면 Cookie를 적용하는 방법이 몇가지가 나오지만 제대로된 방법을 찾기란 너무 어렵다.



1. load 전에 URLRequest의 HTTPHeaderField에 쿠키를 넣는 방법

let properties: [HTTPCookiePropertyKey : Any] = [HTTPCookiePropertyKey.domain: ".test.co.kr",

                                                 HTTPCookiePropertyKey.name: "TEST",

                                                 HTTPCookiePropertyKey.path: "/",

                                                 HTTPCookiePropertyKey.value: "TEST"]

guard let cookie = HTTPCookie(properties: properties), let url = URL(string: "http://m.test.co.kr") else { return }


var request = URLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 20.0)

let headers = HTTPCookie.requestHeaderFields(with: [cookie])

for (name, value) in headers {

    request.addValue(value, forHTTPHeaderField: name)

}


load(request) 


간단하고 편리하지만 웹 페이지에 ajax 통신이 일어나거나 리다이렉션을 통해 다른 페이지로 넘어 갈 경우 설정한 쿠키 값이 사라진다.



2. WKNavigationDelegate의 webView(_:decidePolicyFor:decisionHandler:) 메서드에서 쿠키 넣는 방법

let headerKeys = navigationAction.request.allHTTPHeaderFields?.keys

let hasCookies = headerKeys?.contains("Cookie") ?? false


if hasCookies {

    decisionHandler(.allow)

} else {

    let cookies = HTTPCookie.requestHeaderFields(with: HTTPCookieStorage.shared.cookies ?? [])

    

    var headers = navigationAction.request.allHTTPHeaderFields ?? [:]

    for cookie in cookies {

        headers[cookie.key] = cookie.value

    }

    

    var req = navigationAction.request

    req.allHTTPHeaderFields = headers

    webView.load(req)

    

    decisionHandler(.cancel)

}


테스트 시 페이지가 제대로 열리지 않음. 내가 잘못 한 건진 모르겠지만 잘 동작 한다 하더라도 decisionHandler(.cancel)를 이용해 페이지 이동을 멈추고 새로 웹페이지를 load를 하는 방법이기 때문에 개인적으로 비추한다.



3. JavaScript를 이용해 쿠키 넣는 방법

let cookieString = "document.cookie='SESSID=TEST;path=/;domain=.test.co.kr;'"

webView.evaluateJavaScript(cookieString) 


간단하게 쿠키를 넣기에 편하지만 웹 로딩 전 미리 쿠키를 넣는게 불가능하다.

트릭으로 Window에 사이즈가 없는 WKWebView를 넣어 빈페이즈를 로드 후 해당 코드가 실행되게 하고 제거 한다음에 웹페이지를 띄워야 한다.

웹 로딩이 끝난 후에 쿠키를 추가하는 용도로는 용의해 보인다.



4. WKUserContentController를 이용해 쿠키 넣는 방법

func addUserScript(cookies: [HTTPCookie], userContentController: WKUserContentController) {

    for cookie in cookies {

        let cookieString = "document.cookie='SESSID=TEST;path=/;domain=.test.co.kr;'"

        let cookieScript = WKUserScript(source: cookieString, injectionTime: .atDocumentStart, forMainFrameOnly: false)

        userContentController.addUserScript(cookieScript)

    }

} 


쿠키를 넣는데 정석(?)이라고 생각되는 방법이다. 하지만 바로 적용되지 않고 페이지 이동 후 적용된다.

(A페이지 -> 쿠키 변경 -> B페이지 (쿠키 적용 안된) - > C페이지 (쿠키 적용됨)

이러한 단점을 보완하기 위해 Window에 사이즈가 없는 WKWebView를 넣어 빈페이지를 로드 후 사용자 눈에 안보이게 다음 페이지 이동시 쿠키가 적용 되도록 했다.



5. iOS11 이상 부터 WebSiteDataStore를 이용해 쿠키 넣는 방법

webView.configuration.websiteDataStore.httpCookieStore.setCookie(cookie) {

   // 쿠키 등록 완료 후 처리

} 


iOS11 이상 부터 WebSiteDataStore를 이용해 쿠키는 넣고 빼고를 할 수 있다.

하지만 역시 이미 웹에서 만들어진 쿠키의 변경은 안된다.



결론.

SESSID와 같이 웹에서 자동으로 생성되는 쿠키는 생성 후에 앱에서 변경이 불가능하다.

그래서 미리 API 네트워크 후 받아오는 SESSID를 웹 쿠키에 심어 줘야한다.

그리고 중간에 웹에서 쿠키가 변경되면 webView(_:decidePolicyFor:decisionHandler:) 에서 변경된 쿠키를 네트워크 쿠키에 넣어줘야한다.