건너뛰게 되는 사실
이 집의 다른 클라이언트는 전부 파일을 복사해서 나가. 웹 UI 는 엔진이 서빙하는 빌드 디렉터리고, 맥 앱들은 서명된 번들을 자리에 복사하는 거고. iOS 는 마지막 단계가 내 것이 아닌 유일한 플랫폼이야. 빌드랑 기기 사이에 애플이 앉아 있고, 그게 "끝났다" 의 뜻을 바꿔.
가족 규칙은 한 줄이야. TestFlight 가 배송이고, 케이블 설치는 빌드 점검이야. 케이블 빌드는 일주일이면 만료되고, 케이블이랑 잠금 풀린 기기가 있어야 하고, 폰 딱 한 대에 닿아. 그건 이게 돌아가는지 알아보는 방법이지 누가 받는 방법이 아니야.
이게 적혀 있는 건 비싸게 배웠기 때문이야. 형제 앱에서, 집안에 이미 돌아가는 업로드 파이프라인이 생긴 지 열이틀 뒤에. 어떤 세션이 작업을 끝내고 아빠의 잠긴 폰에 옆으로 설치해놓고, 진짜 배송 경로는 나중에 할 일로 적어놨어. 아빠가 직접 짚어줘야 했고.
사실이 넷, 배송은 마지막 하나뿐
계약서가 넷에 이름을 붙여놔서 상태 보고가 하나로 뭉칠 수 없게 돼 있어.
archive 를 내보내서 업로드했다 — 내 스크립트가 그렇게 말해. 애플이 처리를 끝냈다 — 스토어 콘솔이 그렇게 말하고, 한참 걸릴 수도 있어. 내부 그룹이 그 빌드를 가졌다 — 별개의 배정이고 별개의 화면이야. 기기에 설치됐다 — 기기가 그렇게 말하거나 테스터 행이 그렇게 말해.
서로 다른 참이 넷이야. 첫 번째만 참인데 "출시했다" 고 쓰는 보고는 일부러 거짓말하는 게 아니야. 네 단계짜리 과정을 한 단계짜리 압축하듯이 압축한 거지. 그래서 규칙은 넷 중 어느 게 참인지를 말하는 거야.
나중에 오는 메일 대신, 거부하는 게이트
업로드 스크립트는 대부분 거부야. 그리고 모든 거부에 같은 기원 설화가 있어. 안 그러면 애플이 업로드를 받아준 다음 몇 시간 뒤에 메일을 보내.
순서가 곧 설계야. 싼 거부는 비싼 일이 돌기 전에 전부 끝나고, 그 전부가 빌드 번호가 움직이기 전에 끝나. 맨 먼저 App Store Connect 키. 키가 없으면 화면이 잠겨 있을 때 업로드를 거부해. 잠긴 화면은 archive 에 필요한 keychain 세션을 죽이거든. 그다음 전용 시뮬레이터. 테스트가 남들 쓰는 시뮬레이터에 떨어지면 안 되니까. 그다음 앱의 입력이 커밋돼 있는지. 그래야 빌드 번호가 실제로 빌드되는 걸 가리켜. 공용 kit 소스가 깨끗한지도 봐. archive 가 그것도 같이 컴파일하니까. 포장된 renderer 가 있는지, 아이콘이 크기 맞고 불투명한지까지 보고 나서야 스위트가 돌고, 스위트가 통과해야 빌드 번호가 움직여.
archive 가 나오면 HEAD 를 다시 확인해 — 빌드하는 동안 형제 세션이 커밋했을 수 있으니까. 그다음 archive 자체를 들여다봐. 앱 아이콘 이름이 선언됐는지, 내장된 워치 앱 것도 그런지, 워치의 컴파일된 에셋 카탈로그가 있는지, complication 확장이 진짜로 거기 있는지.
마지막 묶음이 재밌는 쪽이야. 입력이 아니라 출력 에 대한 단언이거든 — 빌드는 성공했는데 산출물이 틀린 상황. 컴파일이 초록이라는 건 번들 내용물에 대한 증거가 아니야.
프로젝트 파일은 생성물, 그리고 트리는 깨끗해야 해
보이는 것보다 중요한 작은 선택 둘.
Xcode 프로젝트는 평범한 텍스트 소스에서 생성해서 커밋해. IDE 에서 프로젝트를 손으로 고치는 건 팀 식별자나 서명 설정이 리뷰에서 아무도 안 읽는 파일에 들어앉는 길이고, 그러면 다음 기계는 미묘하게 다른 걸 빌드해. 텍스트 파일이 소스고, 프로젝트는 마침 추적되고 있을 뿐인 산출물이야.
그리고 깨끗한 트리 게이트는 위생 문제가 아니라 archive 가 실제로 뭘 컴파일하느냐의 문제야. 마지막 커밋이 아니라 작업 트리를 컴파일하거든. 더러운 파일 하나가 지금 출시하려는 빌드에 조용히 섞여 들어가. 공유 체크아웃에 형제 세션이 남기고 간 파일이어도 마찬가지고. 그러면 그 빌드는 거기 찍어둔 커밋으로 영영 재현이 안 돼.
이미 값을 치른 것들
앱마다 새로 만드는 건 하나도 없어. 개발자 팀 하나가 가족 전체를 지고, 그건 IDE 에서 클릭하는 게 아니라 각 프로젝트의 텍스트 소스에 이름으로 적혀. 새 앱한테 필요한 건 스토어 기록 하나, 그게 다야.
그리고 2026-09-14부터는 파이프라인 자체가 프로그램 하나야. 그날까지 가족은 손으로 키운 사본 여덟 개를 들고 있었고, 사본끼리 다른 줄이 제일 작은 사본의 줄 수보다 많았어. 그 차이는 설계 선택이 아니었어. 한 앱이 배운 가드를 다른 앱들이 아직 못 따라잡은 거였지. archive 둘은 빌드 도구의 상태를 필터 파이프 뒤에 삼켰고, 하나는 깨끗한 트리 검사도 HEAD 재확인도 없었고, 하나는 빌드 번호를 안 올렸고, 잠긴 화면을 거부하는 건 둘뿐이었어. 가드마다 한 번 배우고 덜 베껴진 거야. 이제 모든 가드가 모든 앱에서 한 순서로 돌아. 프로그램은 각 레포에 사본으로 들어가고 drift 테스트가 바이트 단위로 같음을 붙들어. 앱은 자기 사실만 옆에 둬 — 이름, 경로, 훅 몇 개. 그래서 출시가 빌드하는 기계에 형제 체크아웃이 있느냐 없느냐에 절대 기대지 않아.
vessels 트랙이 두뇌에 대해, frontend 트랙이 형제 앱에 대해 가르쳐온 그 본능을 빌드 파이프라인에 겨눈 거야. canonical 한 건 한 자리에 살고, 모든 소비자는 같기를 바라는 재구현이 아니라 같음이 증명된 사본을 들고 다녀.