
로그인 페이지가 곧 데모다
GeekBye v1.7.5는 로그인 페이지에 제품 스크린샷을 추가했다가 같은 날 삭제했습니다 — 대신 진짜 컴포넌트로 다시 만들었죠. 덧붙여: OCR보다 빨랐던 사용자, 그리고 하루에 25개의 style 커밋이 실제로 어떤 모습인지.
GeekBye v1.7.5는 이중인격을 가진 한 주짜리 릴리스입니다: 37개 커밋 중 28개가 로그인 페이지 재설계이고 — 그중 25개는 style(login) 미세 조정으로, 전부 하루에 착지했습니다. 동시에 코드베이스에서 가장 교훈적인 레이스 중 하나를 고친 릴리스이기도 합니다. 두 이야기 모두 들려줄 가치가 있고, 같은 아이디어에서 만납니다: 로그인 페이지는 비로그인 사용자가 가장 먼저 보는 것이니, 제품의 사진이 아니라 제품 _그 자체_여야 한다는 것.
약 다섯 시간 동안의 스크린샷
재설계는 관례대로 시작됐습니다. 두 컬럼으로 재구성하고, 로그인을 왼쪽에 두고, 오른쪽에 제품을 — 이미지로 — 보여주기. 늦은 오전의 커밋들이 정확히 그 일을 합니다: 11:57에 진짜 프리뷰 스크린샷, 114 KB짜리 PNG가 저장소에 들어오고, 6분 뒤 한 번 업데이트됩니다.
17:04에는 사라졌습니다. 방향 전환 커밋은 정적 이미지를 344줄의 진짜 컴포넌트로 교체합니다 — 실제 오버레이 UI를 재현하는 여덟 개의 작은 조각으로 조립된 LoginDemo 패널입니다: 맥동하는 EQ 바와 정지 버튼이 달린 녹음 내비 pill, Chat/Transcript 탭이 있는 채팅 패널, 렌더링된 어시스턴트 답변(공교롭게도 React의 useEffect 설명), "Tell me more / Simplify / Show example"(더 알려줘 / 쉽게 / 예시 보여줘) 퀵 액션, 라이브 프롬프트 줄, 그리고 ⌘⏎ Assist 입력창.
"데모"라는 단어는 과장하기 쉬우니 정확히 해 둡시다: 버튼에는 진짜 호버 상태 — 글로우, 시머, 트랜지션 — 가 있지만, 클릭에 연결된 것은 아무것도 없고 입력창은 읽기 전용입니다. 샌드박스가 아닙니다. 이것의 정체는 제품의 실제 UI 어휘를 라이브로 렌더링한 것입니다: 같은 로고 에셋, 같은 그라디언트 보더와 백드롭 블러 처리, 화이트라벨 시스템이 쓰는 것과 같은 productName import — 그래서 이 데모는 이 코드베이스에서 빌드되는 각 제품에 맞춰 스스로 리브랜딩됩니다. 스크린샷은 그중 아무것도 못 합니다. 스크린샷은 찍는 그날부터 낡고, 맞지 않는 디스플레이에서는 흐릿하며, 우연히 캡처한 그 제품의 브랜드가 영구히 박혀 있습니다. 컴포넌트는 어떤 DPI에서도 선명하고, 항상 최신이며, 모든 브랜드에서 올바릅니다 — 디자인 시스템의 사진이 아니라, 디자인 시스템 _그 자체_이기 때문입니다.
참고로, 삭제된 PNG는 릴리스 태그 시점의 저장소에 아직 남아 있습니다 — 고아가 되어, 아무것도 참조하지 않는 채로. 모든 코드베이스는 패배한 접근법의 흔적 몇 개를 품고 다니는 법입니다.
하루 25개의 style 커밋은 어떤 모습인가
그 하루의 커밋 로그는 보기 드물게 정직하게 보존된 디자이너의 스케치북입니다. 컬럼 비율은 40/60이었다가 45/55가 됐습니다. 플랫폼 로고 위의 "Works with"(지원 플랫폼) 라벨은 한낮에 추가되고, 위치가 옮겨지고, 제거됐습니다 — 반나절쯤 살았죠. 플랫폼 로고 줄 자체(Google Meet, Teams, Zoom, Slack, Webex)는 아침 10:59에 추가됐다가 그날 밤 22:08에 통째로 삭제됐습니다. 그리고 푸터 정렬에는 네 가지 접근을 거친 대장정이 필요했습니다: 단일 래퍼 컨테이너, 그다음 센터링 클래스 제거, 그다음 flexbox 센터링, 그다음 그냥 바닥 쪽으로 슬쩍 내리기.
이걸 헛돈 삽질로 읽기는 쉽습니다. 우리는 이것을 디자인 작업의 실제 모양으로 읽습니다: 렌더링하고, 바라보고, 조정하면서 수렴하는 것 — 그리고 그걸 스쿼시된 "redesign login page" 하나가 아니라 25개의 작은 커밋으로 한다는 것은 탐색 과정 자체가 기록된다는 뜻입니다. 누군가 "우리 예전에 로고 줄 한번 해보지 않았어?"라고 물으면, 답은 타임스탬프와 함께 히스토리 안에 있습니다.
OCR보다 빨랐던 사용자
이 릴리스의 엔지니어링 이야기는 닷새 전, 세 커밋짜리 수정 체인에서 일어났습니다. v1.5.12부터 Cmd+H를 누르면 스크린샷이 즉각적인 피드백과 함께 큐에 들어갑니다: 썸네일은 즉시 큐에 나타나고, 텍스트 추출 — 로컬 Apple Vision OCR, 대략 1~2초 — 은 백그라운드에서 돌아갑니다. 큐 이벤트는 말 그대로 ocrText: ''인 채로 실려 나가고, 나중에 채워질 거라고 약속하는 주석이 붙어 있습니다.
좋은 최적화였고, 그리고 레이스를 하나 찍어냈습니다. 빠른 사용자는 Cmd+H를 누르고 곧바로 질문을 제출합니다. 스크린샷의 텍스트 필드는 아직 비어 있습니다. 요청은 화면 내용 없이 날아가고, 모델은 — 사실대로, 그래서 더 화가 나게 — _"I cannot see images"(이미지가 보이지 않습니다)_라고 답합니다. 순진한 구현은 에러로 실패하지 않았습니다; 잘못된 질문에 대한 올바른 답으로 실패했습니다. 소프트웨어가 실패하는 방식 중 가장 헷갈리는 방식이죠.
수정은 최적화가 미뤄둔 비동기 작업으로 제출 경로에 게이트를 겁니다: 큐에 있는 스크린샷 중 하나라도 OCR 텍스트가 아직 비어 있으면 요청은 대기하고, UI는 "Reading screen..."(화면 읽는 중) 인디케이터를 보여줍니다; 텍스트가 도착하는 순간 useEffect가 발화해 추출된 전체 내용과 함께 요청을 보냅니다. diff에는 훔쳐 갈 만한 React 특유의 디테일이 두 가지 있습니다: 스크린샷 큐는 렌더 중에 동기화되는 ref로 미러링됩니다 — 이벤트 핸들러 클로저 체인이 낡은 상태를 캡처하고 있었기 때문입니다; 그리고 대기하던 요청은 범용 큐 요청 훅을 거치지 않고 직접 전송됩니다. 같은 stale closure 이유에서요.
그리고 커밋 메시지 안에 정직한 반전이 하나 기록되어 있습니다: 이 레이스를 사냥하려 추가한 트레이싱 로그가 백엔드의 두 번째 버그를 드러낸 것입니다 — 한 코드 경로에서 프롬프트 조립이 스크린샷 텍스트를 통째로 떨어뜨리고 있었습니다. 증상은 하나, 버그는 둘. 체인 전체를 계측하고 텍스트가 어디서 사라지는지 지켜봐서 찾았습니다. 체인의 끝은 레이스 수정 9분 뒤의 제품 결정이었습니다: Cmd+H는 일괄 분석용으로 스크린샷을 큐에 넣고, Cmd+Enter는 항상 현재 화면을 새로 캡처한다 — 애매한 워크플로 하나 대신 뚜렷한 워크플로 둘. (Cmd+Enter의 Assist가 실제로 뭘 하는지는 그것대로 별도의 글입니다: 지금 보고 있는 것에 대한 즉석 AI 도움.)
같은 주의 작은 후일담 하나, 화이트라벨 챕터 독자들을 위해: Product/version (platform) User-Agent에 개발 모드 수정이 들어갔습니다. 패키징되지 않은 Electron에서 app.getVersion()은 제품 버전이 아니라 Electron의 버전을 반환하기 때문입니다. 아이덴티티 문자열은, 언제나 그렇듯, 배관에서 샙니다.
이 릴리스가 우리에게 가르쳐 준 세 가지
- 제품을 렌더링하라, 촬영하지 말고. 진짜 컴포넌트로 만든 로그인 페이지 데모는 자동으로 리브랜딩되고, 모든 디스플레이 밀도에서 살아남으며, 낡을 수 없다. 스크린샷 접근법은 시도됐다가 근무일 하루 안에 삭제됐다 — 값싼 실험, 명확한 평결.
- 모든 비동기 최적화에는 사용 지점의 게이트가 필요하다. "OCR 텍스트가 빈 채로의 즉각적 피드백"은 옳은 UX 판단이었지만, 제출 경로는 최적화가 미뤄둔 것을 기다리는 법을 배워야 했다. 작업을 미룬다면, 그 결과의 소비자를 전부 찾아내 "비어 있음"과 "아직 준비 안 됨"의 차이를 가르쳐라.
- 용의자가 아니라 체인을 계측하라. 트레이싱 로그는 스크린샷 텍스트를 끝에서 끝까지 — 단축키 핸들러에서 IPC, API 클라이언트까지 — 따라갔고, 그래서 한 번의 조사가 두 개의 버그를 닫았다. 그중 하나는 네트워크 반대편에 있었다.
v1 이야기의 이전 장은 다시 찾을 수 있는 회의(v1.7.3)를 보시고; 전체 궤적은 소프트웨어를 완벽하게 출시하는 것의 해부학을 보세요.