말하기 쉬운 단어
"Chrome, Safari, Comet의 상위 집합"이 아빠가 세운 기준이야. 그런데 이런 주장은 슬그머니 거짓이 되기 쉬워. Voyager는 WebKit 위에서 돌고, WebKit은 그 자체로 Chrome의 상위 집합이 아니야. 어떤 Chrome 기능은 거기 아예 없어. 아키텍처는 설계 얘기 꺼내기도 전에 두 번째 절에서 그걸 인정해. Chrome엔 있는데 WebKit엔 없는 게 있으면, 그 빈틈은 패리티 계약에 이름을 올려. 그다음엔 셸에서 메우거나, 네이티브로 잇거나, 아빠가 받아들여. 숨기는 일은 절대 없어.
이름 붙이는 일을 맡은 문서가 패리티 계약이야. 짧은 문서인데, 거의 모든 부분이 한 가지 일을 해. "상위 집합"이라는 말이 사실보다 앞서 나가지 못하게 막는 거.
정답지 하나, 도구 둘
정답지는 Comet이야. 아빠가 사는 브라우저니까. Chrome과 Safari는 보조 정답지고, 셋 다 실행 중에 기대는 대상은 아니야. 계약은 이 정답지에 대고 도구 두 개를 써. 둘을 어떻게 나눴는지가 곧 설계야.
- P0 표는 아빠한테 Voyager에서 살아보라고 하기 전에 이미 돼 있어야 하는 것들이야. 기능 목록에서 뽑은 게 아니라 아빠 Mac에서 나온 증거로 그렸어. Comet 프로필에 깔린 확장, 거기 등록된 네이티브 메시징 호스트, 아빠가 공유한 스크린샷, 그리고 그 대화에서 아빠가 한 말.
- 이탈 기록이 그 뒤를 전부 맡아. 다음 단계의 줄은 기록에서만 들어와.
계약은 두 번째 도구를 한 문장으로 줄여. '아무도 떠나지 않은 기능은 일이 아니다.' 상위 집합이라는 목표가 Chrome을 끝없이 다시 만드는 일로 번지지 않게 막는 게 이 문장이야. 기준은 아빠한테 필요한 전부고, 그게 뭔지는 Voyager가 기록을 보고 알아내.
P0 한 줄은 이렇게 생겼어
P0 줄마다 칸이 셋이야. 영역, 사례, 그리고 그걸 증명하는 증거. 영역부터 볼까. 자격 증명, 확장, 다시 켜도 유지되는 로그인이 있어. 미디어는 소리, 전체 화면, PIP, 그리고 재생도 되고 내려받기도 되는 H.264/AAC 파일이야. 입력은 모든 칸에서의 한글 조합, 그리고 탭이 띄울 수 있는 모든 화면에서 먹히는 Chrome 단축키야. 기본기는 다운로드, 업로드, 찾기, 사이트별로 기억하는 확대, PDF로 인쇄, 팝업, HTTP 인증, 인증서 오류고. 그 밖에 가족 쪽, 피파, 아이덴티티, 시스템(다른 앱에서 누른 링크가 Voyager에서 열리는 것), 그리고 가벼움까지.
쓸모 있는 건 증거 칸이야. "한글 입력 됨"은 주장이야. "손으로 쳐봄, 줄 끝에서의 조합 포함"은 다른 사람이 돌려볼 수 있는 시험이고. 계약의 증거 규칙은 여기서 더 조여.
- 사례는
/Applications/Voyager.app에서 통과해야 해. 디버그 빌드는 안 돼. - 사례를 건너뛴 실행은 통과가 아니야. 건너뛴 건 이름을 적어.
- 어떤 macOS 버전에서 통과한 사례는 macOS 업데이트 뒤에 다시 돌려. 엔진이 시스템이랑 같이 바뀌니까.
마지막 규칙은 엔진 선택에서 곧장 나와. 엔진이 운영체제랑 같이 배달되면, OS 업데이트가 곧 네가 일정 잡지 않은 엔진 업데이트야.
P0 사례가 실패하면 계약이 어떻게 하는지 보여주는 줄이 하나 있어. 첫 줄, 1Password가 데스크톱 앱을 거쳐 로그인을 채우는 사례야. 계약은 그 옆에 대비책을 써뒀어. '1Password가 우리 브라우저에서 연결이 안 되면 뭐면 충분한지 아빠가 정하고, 그 줄은 아빠가 정한 대로 다시 쓴다.' 그 줄이 지금 정직하게 어떤 상태인지는 뒤 트랙에서 다뤄.
빈틈마다 이유와 답
이 레슨의 핵심은 계약의 엔진 한계 표야. 줄마다 WebKit이 못 하는 것 하나, 그 이유, 그리고 Voyager의 답. 엔진 평가 문서의 "WebKit에 없는 것" 목록과 같이 읽으면, 아키텍처가 말한 답 세 가지가 실제로 다 나와.
| 답 | 예 |
|---|---|
| 셸에서 메움 | WebKit엔 공개된 탭 음소거가 없어. 그래서 Voyager 자기 스크립트가 모든 프레임에서 페이지의 미디어 요소를 음소거해. WebKit의 찾기는 일치하는 게 있는지만 알려줘. 그래서 "3 of 17" 같은 개수는 페이지에 보이는 텍스트로 직접 세. |
| 네이티브로 이음 | Web MIDI는 WebKit에 없는데 cwkBonfire가 그걸 써. 그 가족 페이지 하나를 위한 네이티브 다리가 선택지 하나야. 판정은 아직 열려 있어. |
| 아빠가 받아들임 | iPhone과 iPad에선 Apple의 웹 브라우저 자격이 없으면 가족 웹 앱의 서비스 워커가 안 돌아. 아빠는 받아들였어. 네트워크가 없으면 어차피 집 서버에 못 닿으니까. |
표 끝엔 문서 전체의 요점을 쥔 문장이 있어. '이것들을 여기 적어두는 건, "상위 집합"을 이 선 너머로는 주장하지 않으려는 거다.' 그리고 그중 하나 때문에 이탈이 생기기 시작하면, 엔진 평가에 남겨둔 선택지를 다시 열 이유가 돼. 기존 Chromium 브라우저에 Voyager 기능을 붙이고 같은 Swift 핵심을 다시 쓰는 길이야. 이름 붙은 빈틈은 자기 비상구까지 들고 다니는 셈이지.
폰은 다른 정답지에 대고 재
iPhone과 iPad 앱은 Mac의 Chrome이 아니라 폰의 Safari에 대고 재. 빈틈 표도 따로 있어. 가족 앱 서비스 워커, 패스키, 기본 브라우저 지정, 확장 호스트, 다운로드 목록이 폰에선 안 돼. 그중 앞의 셋을 메워 줄 자격은 2026-10-26에 포기했어. Apple은 공개 TestFlight 링크와 외부 Beta App Review를 거쳐야만 그 자격을 주는데, 가족 앱은 거기 안 가거든.
아니, 근데 이거 다 디폴트 브라우져 자격 얻는데 필요한 깐깐한 심사 아니야? 디폴트로 안 쓰면 그만이잖아? 이걸 외부 베타 테스트 심사까지 받는다고? 그건 하면 안 돼.
치르는 값을 적어둔 채로 받아들인 빈틈이야. 계약이 바라는 게 딱 그거고.
절대 안 함, 이유와 함께
마지막 표는 Voyager가 안 만들 것들이야. 묶어 오거나 고친 엔진, 아빠 판정 없는 비공개 WebKit 인터페이스, 두 번째 피파, 손으로 챙기는 가족 앱 목록, 그리고 사이트한테 Voyager 정체를 조각조각 숨기는 우회. 줄마다 이유가 붙어 있어. 줄을 바꾸려면 아빠 판정이 있어야 하고, 그 질문을 꺼내게 만든 이탈도 옆에 적어. 이유 없는 "절대"는 편견이야. 이유 있는 "절대"는 다음 세션이 확인해볼 수 있는 결정이고.
빈틈은 문서에만 사는 게 아니야. 코드에도 있어. 주소 해석기 머리말엔 Phase 2 puts a family app's name in front of both (the launch catalog)라고 적혀 있어. 가족 앱 이름(실행 카탈로그)을 그 둘보다 먼저 보는 건 2단계 일이라는 뜻이야. 지금 resolve(_:)가 아는 건 스킴, 호스트, 검색 템플릿뿐이고 그 밖엔 아무것도 몰라. 파일을 여는 사람이면 누구나 그 빈틈을 봐.