팀이 클라우드 Mac에서 iOS 회귀 테스트를 실행할 때, 앱이 두 개의 웹 세션을 차례로 여는 경우가 많습니다. 먼저 작업 공간 A에 로그인한 뒤 작업 공간 B로 전환하는 식입니다. B에서 A의 쿠키를 읽더라도 화면은 정상적으로 로드되는 듯 보이다가, 사용자가 계정을 전환할 때 비로소 문제가 드러날 수 있습니다. “페이지가 열리는가”만 확인하기보다 웹사이트 데이터 저장소의 경계를 반복 실행할 수 있는 테스트로 먼저 검증하는 편이 낫습니다.
무엇을 격리할지 먼저 정하기
WKWebView는 WKWebViewConfiguration.websiteDataStore를 통해 웹사이트 데이터 저장소를 선택합니다. WKWebsiteDataStore.default()는 영구 데이터에 해당하고, WKWebsiteDataStore.nonPersistent()를 호출할 때마다 임시 저장소 인스턴스를 만들 수 있습니다. 이 글에서는 먼저 서로 독립적인 두 임시 저장소 사이의 쿠키 격리를 검증합니다. 이를 전체 로그인 흐름의 안전성이 입증된 것으로 간주하지는 않습니다.
테스트를 만들기 전에 앱 코드에서 웹 뷰를 생성하는 위치를 찾아 각 뷰에 연결된 저장소를 기록하세요. 같은 계정의 여러 페이지가 세션을 공유해야 한다면 서로 독립적인 임시 저장소를 잘못 사용해서는 안 됩니다. 계정을 전환한 뒤 새로 시작해야 한다면 새 뷰가 이전 저장소를 계속 참조하지 않는지 확인해야 합니다. “어떤 페이지가 공유하고 어떤 페이지가 분리되는지”를 표로 정리하는 편이 나중에 쿠키의 출처를 추측하는 것보다 확실합니다.
여기서 검증할 대상은
WKProcessPool이 아닙니다. 프로세스 풀이 같은지만 보고 웹사이트 데이터가 공유되거나 격리된다고 단정하지 마세요. 실제로 연결된websiteDataStore를 확인해야 합니다.
외부 네트워크에 의존하지 않는 저장소 테스트 작성하기
앱의 단위 테스트 target에서 WebKit과 XCTest를 가져옵니다. 아래 테스트는 쿠키 저장소만 다루고 네트워크 요청은 보내지 않으므로 테스트 서버나 페이지 로딩 속도에 의존하지 않습니다. test.invalid는 예시 쿠키의 도메인일 뿐, 서비스 주소로 사용하지 않습니다.
import WebKit
import XCTest
final class WebsiteDataIsolationTests: XCTestCase {
func testEphemeralStoresDoNotShareCookie() {
let storeA = WKWebsiteDataStore.nonPersistent()
let storeB = WKWebsiteDataStore.nonPersistent()
let done = expectation(description: "Both stores checked")
guard let cookie = HTTPCookie(properties: [
.domain: "test.invalid",
.path: "/",
.name: "isolation_probe",
.value: "only_a"
]) else {
XCTFail("Could not create probe cookie")
return
}
storeA.httpCookieStore.setCookie(cookie) {
storeA.httpCookieStore.getAllCookies { cookiesA in
XCTAssertTrue(cookiesA.contains {
$0.name == "isolation_probe" && $0.value == "only_a"
})
storeB.httpCookieStore.getAllCookies { cookiesB in
XCTAssertFalse(cookiesB.contains {
$0.name == "isolation_probe"
})
done.fulfill()
}
}
}
wait(for: [done], timeout: 5)
}
}
먼저 A에서 방금 쓴 값을 읽을 수 있는지 확인하고, 그다음 B에서는 같은 이름의 쿠키를 읽을 수 없는지 확인합니다. 이 순서가 중요합니다. 쓰기가 끝나기 전에 B를 읽으면 두 저장소가 잘못해서 데이터를 공유하더라도 테스트가 “통과”할 수 있습니다. 시간 초과는 콜백 체인이 완료되지 않았다는 뜻이므로 실패 원인을 조사해야 합니다. 제한 시간을 무작정 늘려서는 안 됩니다.
저장소를 실제 페이지에 연결하기
저장소 계층 테스트가 통과하면 앱에서 뷰를 어떻게 생성하는지 확인합니다. 설정은 WKWebView를 초기화하기 전에 지정해야 합니다. 뷰를 만든 뒤 다른 설정 객체를 수정해도 기존 뷰의 저장소가 바뀌지는 않습니다. 페이지 테스트 준비 코드에서 대상 인스턴스를 명시적으로 전달할 수 있습니다.
let sessionStore = WKWebsiteDataStore.nonPersistent()
let configuration = WKWebViewConfiguration()
configuration.websiteDataStore = sessionStore
let webView = WKWebView(frame: .zero, configuration: configuration)
이후 통합 테스트에서는 통제 가능한 테스트 페이지를 로드해 로그인, 상태 읽기, 계정 전환, 로그아웃을 실행하고 요청과 화면 표시를 관찰해야 합니다. 저장소 API로는 테스트 쿠키가 두 인스턴스 사이에서 보이지 않는다는 점을 확인할 수 있습니다. 하지만 서버 측 세션이 무효화되었는지, 앱이 네이티브 계층에서 토큰을 페이지에 다시 주입하지 않는지까지 단독으로 입증할 수는 없습니다. 실제 요청이 필요한 테스트라면 팀에서 직접 관리하는 통제 가능한 서비스를 사용하고, 네트워크 장애와 세션 검증 결과를 구분해 기록하세요.
영구 저장소는 별도로 테스트하기
nonPersistent() 테스트가 통과했다고 해서 로그아웃 절차도 올바르다고 추론하지 마세요. 앱에서 default()를 사용한다면, 로그아웃 테스트에서 해당 영구 저장소의 관련 웹사이트 데이터가 제품 규칙에 따라 처리되는지 확인하고 웹 뷰를 새로 만들어 다시 테스트해야 합니다. 삭제 전에 삭제할 데이터의 범위를 한정하세요. 테스트를 통과시키려고 기기의 다른 페이지에서 여전히 필요할 수 있는 기록까지 무차별적으로 지워서는 안 됩니다.
클라우드 Mac에서도 재현 가능한 결과 유지하기
같은 테스트 target을 개발용 데스크톱과 자동화 작업에서 실행하고 결과를 각각 보관하세요. 실행 전에는 프로젝트에서 요구하는 Xcode, 시뮬레이터 OS 버전, 테스트 설정을 고정합니다. 여기서 특정 OS 버전이 반드시 적합하다고 가정할 필요는 없습니다. 임시 저장소는 테스트 메서드마다 새로 만들고 테스트 쿠키에는 전용 이름을 사용해 테스트 케이스끼리 상태를 빌려 쓰지 않도록 합니다.
“단독 실행은 통과하지만 전체 실행은 실패”한다면 먼저 세 가지를 확인하세요. 테스트가 모르는 사이 default()를 사용하도록 바뀌었는지, 두 웹 뷰가 같은 저장소 인스턴스를 재사용하는지, 비동기 쓰기·페이지 이동·검증이 완료 콜백에 맞춰 순서대로 실행되는지입니다. 대기 시간부터 늘려 문제를 가리지 마세요. 나중에 실패를 분석할 수 있도록 실패한 테스트 케이스 이름, 사용한 Xcode와 시뮬레이터 버전, 테스트 결과 파일을 보관하세요. 로그에는 실제 세션 값을 출력하지 마세요.
전달 전 점검 항목
| 점검 항목 | 통과 기준 |
|---|---|
| 저장소 연결 | 각 웹 뷰의 websiteDataStore가 예상한 세션과 일치함 |
| 임시 저장소 격리 | A에 쓴 테스트 쿠키가 독립적인 B에서 보이지 않음 |
| 콜백 순서 | 쓰기가 끝난 뒤 A를 읽고 마지막으로 B를 읽음 |
| 로그아웃 검증 | 영구 데이터와 서버 측 세션을 각각 제품 규칙에 따라 확인함 |
| 실패 근거 | 환경과 검증 결과를 기록하고 실제 인증 정보는 기록하지 않음 |
이 테스트는 경계 테스트로 사용할 때 가장 효과적입니다. 먼저 로컬 데이터가 임시 저장소 사이를 오가지 않는다는 점을 입증하고, 이어서 페이지 수준 테스트로 계정 전환과 로그아웃을 검증합니다. 두 단계의 결론을 따로 기록해야 실패 시 저장소 연결, 비동기 실행 순서, 앱 자체의 세션 로직 중 어디를 살펴봐야 할지 알 수 있습니다.
자주 묻는 질문
임시 데이터 저장소를 만들면 영구 쿠키도 삭제되나요?
아니요. nonPersistent()는 default()의 데이터를 정리하지 않습니다. 로그아웃 시에는 실제로 사용한 영구 저장소를 별도로 검증해야 합니다.
WKProcessPool만 분리하면 쿠키도 격리되나요?
그렇게 단정할 수 없습니다. 각 웹 뷰에 연결된 WKWebsiteDataStore를 확인하고, 필요하다면 실제 페이지 요청 결과까지 테스트하세요.
원격으로 접속할 독점 물리 Mac이 필요하신가요?
M4 Start, M4 Work, M4 Pro의 사양과 노드, 결제 주기를 비교하고 작업에 맞는 플랜을 선택하세요.