아빠가 브라우저를 여러 개 띄워 둔 이유
Voyager 전에는 한 사이트에 계정 두 개로 들어가 있으려면 브라우저도 두 개가 필요했어. 하나는 첫 번째 계정으로, 다른 하나는 두 번째 계정으로 로그인해 두고, 누구로 있을지 바꾸려면 창을 바꿨지. Voyager 집 간판은 이걸 브라우저가 없애려는 불편 목록에 올려 둬. 한 사이트에 계정 둘로 있으려고 브라우저를 여러 개 동시에 띄운다는 항목이야.
브라우저는 사이트 로그인을 쿠키와 사이트 저장소에 담아 두고, 브라우저 프로필 하나엔 그 통이 하나뿐이야. 같은 사이트에 계정이 둘이면 통도 둘이어야 해. Voyager는 이 통을 아이덴티티라고 불러. 아키텍처 불변식 7번은 규칙을 한 줄로 말해. 아이덴티티는 격리된 저장소고, 탭 하나는 그걸 정확히 하나 가진다.
작은 타입 셋
부모 타깃은 Sources/VoyagerCore/Identity.swift에 아이덴티티를 평범한 값으로 들고 있어. 파일 머리 주석의 마지막 문장은 기억해 둘 만해. 여기 있는 건 하나도 자격 증명이 아니라는 문장이야.
IdentityID는 UUID를 감싼 값이야. UUID 문자열로 인코딩되고, UUID가 아닌 건 디코딩을 거부해.Identity는 그 id에 이름과#RRGGBB색을 붙인 거야. 기본색은 회색#8A8F98.IdentityBook은identities.json파일이야. 목록, 스키마 버전, 그리고 아무도 정해 주지 않을 때 탭이 받는 기본 아이덴티티가 들어 있어. 처음 실행하면 "Main"이라는 아이덴티티 하나를 새로 만들어. 그래서 그 저장소는 빈 채로 시작해.
예약된 id가 하나 있어. IdentityID.privateSession은 어느 책에도 없는 고정 UUID야. 시크릿 모드가 이걸 쓰는데, 레슨 3에서 열어 볼 거야.
id가 곧 저장소 이름
엔진 자식은 호출 하나로 id를 WebKit 저장소로 바꿔. WKWebsiteDataStore(forIdentifier:). Apple 문서 설명은 담백해. 그 식별자로 된 저장소가 없으면 시스템이 만들어 주고, 이 메서드는 프로필용 저장소를 얻는 방법이래. 엔진은 만든 저장소를 전부 딕셔너리에 담아 둬. 그래서 같은 아이덴티티의 페이지는 전부 같은 객체를 써. 그리고 엔진이 만드는 WKWebViewConfiguration마다 websiteDataStore를 그 페이지 아이덴티티의 저장소로 정해.
격리 장치는 이게 전부야. Voyager는 쿠키를 나누는 코드도, 요청 필터도, 자기만의 프로필 폴더도 안 써. 쿠키, 로컬 스토리지, 그 밖의 사이트 데이터는 WebKit이 그 저장소를 두는 곳에 살아. 설정에 다른 저장소가 적힌 페이지는 거기 닿을 길이 없고.
| 질문 | WebKit | Chromium |
|---|---|---|
| 탭이 자기 계정을 어떻게 갖지? | 공개 API. WKWebsiteDataStore(forIdentifier:), macOS 14와 iOS 17부터 | 요청 컨텍스트를 따로 줘. 그게 프로필 통째라 확장이랑 기록도 같이 갈라져. 로그인만 떼려면 포크에 얹어 다니는 패치 묶음이 필요했어 |
| 이미 누가 쓰고 있어? | Orion, 같은 데이터 저장소 API로 | Brave, 패치 묶음을 8개월 들고 다닌 끝에 들어감 |
엔진 평가서의 이 줄이 Voyager가 WebKit 위에 선 이유 중 하나야. 나머지 이야기는 트랙 2에 있어. Firefox는 컨테이너라는 방식으로 같은 결과에 닿아.
한 사이트, 탭 둘, 아이덴티티 둘, 창은 하나. 웹뷰마다 태어날 때 다른 저장소를 받았으니까 탭마다 제 계정으로 로그인돼 있는 거야.
그걸 증명하는 테스트
실제 WebKit 테스트 묶음에 twoIdentitiesShareNothing이 있어. 페이지를 셋 열어. 둘은 아이덴티티 A, 하나는 B. 페이지 A가 쿠키 하나와 로컬 스토리지 값 하나를 써. A의 두 번째 페이지는 둘 다 봐야 해. 그건 다른 웹 콘텐츠 프로세스라서 테스트가 최대 3초 동안 다시 물어봐(50 ms 간격으로 60번). 페이지 B는 아무것도 못 봐야 해. 답은 null|. 끝으로 아이덴티티마다 저장소를 지우는데, 여기엔 재시도를 붙여. 그 저장소의 웹뷰가 하나라도 살아 있으면 WebKit이 지우기를 거부하거든. 집 간판도 이걸 함정으로 적어 뒀어. 아이덴티티의 데이터 저장소는 그 아이덴티티의 웹뷰가 하나도 안 남았을 때만 지워진다고.
같은 확인이 WebKit 관문의 W7 항목이기도 해. 창 하나의 탭 두 개에 한 사이트의 계정 두 개를 띄우고, 팝업은 자기 탭의 아이덴티티에 남는다는 항목이야. 관문은 이 테스트 묶음을 증거로 적어 뒀어. 합성 페이지로 한 테스트고, 그 줄에 실제 계정 두 개로 로그인해 본 기록은 없어. 그러니 나도 그렇게 말하진 않을 거야.
책을 못 읽을 때
id가 저장소의 유일한 이름이니까, id를 잃으면 저장소로 돌아갈 길도 잃어. Voyager는 시작할 때 이걸 지켜. identities.json을 못 읽으면 메모리에 새 책을 만들어 그걸로 돌고, 알림으로 그렇다고 말해. 알림 문구는 This run uses a fresh one and leaves the file alone, 이번 실행은 새 책으로 돌고 파일은 건드리지 않는다는 뜻이야. 그 실행 동안엔 그 파일에 절대 안 써. 망가진 파일이 옛 id를 그대로 들고 있어서 고치는 사람이 쓸 수 있어. IdentityBook 주석도 이 파일은 손으로 다시 만들어도 안전하다고 적어 뒀어.