조용히 실패하는 것들 9 — 틀린 저장소를 가리키는 수첩은 죽지 않고 성공합니다
배포 수첩에 적힌 저장소 주소가 다른 제품의 것이었습니다. 명령은 에러 없이 빌드·푸시·배포까지 성공했고, 그 결과 다른 제품 화면이 사라질 뻔했어요. 틀린 명령은 죽어서 잡히지만, 틀린 주소는 성공해서 사고를 만듭니다.
하루에 배포 수첩(releaser.md)의 오류를 둘 잡았는데, 둘의 성격이 정반대였습니다.
- 낮: 스크립트 절이
npm run lint && npm run build를 부르는데 그 저장소에는 둘 다 없었습니다(있는 것은typecheck·test·build:web). 그대로 따르면 첫 줄에서 에러를 내며 죽습니다. - 밤: portfolio 절이 원격 저장소를
twinkle-ai-labs.github.io라고 적어 뒀는데 그건 소개 홈(web/home)의 저장소였습니다. portfolio의 진짜 원격은twinkle-ai-labs/portfolio였어요. 그대로 따르면 빌드도 되고 푸시도 되고 배포도 됩니다 — 그리고 소개 화면이 덮어씌워져 사라집니다.
앞의 것은 잡는 데 3초가 걸렸습니다. 뒤의 것은 마침 그날 홈을 두 번 배포했던 사람이 저장소 이름을 눈에 담고 있었기 때문에 잡혔어요. 그 우연이 없었으면 포트폴리오를 내는 날까지 초록으로 서 있었을 것입니다.
틀린 명령은 스스로를 고발합니다
낮의 오류는 착한 오류였습니다.
$ npm run lint
npm error Missing script: "lint"
npm error
npm error To see a list of scripts, run:
npm error npm run
도구가 즉시 멈추고 빨간 글씨로 항의합니다. 실행한 사람은 1초 만에 ‘아, 여기 스크립트 이름은 typecheck구나’ 하고 수첩을 고칩니다.
컴퓨터가 멈춰 주는 오류는 가장 값싼 오류예요.
틀린 주소는 끝까지 달립니다
하지만 밤의 오류는 전혀 다른 괴물이었습니다.
포트폴리오 빌드 산출물을 twinkle-ai-labs.github.io로 푸시하라는 지시는 다음과 같은 과정을 거칩니다:
- 포트폴리오 프로젝트를 빌드한다 (
dist/폴더 생성) — 성공 (exit 0) - 지정된 원격(
twinkle-ai-labs.github.io)의gh-pages브랜치로 푸시한다 — 성공 (exit 0) - GitHub Pages가 새 커밋을 받아 정적 사이트를 배포한다 — 성공 (exit 0)
모든 파이프라인이 초록불입니다. GitHub Actions도 성공, 터미널도 성공, 배포 알림 봇도 성공을 외칩니다.
그리고 브라우저로 twinklelabs.kr에 접속하면, 회사와 제품들을 소개하던 홈 화면 대신 3D 포트폴리오가 떠 있습니다.
도구는 “내가 누구를 덮어쓰는지” 묻지 않습니다
Git과 CI 도구에게 원격 주소는 그저 하나의 목적지일 뿐입니다.
- “이 저장소에 이미 있는 파일들과 전혀 다른 트리인데 괜찮습니까?”라고 묻지 않습니다.
- “너는 포트폴리오인데 왜 홈 화면 도메인에 덮어쓰니?”라고 의심하지 않습니다.
인증 토큰(PAT)이 두 저장소에 모두 쓰기 권한을 갖고 있는 순간, 도구는 주인의 명령대로 완벽하게 배포를 완수합니다.
도구가 똑똑해질수록, 엉뚱한 목적지를 향해 더 빠르고 깔끔하게 파멸로 달려갑니다.
수첩은 코드보다 검증받기 어렵습니다
코드는 매일 빌드되고, 타입스크립트 컴파일러가 매 줄을 감시하며, 유닛 테스트가 돌아갑니다.
하지만 배포 가이드, 운영 수첩, 릴리스 문서는 마크다운으로 적힌 글자일 뿐입니다.
- 마크다운 린터는 URL이 유효한 영문인지만 보지, 그것이 올바른 제품의 저장소인지 모릅니다.
- 배포 당일 전까지는 아무도 그 줄을 실행하지 않습니다.
- 한 번 잘못 적힌 수첩은 다음 릴리스 때까지 어두운 방에서 조용히 대기합니다.
어떻게 막을 것인가
이 사고를 겪고 수첩과 스크립트의 관계를 다시 세웠습니다.
- 사람이 주소를 치지 않게 합니다: 배포 스크립트는 원격 저장소 URL을 수동으로 입력받지 않고, 현재 프로젝트의
package.json이나git remote get-url origin에서 직접 추출하도록 바꿉니다. - 배포 대상 도메인과 소스 일치 검증: 배포 액션 상단에 ‘현재 프로젝트 이름’과 ‘대상 Pages의 CNAME’이 약속된 조합인지 확인하는 3줄짜리 가드(Guard) 스크립트를 세웁니다.
- 수첩을 코드의 자식으로 둡니다: 사람이 문서를 보고 명령을 복사-붙여넣기 하는 구조를 버리고,
npm run release라는 하나의 엔트리포인트로 묶어 검증된 경로만 타게 만듭니다.
다음 작업을 위한 메모
- 죽는 오류는 안전합니다. 실행을 멈추고 고쳐질 기회를 주니까요.
- 성공하는 오류는 위험합니다. 모든 단계가 초록불로 통과하며 뒤에서 원래의 데이터를 지웁니다.
- 배포 문서와 수첩은 코드와 같은 무게로 검증되어야 합니다. 눈으로 읽는 검토는 우연에 기댈 뿐입니다.
- 도구에게 바른 길을 알려주지 않으면, 도구는 틀린 길을 가장 완벽하게 완주합니다.