시작은 한 문장
2026-10-03, On Air 대화에서 아빠가 문제를 한 줄로 짚었어.
나를 기억하지 못하는 브라우저들이라고, 이게.
그 대화가 coop 할 일 #303이 됐고, 그 할 일이 Voyager가 됐어. 아빠만의 웹 브라우저. 아키텍처 문서는 이 불평을 명제로 바꿔놨어. '다른 브라우저는 전부 모두를 위해 만들어서 누구도 모른다. 이 브라우저는 한 사람을 위해 만들어서 그 사람을 기억한다.' 이름은 1977년에 떠나서 아직도 아무도 가본 적 없는 곳을 날고 있는 탐사선들에서 따왔어.
이 퀘스트는 그 브라우저가 어떻게 지어졌는지를 따라가. 그래도 WebKit 얘기는 나중이고, 시작은 여기야. 뒤에 나오는 공학적 선택은 전부 이 명제를 위한 거라서, 뭘 위한 선택인지 모르면 잘 골랐는지도 판단할 수 없거든.
잊어버리는 브라우저가 치르게 하는 값
모두를 위해 만든 브라우저는 네가 다니는 사이트 중 어느 게 네 건지 몰라. 그러니 네 세계에 관한 사실은 전부 손으로 챙겨야 해. 그리고 손으로 챙기는 건 결국 썩어. Voyager 집 간판(CLAUDE.md)에 아빠 경우가 적혀 있어.
- 고정 탭 서른 개와 가족 북마크 한 줄. 전부 손으로 관리했어.
- 14개 앱에서 멈춘 북마크 파일. 그동안 가족 앱은 45개쯤으로 늘었어. 업데이트를 그만두자고 정한 사람은 없어. 새 앱이 생길 때마다 손이 한 번씩 더 가야 했는데, 어느 순간 그 손이 안 갔을 뿐이야.
- 아빠 자기 마이크인데도 뜨는 권한 창. 아빠 기계에서 피파 음성 모드가 쓰는 바로 그 마이크인데도 권한을 물어.
- 동시에 띄워둔 브라우저 여러 개. 아빠한테는 계정 두 개를 동시에 써야 하는 사이트가 있어. 그런데 브라우저는 프로필 하나에 쿠키를 한 벌만 둬서, 창 하나에서는 한 사이트를 한 계정으로만 볼 수 있어. Chrome이나 Safari 프로필로 두 번째 계정을 들 수는 있지만, 프로필마다 탭이 따로 노는 창이 따로 떠. 그래서 실제로는 두 번째 계정이 아예 다른 브라우저에 가 있었던 거야.
넷 다 같은 실패를 다른 쪽에서 본 거야. 브라우저가 아빠 것과 남의 것을 구분 못 해. 그러니 가족 앱은 아무 웹사이트 취급이고, 아빠 기계는 아무 출처 취급이고, 두 번째 계정은 첫 번째 계정 자리에 끼어든 침입자 취급이지.
여기서 기억한다는 건
"기억한다"는 게 더 큰 북마크 파일을 뜻하진 않아. Voyager에서 기억한다는 건 이런 거야. 사실마다 그걸 가진 곳이 딱 한 군데 있고, 필요한 순간에 거기서 읽어 와.
| 불편 | Voyager가 대신 하는 일 | 이 퀘스트에서 다루는 곳 |
|---|---|---|
| 손으로 챙기는 고정 탭과 북마크 | Firelink 실행 카탈로그를 실행할 때 읽어서 네이티브 Home에 보여줘 | 가족 앱은 이름으로, 신뢰는 구역으로 |
| 자기 마이크에 뜨는 권한 창 | 정확히 확인되는 가족 출처에는 마이크와 카메라를 묻지 않고 주고, 다른 사이트에는 여전히 물어봐 | 가족 앱은 이름으로, 신뢰는 구역으로 |
| 계정 두 개 때문에 브라우저 여러 개 | 아이덴티티마다 격리된 WebKit 데이터 저장소를 따로 줘. 그래서 창 하나 안에서도 탭마다 다른 계정을 써 | 한 사이트, 여러 계정, 나란히 |
| 탭이나 확장 패널에 갇힌 피파 | 피파 본인은 고정 탭에 들어앉고, 피파 패널은 어느 페이지 옆에든 붙어 | 피파가 들어 있는 브라우저 |
아키텍처는 첫 줄을 불변식으로 박아뒀어. 가족에 관한 사실은 읽어 오지, 베끼지 않는다. 앱, 포트, 호스트 목록은 Voyager 소스에도, 설정에도, 문서에도 없어. 이유는 14개에서 멈춘 그 파일이야. 사본은 만든 날부터 낡기 시작하거든.
처음 보게 되는 화면
새 탭을 열면 Voyager 네이티브 Home, voyager://home이 떠. 뒤에 웹 페이지가 없는 SwiftUI 화면이야. 맨 위엔 Open Pippa 버튼이 있어. 그 아래는 계정 바로가기야. 바로가기마다 정해둔 아이덴티티로 사이트 하나를 열어. 이어서 가족 앱 격자가 나와. 카탈로그를 보고 그리는데, 앱 개수와 마지막으로 읽은 시각이 붙어 있고 이름으로 찾는 칸도 있어. 맨 아래엔 그 창에 열린 탭들이 와.
더 가기 전에 정직하게 하나 짚을게. 아키텍처는 주소창에 가족 앱 이름을 넣으면 열린다고 써놨어. 그건 아직 안 만들었어. 지금 가족 앱을 이름으로 찾으려면 Home의 그 칸에 치면 되고, iPhone에서는 위젯을 누르면 돼. 문서가 코드보다 앞서 나간 곳에서는 이 퀘스트가 코드를 따르고, 앞서 나갔다고 밝혀둘게.
아빠가 세운 기준
한 사람용 브라우저는 작은 브라우저가 되기 쉬워. 가족 쪽 일은 잘하는데 나머지는 아빠가 이미 쓰던 것보다 못한 거. 아빠는 같은 대화에서 그 길을 막았어.
불완전한 브라우저를 만들면 안 돼. 크롬 브라우저든지, 사파리 브라우저든지, 코멧이든지, 그거의 수퍼셋이어야 하는 거지, 섭셋이면 안 되는 거야.
성공이 어떤 모습인지도 말했어.
우리 브라우저가 너무 좋아서 Comet이나 Chrome으로 돌아갈 일이 없어야 하는 거지.
그래서 성공 기준은 행동이고, 한 줄이야. 아빠가 다른 브라우저를 다시 안 여는 것. 아빠의 글쓰기 편집기 Rekindle도 Bear와 Sublime Text를 상대로 같은 종류의 기준을 넘었어. 이런 기준은 기능 목록으로는 통과할 수 없어. 재는 도구가 있어야 해. Voyager한테 그 도구는 이탈 기록이야. 다른 브라우저가 앞으로 나올 때마다 Voyager가 적어둬. 아빠가 정한 기간 동안 기록이 비어 있으면 끝난 거야. 이 트랙 마지막 두 레슨에서 이 기록을 뜯어볼 거야.
작은 핵심부터, 그다음엔 기본 브라우저 전부
아빠가 정한 순서는 이랬어. 계획 세션을 먼저 하고, 쓸 만한 작은 핵심을 만들고, 거기서 상위 집합까지 키워 가기. 계획 세션은 그날 밤 바로 돌았고 엔진까지 바꿨어. 그 얘기는 다음 트랙에서 해. 첫 벽돌이 놓이자 아빠는 같은 날 더 밀어붙였어.
조금조금 하지말고 기본적인 브라우져 기능은 (comet 기준) 필수적으로 다 구현하고 테스트해. prototype v1 이 목표야. 그러고 나서 다듬어.
그래서 v1 프로토타입은 방문 기록, 북마크, 추천, 시크릿 창, 아이덴티티를 그날 하루 안에 다 갖췄어. 상위 집합이라는 기준과 이탈 기록이 함께 있으면, 반쪽짜리 브라우저가 얼마나 비싼지 눈에 보여. 기본 기능이 하나 빠질 때마다 Comet으로 한 번 돌아가게 되고, 그게 전부 기록에 한 줄씩 남으니까.