lifecarelog
개발일지

launchd 잡이 조용히 죽어 있던 이유, Xcode 라이선스와 python3

macOS launchd 잡 5개가 코드 변경 없이 멈춘 원인은 Xcode 라이선스 게이트에 막힌 /usr/bin/python3였어요. DEVELOPER_DIR로 고친 과정을 적어요.

5분 읽기

지난 주말 마케팅 자동화 저장소를 손보다가 이상한 걸 봤어요. 승인 배치, 블로그 아웃박스, 알림 아웃박스, 아웃박스 정합, 지식 증류까지 launchd 잡 5개가 전부 멈춰 있었어요. 코드는 건드린 적이 없는데요. 2026-09-20 하루에 커밋을 8건 넣었는데, 그중 이 수정이 제일 코드답지 않은 수정이었어요. 파이썬 한 줄도 안 고쳤거든요.

로그가 아니라 종료 코드부터

직접 확인해 보니 잡 로그엔 아무것도 없었어요. 스크립트가 시작조차 못 하면 로그도 안 남으니까요. 그래서 launchctl list로 마지막 종료 상태부터 봤어요. 잡마다 종료 상태가 0이 아니었고, 공통 분모는 하나였어요. 전부 /usr/bin/python3로 시작한다는 것.

터미널에서 같은 명령을 그대로 돌려 보니 답이 바로 나왔어요. Xcode 라이선스에 동의하라는 메시지가 뜨면서 파이썬이 실행되지 않았어요. macOS의 /usr/bin/python3는 진짜 인터프리터가 아니라 얇은 껍데기(shim)예요. xcode-select가 가리키는 개발자 디렉터리로 호출을 넘기는데, 그 대상이 Xcode 본체면 라이선스 동의 여부를 먼저 검사해요. 동의가 안 된 상태였고, 잡들은 사람이 안 보는 시간에 도니 터미널에서 마주칠 일이 없었던 거예요.

launchctl list로 종료 코드 확인터미널에서 같은 명령 재현plist마다 DEVELOPER_DIR 고정

동의 대신 경로를 고정한 이유

sudo xcodebuild -license accept 한 번이면 당장은 풀려요. 그런데 다음 Xcode 업데이트 때 같은 일이 또 생기고, 그때도 잡은 조용히 죽어요. 이 잡들은 사람이 안 보는 시간에 도는 게 목적이라, 사람 손에 기대는 해결은 해결이 아니에요.

그래서 plist 5개마다 EnvironmentVariables에 DEVELOPER_DIR를 Command Line Tools 경로로 못 박았어요. man xcode-select에 적혀 있듯 이 변수는 활성 개발자 디렉터리를 덮어써요. Command Line Tools 쪽은 Xcode 본체의 라이선스 검사를 거치지 않으니 파이썬이 그냥 뜨고요.

<key>EnvironmentVariables</key>
<dict>
  <key>DEVELOPER_DIR</key>
  <string>/Library/Developer/CommandLineTools</string>
</dict>

적용해 봤더니 5개 잡이 다시 돌기 시작했어요. 고쳐 보니 결국 한 항목짜리 수정인데, launchd는 셸 프로필을 읽지 않으니 .zshrc에 export를 넣어봐야 소용없고 plist 안에 직접 써야 한다는 걸 이번에 다시 새겼어요.

사람이 없어도 도는 게 자동화의 조건

플랜씨 같은 계산기는 로그인 없이 바로 쓰는 단순한 앱이지만, 그걸 알리는 글과 알림을 내보내는 쪽은 잡 5개가 얽혀 있어요. 이 블로그 글을 내보내는 아웃박스 잡도 그중 하나예요. 하나가 멈추면 나머지가 멀쩡해도 결과물은 안 나가요.

이번 일로 하나 배운 건, 잡이 죽었을 때 "실패 로그"를 찾지 말고 "시작조차 못 한 흔적"부터 찾자는 거예요. 테스트해 보면 무인 실패의 상당수는 스크립트 안이 아니라 스크립트가 뜨기 전 환경에서 나요. 다음엔 종료 코드가 0이 아닌 잡을 알려주는 감시 한 줄을 붙일 생각이에요.

  • 종료 코드 먼저
  • 터미널에서 재현
  • plist에 환경 고정
  • 셸 프로필은 launchd가 안 읽음
#launchd#macOS#빌드로그

라이프케어로그 서비스가 궁금하신가요?

AI 기반 건강·일정·재활 관리 앱을 직접 써보세요.

서비스 살펴보기

관련 글

댓글

아직 댓글이 없어요. 첫 댓글을 남겨주세요.