툴과 개발 환경6 분 읽기

제 변경이 기기에서 안 보였습니다. 기기에 깔린 건 두 시간 전 앱이었습니다

시뮬레이터 스크린샷은 코드와 맞는데 실기만 달랐습니다. 코드를 의심하기 전에 봐야 했던 건 설치 아티팩트였습니다.

#gotchas#ios#debugging#reality-check#verification
Left panel: three DerivedData folders for the same app, with find piped to head selecting an old one. Right panel: the build log path and BUILT_PRODUCTS_DIR pointing at the fresh build.
같은 앱의 DerivedData 폴더가 세 개였습니다. 알파벳 순으로 첫 번째가 최신은 아닙니다.

새 기능을 넣고 빌드해서 실기에 설치했습니다. 앱을 열었는데 진입점이 없습니다.

여기서 보통 무엇을 의심하십니까? 저는 코드를 의심했습니다. 조건부 표시인가, 플래그가 꺼져 있나, 빌드 구성이 다른가. 한동안 그쪽만 팠습니다.

Purro 이구아나 미니게임 작업 중이었고, 시뮬레이터에서는 잘 보였습니다. iPhone 16 Pro에만 없었습니다.

이상한 조합

정리하면 이렇습니다.

  • 시뮬레이터 스크린샷: 코드와 일치. 새 진입점 보임.
  • 실기: 같은 커밋, 같은 명령으로 설치. 진입점 없음.

이 조합 자체가 힌트입니다. 코드가 문제라면 시뮬레이터에서도 틀려야 합니다. 두 결과가 갈렸다는 건 둘이 서로 다른 산출물을 보고 있다는 뜻입니다.

설치 명령이 집어온 것

실기 설치는 이런 형태였습니다.

find ~/Library/Developer/Xcode/DerivedData/Purro-*/Build/Products/Debug-iphoneos/Purro.app | head -1

그리고 그 앱의 DerivedData 폴더가 세 개 있었습니다.

Purro-ctvxvemt…
Purro-dwhrqz…
Purro-eawaacqn…

find 는 최신순으로 주지 않습니다. head -1 은 그중 하나를 가져올 뿐이고, 이번에 걸린 것은 Purro-eawaacqn…이구아나 작업을 시작하기 전, 두 시간 전 빌드였습니다. 그걸 기기에 설치했으니 진입점이 없는 게 당연합니다.

코드는 처음부터 맞았습니다. 그동안 멀쩡한 코드를 읽고 있었던 겁니다.

왜 폴더가 여러 개인가

DerivedData 폴더 이름 뒤의 해시는 프로젝트 파일의 경로에서 나옵니다. 그래서 같은 앱이라도 이런 일이 있으면 폴더가 새로 생깁니다.

  • 프로젝트를 다른 경로로 옮기거나 복사했을 때
  • worktree나 별도 체크아웃에서 빌드했을 때
  • 경로에 심볼릭 링크가 섞여 서로 다르게 해석됐을 때

오래된 폴더는 자동으로 사라지지 않습니다. 계속 거기 남아서, 경로 패턴으로 뭔가를 찾는 스크립트에 조용히 걸립니다.

여기서 당신이라면?

두 갈래가 있습니다.

  1. DerivedData를 전부 지우고 다시 빌드한다.
  2. 방금 돌린 빌드가 어느 폴더에 넣었는지를 확인해서 그 경로로 설치한다.

1번은 이번엔 해결되지만 다음에 또 납니다. 폴더는 다시 여러 개가 되고, 그때도 head -1 은 아무거나 집습니다. 저는 2번으로 바꿨습니다.

방금 빌드한 산출물을 정확히 집는 법

정답에 가까운 순서대로 세 가지입니다.

1. 빌드 로그에서 꺼낸다. 방금 돌린 xcodebuild 출력에 실제로 쓴 경로가 찍혀 있습니다. 이게 가장 확실합니다.

2. 빌드 설정에 직접 묻는다.

xcodebuild -showBuildSettings … | grep BUILT_PRODUCTS_DIR

3. 급하면 mtime 최신순으로.

/bin/ls -dt ~/Library/Developer/Xcode/DerivedData/Purro-*/Build/Products/Debug-ipho*/Purro.app | head -1

find | head -1 과의 차이는 -t 하나입니다. 그 한 글자가 "아무거나"와 "가장 최근"을 가릅니다.

자가진단 3개

  • 설치·업로드 스크립트가 .app / .ipa / .aab경로 패턴으로 찾고 있습니까? 그렇다면 정렬 조건이 있습니까?
  • 지금 그 앱의 DerivedData 폴더가 몇 개입니까? (ls -d …/DerivedData/<앱>-*)
  • 기기에 깔린 앱이 방금 빌드한 것과 같다는 근거가 있습니까? 버전·빌드 번호를 화면 어딘가에 노출해 두면 이 질문이 1초에 끝납니다.

솔직한 부분

이 사건에서 제일 비쌌던 건 잃은 시간이 아니라 의심의 순서였습니다. "코드가 틀렸나"부터 시작하면 무한히 팔 수 있습니다. 코드는 언제나 조금씩 수상해 보이니까요.

그래서 규칙 하나를 만들었습니다. 시뮬레이터는 코드와 맞는데 실기만 다르면, 코드보다 설치 아티팩트를 먼저 본다. 두 환경이 같은 소스에서 갈렸다면 갈린 지점은 소스가 아닙니다.

테스트 200개가 통과했고 버튼은 아무것도 하지 않았습니다와 같은 계열입니다. 그때는 엔진이 아니라 경로를 검증해야 했고, 이번엔 코드가 아니라 산출물을 검증해야 했습니다.

지금 딱 하나만 해보세요. ls -d ~/Library/Developer/Xcode/DerivedData/<당신의앱>-* 를 쳐보시기 바랍니다. 한 줄만 나오시나요?

유튜브

제 변경이 기기에서 안 보였습니다. 기기에 깔린 건 두 시간 전 앱이었습니다

관련 글