당신의 검증 스크립트는, 확인 대상을 읽지 못했을 때 무엇을 합니까?
거부됐던 안드로이드 앱 세 개를 다음 날 한 번에 재제출하려고, 전날 미리 빌드해 두는 작업이었습니다. 버전 코드를 올린 번들을 만들고, 올리기 전에 검증하는 단계까지가 목표였습니다. 검증 항목은 둘입니다. 번들의 버전 코드가 의도한 값인지, 그리고 올바른 업로드 키로 서명됐는지. 두 번째를 넣은 이유가 있습니다. 서명이 틀리면 업로드 시점에야 거부되는데, 그때는 그 버전 코드가 이미 소모돼 있을 수 있습니다.
당신이라면?
세 개의 대상이 동시에 같은 오류를 냅니다. 스크립트를 의심하시겠습니까, 대상 세 개를 의심하시겠습니까? 저는 대상 쪽으로 갔고, 잠깐 "서명이 안 붙었나"까지 갔습니다. 공통 원인은 제 셸이었습니다.
첫 실행에서 세 앱 전부 "읽기 실패"
키스토어에서 인증서 지문을 뽑고, 번들 안 서명 블록에서도 지문을 뽑아 비교하는 스크립트였는데 양쪽 다 빈 값이었습니다. 세 앱이 동시에 같은 증상이면 스크립트가 아니라 대상 쪽을 의심하게 됩니다.
원인은 인증서가 아니라 명령이 실행되지 않은 것이었습니다. 빌드는 특정 JDK를 명시해서 돌렸는데, 검증은 다른 셸에서 실행했고 거기엔 그 경로가 없었습니다. 키 도구가 없으니 아무것도 출력하지 않았고, 출력을 grep으로 받는 구조라 빈 결과가 "지문을 못 읽음"으로 보였습니다.
이게 위험한 이유는 방향이 반대인 쪽입니다. 지금은 빈 값이 실패로 보였지만, 조건을 if 지문이 다르면 실패로 썼다면 빈 값 두 개가 서로 같아서 통과했을 겁니다. 도구가 없는데 검증이 성공했다고 말하는 상태가 됩니다.
같은 함정을 번들 버전 검사에서도 이미 겪었습니다. 표준 패키지 도구는 번들 형식을 읽지 못하는데, 출력에서 정규식으로 버전을 긁는 가드는 매치가 없으면 조용히 통과합니다. 예전에 그 가드를 달아 두고도 이전 버전 번들을 올려 "이미 사용된 버전"이라는 거절을 받은 적이 있습니다.
검증기는 "무엇을 확인했는가"가 아니라 "무엇을 확인하지 못했을 때 어떻게 되는가"로 설계해야 합니다.
정직하게
- 빌드 셸과 검증 셸의 환경이 다르다는 걸 몇 분 동안 못 봤습니다. 세 앱이 동시에 같은 증상인 것을 "공통 원인 = 대상 쪽 문제"로 읽었는데, 공통 원인은 제 셸이었습니다.
- 검증 스크립트를 쓰면서 "못 읽으면 실패"를 명시적으로 넣은 건 이번에 운이 좋았던 부분입니다. 습관이 아니라 직전 사고 때문에 넣은 것이고, 다른 스크립트들에는 아직 그 형태가 아닙니다.
번들 버전 검사가 실제로 검사하게 만드는 조건
- 표준 패키지 도구 대신 같은 빌드가 남긴 평문 매니페스트를 읽습니다.
- 그 매니페스트가 번들보다 새로우면 실패시킵니다. 매니페스트는 번들 바깥에 있어 다른 빌드의 것일 수 있습니다.
- 그리고 값을 못 읽으면 통과시키지 않습니다.
서명 대조는 두 곳에서 지문을 뽑아 비교합니다. 앱마다 지문이 서로 달라야 정상입니다 — 같으면 키가 섞인 겁니다.
셸 환경은 검증 쪽에도 똑같이 넘깁니다.
export JAVA_HOME=$(...)
export PATH="$JAVA_HOME/bin:$PATH"이번 빌드 결과: 세 앱 모두 버전 코드·버전 이름 일치, 서명 지문 세 개가 서로 다름. 빌드 소요는 앱당 2~10분이었습니다.
자가진단 3개
- 값을
grep이나 정규식으로 긁어 판정하는 가드가 있다면, 매치가 0건일 때 통과입니까 실패입니까? 조용히 통과라면 도구가 죽어도 초록불이 켜집니다. - 빌드를 특정 런타임 경로로 고정해 돌린다면, 그 뒤에 오는 검증 스텝도 같은 환경을 물려받습니까? 아니면 다른 셸에서 맨몸으로 돕니까?
- "A와 B가 다르면 실패" 같은 비교 검사에서, A와 B가 둘 다 빈 값이면 어떻게 됩니까? 같아서 통과됩니다.
같은 재제출 준비에서 나온 다른 함정으로, 심사 회신만으로 재개되는 줄 알고 두 시간을 버린 이야기도 있습니다. 이쪽은 출력이 비어서 오진했고, 저쪽은 적어 둔 절차 자체가 틀렸습니다.
지금 파이프라인에서 값을 긁어 판정하는 가드 하나를 골라, 그 도구를 일부러 PATH에서 빼고 돌려 보세요. 초록불이 켜지면 그 가드는 검사하고 있지 않습니다.