Steven
Steven5 분 소요

CSS 변수 하나, 다섯 번의 코드 리뷰, 그리고 거짓말을 한 Swift 툴체인

GeekBye v2.0.7은 오버레이 전체를 조절 가능한 반투명으로 만들었습니다 — 한 줄짜리 CSS 변경처럼 들리지만 절대 그렇지 않았습니다. 진짜 이야기는 코드 리뷰가 잡아낸 것들입니다: 흐려지기를 거부한 두 개의 표면, 같은 불투명도인데도 잘못 보이던 녹음 바, 그리고 우리 자신의 문서가 리뷰어에게 잘못된 Swift 버전을 알려준 탓에 672바이트가 왔다 갔다 한 컴파일된 바이너리.

엔지니어링
디자인
빌드
GeekBye 릴리스
CSS 변수 하나, 다섯 번의 코드 리뷰, 그리고 거짓말을 한 Swift 툴체인

"오버레이를 반투명하게 만들라"는 것은 슬라이더 하나와 CSS opacity 속성처럼 들리는 종류의 작업입니다. GeekBye v2.0.7은 사용자에게 보이는 그 결과물을 정확히 출시했습니다 — 어시스턴트 패널 뒤에 무엇이 있든 그것이 비쳐 보이게 해주는, 조절 가능한 단일 "라이트 모드"입니다. 하지만 이번 릴리스의 흥미로운 부분은 기능이 아닙니다. 그것을 제대로 만들기까지 걸린 다섯 번의 리뷰, 그리고 우리 자신의 문서가 리뷰어에게 거짓말을 한 탓에 크기가 바뀐 컴파일된 바이너리에 관한 무용담입니다.

UI 전체를 반투명하게 만드는 올바른 방법

이것의 순진한 버전은 모든 패널 컴포넌트에 불투명도 값을 뿌려대고 끝났다고 부르는 것입니다. 그것은 유지보수의 함정입니다: 누군가가 하나를 건드리는 순간 서로 어긋나기 시작하는, 열두 개의 하드코딩된 값들이죠.

대신 v2.0.7은 공유된 CSS 유틸리티 하나와 표면별 토큰을 사용합니다. 두 개의 커스텀 속성으로부터 패널의 배경을 계산하는 단일 overlay-surface 클래스가 있습니다: 각 표면이 스스로 선언하는 기본 불투명도(컨트롤 바, 채팅, 트랜스크립트 말풍선, 코드 블록 등이 각자 자기 것을 고릅니다), 그리고 오버레이 루트에 실려 사용자의 슬라이더 설정을 나타내는 전역 알파 배율. 모든 표면은 같은 배율을 읽으며, 각자는 자신의 개성을 유지합니다. 결정적으로, 패널 배경만 배율이 적용됩니다 — 텍스트, 아이콘, backdrop-blur는 완전히 렌더링된 채로 유지되므로, "반투명"이 결코 "읽을 수 없음"을 뜻하지 않습니다.

그 설계의 두 가지 결정은 짚어둘 만합니다. 도움이 되는 기능과 지원 티켓을 만들어내는 기능의 차이가 바로 여기에 있기 때문입니다:

  • 기본값은 완전 불투명입니다. 불투명도 설정은 100%로 출시됩니다 — 아무 동작도 하지 않는 값이죠. 업데이트했다고 해서 누군가의 오버레이가 갑자기 비쳐 보이게 되지 않았습니다. 반투명은 당신이 그것을 찾으러 갈 때 거기 있고, 그전까지는 보이지 않습니다.
  • 설정은 방어적으로 검증됩니다. 최솟값, 최댓값, 기본값, 스텝에 대한 단 하나의 진실의 원천이 있고, 비어 있거나 누락된 경우를 막아주는 정규화기가 있습니다. 이것이 중요한 이유는 JavaScript의 함정 때문입니다: Number(null)0이므로, 누락된 설정을 순진하게 강제 변환하면 오버레이가 기본값이 아니라 가장 투명한 상태로 튀어버립니다. 정규화기가 그런 일이 벌어지기 전에 이를 잡아냅니다.

슬라이더를 끌면 오버레이 전체가 녹음 도중에도 실시간으로 다시 색조를 입습니다. 모든 표면이 같은 설정을 구독하고 있기 때문입니다. 재시작도, 리로드도 없습니다.

리뷰가 잡아낸 것 (그리고 단일 모니터 테스트라면 결코 잡지 못했을 것)

바로 여기서 "CSS 변수 하나"가 더 이상 단순하지 않게 됩니다. 리뷰에서 세 개의 버그가 드러났고, 그중 둘은 서로 다른 탈을 쓴 같은 교훈입니다.

흐려지지 않으려 한 패널. 다른 모든 것이 반투명해지는 동안 키보드 단축키 오버레이만 고집스럽게 불투명하게 남아 있었습니다. 원인은 이것입니다: 그것은 불투명도 변수를 실어 나르는 DOM 하위 트리 바깥에서 렌더링되므로, 오버레이 루트에서 아래로 흘러내리는 값을 결코 상속받지 못했습니다. CSS 커스텀 속성은 DOM 트리를 통해 상속됩니다 — 그리고 어떤 노드가 실제로 당신이 변수를 설정한 곳의 자손이 아니라면, 그것은 조용히 아무것도 받지 못합니다. 해결책은 그 패널에도 변수를 로컬로 설정하는 것이었습니다.

사라진 호버 색조. Fn/Assist 버튼이 청록색 호버 광채를 잃었습니다. 반투명 유틸리티는 !important로 배경을 설정했고(표면 기본값을 이겨야 하니까요), 그것이 같은 요소의 hover: 배경 색조를 밀어붙여 짓밟았습니다. 특이성 충돌입니다. 해결책은 호버 색조를 똑같이 강조해서 호버 시 다시 이길 수 있게 만드는 것이었습니다.

이 둘은 모두 같은 근본 진실입니다: CSS 변수의 캐스케이드는 스타일링의 세부사항이 아니라 진짜 정확성의 경계입니다. 상속과 특이성이 당신의 값이 요소에 도달하기라도 하는지를 결정하며, 둘 중 어느 것도 잘못되기 전까지는 스크린샷에 나타나지 않습니다.

같은 불투명도, 다른 모습. 가장 미묘한 것: 녹음 바와 대기 바는 동일한 기본 불투명도로 설정되었는데도 눈에 띄게 다르게 보였습니다. 왜일까요? 녹음 알약은 더 무거운 backdrop-blur를 사용했고, 어두운 배경 위의 강한 블러는 더 어두운 결과로 평균이 나므로 — 같은 알파가 다른 외관을 만들어냈습니다. 또 녹음 바는 희미한 테두리를 가진 반면 대기 바는 밝은 그러데이션 링 가장자리를 가지고 있었습니다. 해결책은 블러 정도와 링을 맞추는 것이었습니다. 교훈은 확실히 남았습니다: 블러가 그림에 들어오는 순간 같은 알파는 같은 외관이 아닙니다. 반투명은 단일한 숫자가 아니라 상호작용하는 효과들의 시스템입니다.

문서가 거짓말을 해서 크기가 바뀐 바이너리

이제 무용담입니다. 제가 가장 좋아하는 종류죠 — 툴링과 문서가 서로 어긋나고, 꼼꼼한 리뷰어가 그 십자포화에 휘말리는 이야기 말입니다.

GeekBye는 일부 사전 컴파일된 Swift 바이너리를 함께 배포합니다(회의 감지 같은 것을 위한 네이티브 헬퍼들이죠). 컴파일된 바이너리가 저장소에 체크인되어 있기 때문에, 모든 개발자는 정확히 같은 Swift 버전으로 그것을 컴파일해야 합니다 — 서로 다른 툴체인은 동일한 소스에서 다른 바이트를 만들어내고, 그것은 누군가가 다시 빌드할 때마다 유령 같은 git 충돌을 의미하게 됩니다. 그래서 하나의 특정 버전을 강제하는 스크립트가 있습니다: 빌드 시점에 확인되는 Swift 6.3.x입니다.

이번 릴리스의 리뷰 중, 한 리뷰어가 어떤 바이너리를 "저장소가 규정한 툴체인"에 맞춰 다시 빌드해야 한다고 지적했습니다. 개발자는 정확히 그렇게 했습니다 — 그리고 바이너리의 크기가 89,184바이트에서 88,512바이트로 바뀌었습니다. 커밋 메시지는 심지어 Swift 6.2.1, "저장소가 규정한 툴체인"으로 다시 빌드함으로써 "바이트 안정적 출력을 복원한다"고까지 말했습니다.

다만 6.2.1은 잘못된 버전이었습니다. 빌드 강제 스크립트는 6.3.x를 요구했습니다. 문제는 산문 문서 — README와 기여자 가이드 — 가 몇 군데에서 여전히 6.2.1이라고 말하고 있었다는 것입니다. 그래서 양심적인 리뷰어와 양심적인 개발자가, 둘 다 옳은 일을 하려다가, 문서를 따라 곧장 강제 스크립트가 거부하려고 존재하는 바로 그 산출물을 커밋하는 데로 들어갔습니다. 6.3.x 저장소 안의 6.2.1 바이너리는, 다른 모든 개발자의 깨끗한 재빌드를 망가뜨릴 바로 그 어긋남입니다.

다음 커밋이 그것을 고쳤습니다 — 그리고 그 수정이 핵심입니다. 그것은 단지 바이너리를 6.3.x 형태로 되돌리는 데 그치지 않았습니다(크기가 89,184로, 시작했던 바로 그 자리로 복원됨). 그것은 또한 함정이 다시 발동될 수 없도록 문서의 낡은 "6.2.1" 참조를 바로잡았습니다. 바이너리는 두 커밋에 걸쳐 672바이트를 오갔다 되돌아왔고, 진짜 버그는 결코 바이너리가 아니었습니다. 그것은 두 개의 진실의 원천 — 강제 스크립트와 작성된 문서 — 이 서로 어긋났고, 사람이 잘못된 쪽을 믿었다는 것이었습니다.

이 릴리스가 가르쳐준 세 가지

  1. CSS 변수의 캐스케이드는 정확성의 경계입니다. 세 리뷰 버그 중 둘은 상속되지 않은 변수와 호버 상태를 짓밟은 !important로 귀결되었습니다. 어떤 값이 상속을 통해 요소에 도달해야 하거나 특이성을 통해 이겨야 한다면, 그 경로는 로직입니다 — 스타일링이 아니라 로직처럼 검증하세요.
  2. 같은 매개변수가 다른 결과를 낳을 수 있습니다. 녹음 바와 대기 바는 불투명도를 공유했는데도 다른 블러가 결과를 바꿨기 때문에 다르게 보였습니다. 시각 시스템을 하나의 숫자로 통일할 때는, 각 표면 주변의 다른 변수들이 그 숫자의 의미를 조용히 바꾸고 있지 않은지 확인하세요.
  3. 문서와 강제가 어긋날 때는, 증상이 아니라 그 어긋남을 고치세요. 버그는 나쁜 바이너리가 아니었습니다. 그것은 잘못된 툴체인을 지목한 문서와 다르게 말하는 강제 스크립트였습니다. 바이너리를 다시 빌드하는 것은 증상을 다루는 일입니다. 모든 진실의 원천이 일치하게 만드는 것이 진짜 수정입니다. 다음 리뷰어도 문서를 믿을 것이기 때문입니다.

GeekBye v2.0.7은 모든 예외 경우가 리뷰되고 툴체인 문서가 바로잡힌 채로, 균일한 반투명을 출시했습니다. 그것은 뒤이은 신뢰성 스프린트 전, "조용한 장인정신" 릴리스들의 마지막입니다. 그 아래에 놓인 토대에 대해서는 버전 2에 정말로 필요한 것(v2.0.0)을 보세요. 형제 격인 다듬기 릴리스에 대해서는 차분한 소프트웨어: 깜빡임 수정과 답변 모드 칩(v2.0.3 + v2.0.5)을, 그리고 통화 중에 오버레이가 숨겨진 채로 유지되는 방법에 대해서는 화면 공유 중 보이지 않게 유지하는 법을 보세요.

관련 글

차분한 소프트웨어: 깜빡이는 토글을 없애고, 채팅에게 지금 어떤 모드인지 말하게 하기
Steven
Steven5 분 소요

차분한 소프트웨어: 깜빡이는 토글을 없애고, 채팅에게 지금 어떤 모드인지 말하게 하기

GeekBye의 자잘한 두 릴리스, 대표 기능은 없습니다 — 그저 더 이상 깜빡이지 않게 된 설정 토글, 그리고 회의 모드로 답했는지 코딩 모드로 답했는지 드디어 알려주는 채팅 어시스턴트뿐입니다. 이것이 "차분한 소프트웨어"에 실제로 드는 비용입니다, 디테일 하나씩.

제품
디자인
엔지니어링
소프트웨어를 완벽하게 출시하는 해부학: 테스트가 놓친 것을 코드 리뷰가 잡아낸 방법
Steven
Steven5 분 소요

소프트웨어를 완벽하게 출시하는 해부학: 테스트가 놓친 것을 코드 리뷰가 잡아낸 방법

GeekBye v2 시리즈 전반에 걸쳐 똑같은 일이 계속 벌어졌습니다. 개발자의 머신에서는 모든 테스트를 통과한 수정이, 코드 리뷰에 이르러서는 거의 모든 사용자에게 실패했을 것이라는 사실이 증명됩니다. 이것은 아홉 번의 릴리스 뒤에 있었던 작업 흐름 — 리뷰 게이트, 수정보다 먼저 잡아내는 진단, 그리고 "내 컴퓨터에선 되는데"를 "된다"로 바꿔놓는 출시 전 테스트 규율 — 의 이야기입니다.

엔지니어링
프로세스
코드 리뷰
Mac 앱이 마이크 권한을 받고도 실행할 때마다 잊어버리는 이유
Steven
Steven5 분 소요

Mac 앱이 마이크 권한을 받고도 실행할 때마다 잊어버리는 이유

GeekBye가 마이크 권한을 요청했고, 당신은 허용했고, 잘 작동했습니다. 그런데 다음 실행: 사라졌습니다. 게다가 앱은 시스템 설정 → 마이크 목록에 아예 나타나지도 않았습니다. 범인은 사라지는 경로에서 앱을 조용히 실행하던 macOS 보안 기능이었습니다 — 여기 그 진단과 한 번의 프롬프트로 끝나는 해결책이 있습니다.

macOS
권한
엔지니어링