dotgimon 제작기 15. pnpm ios 실행 실패 추적
2개월 넘게 손을 놓았던 프로젝트를 열고 pnpm ios를 실행했다. 당시 스크립트는 expo start --ios였고, Metro라는 React Native 개발 서버를 띄운 뒤 iOS 시뮬레이터에서 앱을 여는 명령이었다. 코드를 바꾸기도 전에 실행이 막혔다.
터미널에서는 명령이 실패했고 시뮬레이터 앱은 뜨자마자 꺼졌다. 당시 실행 기록에는 ~/Library/Logs/DiagnosticReports/에 크래시 리포트 네 개가 남아 있었다. 시각은 14:45, 14:53, 15:34 두 건이었다. Claude Code에 질문한 14:52보다 앞서 이미 한 번 꺼져 있었다.
포트를 비워도 크래시는 남았다
당시 환경은 macOS 26.7, Expo SDK 57의 expo 57.0.3, React Native 0.86.0이었다. /Applications에는 Xcode 27.0, 26.6, 16.4가 함께 있었고 iPhone 17 Pro의 iOS 26.4 시뮬레이터가 부팅돼 있었다.
에이전트가 비대화형 모드로 명령을 실행하자 다른 프로젝트의 Metro가 8081 포트를 쓰고 있다는 메시지가 나왔다.
> expo start --ios
› Port 8081 is running <other-project> in another window
/Users/.../<other-project> (pid 6409)
Input is required, but 'npx expo' is in non-interactive mode.
Required input:
> Use port 8082 instead?
› Skipping dev server
ELIFECYCLE Command failed with exit code 1.Expo가 다른 포트를 쓸지 물었지만 비대화형 실행에서는 답을 받을 수 없어 개발 서버를 건너뛴 상태였다. --port 8082를 붙이면 Metro가 떴다. 필요 없는 프로젝트라 당시 PID 6409 프로세스를 종료해 8081을 비웠다.
포트 충돌은 해결했지만 Simulator는 계속 꺼졌다. 명령 실패와 시뮬레이터 크래시가 하나의 원인은 아니었다. 개발 서버가 뜨는지와 어느 앱을 여는지를 나눠서 살펴봤다.
기본 Xcode와 죽는 Simulator의 버전이 달랐다
당시 기록에 옮긴 크래시 리포트 헤더에는 Simulator 16.0, 빌드 1042.1이 적혀 있었다.
"app_name":"Simulator","app_version":"16.0","build_version":"1042.1",
"bundleID":"com.apple.iphonesimulator", "os_version":"macOS 26.7 (25G229)"기본 Xcode는 27.0인데 실행된 Simulator는 16.x였다. 설치된 Xcode 안의 앱을 대조했다.
| Xcode 경로 | Xcode 버전 | 확인한 시뮬레이터 앱 |
|---|---|---|
Xcode.app | 27.0 | Contents/Applications/DeviceHub.app |
Xcode-26.6.app | 26.6 | Simulator.app, 빌드 1063.4 |
Xcode-16.4.0.app | 16.4 | Simulator.app, 빌드 1042.1 |
크래시 리포트의 빌드가 Xcode 16.4 안의 Simulator와 같았다. 당시 관찰한 것은 그 오래된 앱이 실행된 뒤 SIGABRT로 종료되는 상황이었다.
실패해야 넘어가는 경로가 실패하지 않았다
Xcode 27의 DeviceHub를 여는 처리는 Expo CLI에 있었다. SDK 57의 시뮬레이터 실행 코드는 Simulator를 우선 열고, 그 호출이 실패하면 DeviceHub로 넘어간다. 당시 읽은 코드도 같은 순서였다.
async function openSimulatorAppAsync(device) {
try {
const args = ['-a', 'Simulator'];
if (device.udid) args.push('--args', '-CurrentDeviceUDID', device.udid);
await spawnAsync('open', args);
} catch {
// DeviceHub의 devices:// URL로 해당 기기를 연다.
}
}내 Mac에는 옛 Xcode의 Simulator.app이 남아 있었다. macOS의 앱 등록 정보를 관리하는 LaunchServices가 그 이름의 앱을 찾았고, open -a Simulator는 성공으로 끝났다. 그 뒤에 열린 앱이 죽어도 이미 성공한 open 호출의 catch로 돌아가지는 않았다.
Simulator.app을 찾지 못했을 때 DeviceHub로 넘어가는 구성인데, 오래된 앱을 찾는 데 성공한 것이 문제였다. 크래시 리포트의 실행 경로와 CLI의 호출 순서를 연결하니 기본 Xcode와 다른 Simulator가 뜬 이유를 설명할 수 있었다.
DeviceHub를 먼저 열어 우회했다
Expo CLI는 Simulator나 DeviceHub 프로세스가 이미 실행 중이면 앱을 다시 열지 않았다. 부팅된 기기를 DeviceHub에서 열도록 URL을 직접 호출했다.
open "devices://device/open?id=<시뮬레이터 UDID>"그 뒤 프로세스 목록에서 Xcode 16.4의 Simulator가 실행되지 않은 것을 확인했다. 이미 떠 있던 Metro에는 다음 명령으로 연결했다.
xcrun simctl openurl booted exp://127.0.0.1:8081당시 실행 기록에는 시뮬레이터의 메인 화면과 도감 DEX 19/87이 보였다고 남았다. 앱 코드 변경 없이 포트를 비우고 DeviceHub를 먼저 여는 방법으로 실행까지 갔다.
옛 Xcode를 지우거나 Simulator 앱 등록을 해제하는 방법도 있었지만 이날은 삭제하지 않았다. DeviceHub를 먼저 여는 우회만 적용했다. 설치된 앱과 등록 정보는 그대로 남아 있었다.
실행 중에는 57.0.3에서 ~57.0.24로 Expo를 업데이트할 수 있다는 안내도 떴다. 이날 추적한 포트 충돌과 앱 선택 경로에서는 그 안내를 원인으로 잡지 않았다. 원인이 하나일 것이라고 가정했다면 포트를 비운 뒤 끝내고, 계속 꺼지는 Simulator를 놓쳤을 것이다.