이 글에서 다루는 것
기능 코드가 아니라 개발 환경(도구·빌드·CI 설정) 자체를 정비한 날이었다. 표면은 제각각이었지만 —훅 스크립트가 안 먹고, 모노레포 배포가 깨지고, 자동 릴리스가 안 돌고, 위험 명령 가드를 새로 다는— 관통하는 교훈은 하나였다. "안 되는 것"의 첫 의심은 도구 버그가 아니라 배선(인용·버전 프로토콜·러너 라벨)이라는 것.
여기서는 그날 만난 개념을 특정 회사·프로젝트와 무관한 범용 지식으로만 정리한다.
하루 요약
| 개념 | 어떤 문제였나 | 배운 것 |
|---|---|---|
| 셸 단어 분리·인용 | 값은 맞는데 명령만 실패 | 공백 경로는 "$VAR"로 인용 |
| 모노레포 workspace 배포 | 내부 의존이 외부 설치에서 깨짐 | workspace:*는 pnpm publish로 치환 |
| CI 러너 불일치 | "릴리스 도구 오동작" | 로그 없는 실패 = 러너·권한 문제 |
| fail-open 가드 | 안전장치가 작업을 막음 | 고장 시 통과, 명백한 재앙만 차단 |
1. 값은 맞는데 명령만 실패 — 셸 단어 분리
어떤 상황이었나. 자동 실행되는 훅 스크립트가 ${PLUGIN_ROOT}/hook.sh를 실행하는데, 경로에 공백이 있는 환경에서 아무 반응이 없었다. 변수 값을 출력해 보면 분명 맞는데, 실행만 실패했다.
무엇을 배웠나. 셸(bash·zsh)은 변수를 확장한 뒤 그 결과를 공백 기준으로 여러 인자로 쪼갠다(단어 분리, word splitting). /tmp/My Files/hook.sh가 /tmp/My + Files/hook.sh 두 조각이 되어 스크립트를 못 찾는다. "${PLUGIN_ROOT}/hook.sh"로 인용하면 값 하나가 인자 하나로 보존된다. 훅·CI처럼 사람이 잘 안 보는 스크립트에서 이 실수는 조용히 깨지므로, 경로·파일명은 예외 없이 인용하는 것이 기본이다.
2. 내부 의존이 외부 설치에서 깨짐 — 모노레포 workspace 배포
어떤 상황이었나. 여러 패키지가 한 저장소에 사는 모노레포에서, 배포한 패키지를 외부에서 설치하니 의존이 풀리지 않았다. 또 다른 패키지는 존재하지 않는 버전을 의존에 박아 둬 CI 설치가 404로 죽었다.
무엇을 배웠나. 모노레포는 내부 의존을 "pkg": "workspace:*"로 링크해 로컬 소스를 즉시 반영한다. 그러나 이 workspace:*를 그대로 배포하면 외부 사용자는 해석하지 못한다. pnpm publish(워크스페이스를 아는 매니저)는 배포 시 이를 실제 버전으로 치환하지만 npm publish는 못 한다. 배포는 의존 하위(잎)부터 올려야 하고, pnpm publish는 npm OIDC(무토큰 배포)를 못 하므로 배포 토큰이 필요하다.
3. "도구 오동작"의 실체는 인프라 — CI 러너 불일치
어떤 상황이었나. 자동 릴리스가 "오동작한다"는 신고. 그런데 실패한 잡은 스텝 로그가 아예 없었다.
무엇을 배웠나. CI 잡은 runs-on 라벨로 실행할 러너(잡을 돌리는 머신)를 고른다. 조직이 클라우드 호스티드 러너를 끊고 self-hosted로 옮겼는데 릴리스 잡만 예전 라벨(ubuntu-latest)을 그대로 두면, 그 잡은 배정할 러너가 없어 시작조차 못 하고 로그도 안 남긴다. 도구 버그가 아니라 인프라 배선 문제였다. 로그 없는 CI 실패는 코드가 아니라 러너·권한·트리거를 먼저 본다. 부분 마이그레이션의 잔재(가끔 도는 잡)를 놓치지 않도록 저장소 전체의 runs-on을 한 번에 맞추는 것이 예방책이다.
4. 안전장치가 벽이 되지 않게 — fail-open 가드
어떤 상황이었나. 위험 명령을 막는 가드 훅을 새로 달았다. 문제는 이 가드가 스스로 고장 나면(의존 도구 부재, 파싱 실패) 셸 전체를 막아 버릴 수 있다는 것.
무엇을 배웠나. 가드가 고장 났을 때 통과시키면 fail-open, 차단하면 fail-closed다. 개발 편의 도구의 가드는 fail-open으로 만들어(의존성 없으면 통과) 안전망이 벽이 되지 않게 한다. 반대로 인증·결제·데이터 파괴 같은 보안 경계는 fail-closed다. 또 가드는 명백한 재앙만 막아야 한다 — 애매한 것까지 막으면 오탐이 잦아 사용자가 가드를 꺼 버려 그물째 사라진다. 전역 설정에 넣기 전에는 위험·정상 케이스로 self-check하고 백업을 남긴다.