툴과 개발 환경6 분 읽기

31개 앱이 전부 검증을 통과했고, 전부 컴파일에 실패했습니다

앱 31개에 가드 한 줄을 일괄 삽입하고 파싱 검증을 돌렸습니다. 31개 전부 통과. 그 다음 타입체크를 돌렸더니 31개 전부 실패했습니다. 가드를 부르는 코드만 심고 정의는 심지 않았기 때문입니다.

#swift#build#verification#tooling#gotchas
두 패널 도식. 왼쪽은 파싱 검증이 31개 앱 전부를 통과시키는 모습으로, 존재하지 않는 멤버를 부르는 코드도 문법적으로는 완벽하다는 점을 보여준다. 오른쪽은 타입체크가 31개 앱 전부를 실패시키며 없는 멤버 오류를 내는 모습과, 문자열 grep이 소속을 말해주지 않는다는 점을 보여준다.
빠른 검증을 골랐는데, 빠른 이유가 곧 못 잡는 이유였습니다.

앱 31개에 같은 배너 컴포넌트가 들어 있습니다. 스토어 스크린샷을 찍을 때 이 배너가 화면에 나오면 안 되므로, 31개 전부에 가드 한 줄을 일괄 삽입했습니다.

검증으로 파싱을 돌렸습니다.

xcrun swiftc -parse <>

31개 전부 통과했습니다.

일괄 수정을 할 때, 당신이 고른 검증 도구는 당신이 낼 수 있는 실수를 잡을 수 있는 도구입니까?

그 다음 타입체크를 돌렸습니다

31개 전부 실패했습니다.

error: type 'PromoConfig' has no member 'isScreenshotMode'

가드를 부르는 코드만 심고, 가드 정의는 안 심었습니다.

-parse는 문법만 봅니다. 존재하지 않는 멤버를 호출하는 코드는 문법적으로 완벽하므로 통과합니다. 그대로 커밋했으면 31개 앱이 동시에 깨졌습니다.

더 앞의 실수

왜 정의를 안 심었을까요. 사전 조사에서 "정의가 이미 있다"고 판정했기 때문입니다.

그 판정의 근거는 이거였습니다 — 파일에 isScreenshotMode라는 문자열이 있는지 grep한 것.

그 문자열은 같은 파일에 있었습니다. 다만 다른 타입의 멤버였습니다.

문자열의 존재는 소속을 말해 주지 않습니다. 검증이 틀리는 다른 방향은 제 검증이 늑대라고 소리친 적에 적었습니다. 멤버가 어느 타입에 속하는지 볼 때는 grep이 아니라 그 타입의 선언 블록만 잘라서 봐야 합니다.

당신이라면?

31개 저장소에 같은 한 줄을 넣었습니다. 커밋 전에 무엇으로 게이트를 걸겠습니까?

  • 파싱 — 31개가 몇 초에 끝난다. 그리고 방금 낸 실수는 하나도 못 잡는다.
  • 타입체크 — 느리지만 "없는 멤버"를 잡는다. 다른 파일의 타입은 모르므로 무해한 오류가 섞인다.
  • 실제 빌드 — 확증이지만 31개 전부에 돌리기엔 너무 비싸다.

두 번째를 게이트로 걸고, 세 번째는 형태별 표본으로 돌렸습니다.

swiftc -typecheck \
  -sdk $(xcrun --sdk iphonesimulator --show-sdk-path) \
  -target arm64-apple-ios17.0-simulator <>

타입체크도 그냥 초록/빨강이 아닙니다

단일 파일 타입체크는 다른 파일의 타입을 모릅니다. 그래서 이번 수정과 무관한 오류가 섞여 나옵니다.

이건 자동으로 걸러지지 않습니다. 변경 전 원본에 같은 오류가 나는지로 하나하나 갈라내야 합니다.

git show HEAD:<> > /tmp/before.swift   # 같은 오류가 나면 내 변경 탓이 아니다

이번에 두 앱이 그 경우였습니다.

자가진단 세 줄

  1. 일괄 수정의 검증 도구가 "빠른 것"으로 골라졌습니까? 빠른 이유를 확인하십시오. 대개 안 보는 게 있어서 빠릅니다.
  2. "이미 정의돼 있다"를 grep으로 판정했습니까? 문자열은 파일에 있고 멤버는 다른 타입에 있을 수 있습니다.
  3. 검증 통과가 빌드 성공과 같다고 보고 있습니까? 아닙니다. 최소한 형태별로 하나씩은 실제로 빌드해야 합니다.

솔직한 부분

타입체크가 통과해도 앱이 빌드된다는 보장은 아닙니다. 마지막 확증은 실제 빌드뿐이고, 그건 31개 전부에 돌리기엔 너무 비쌉니다.

형태가 3종으로 갈려 있어서 종마다 하나씩 골라 두 개만 돌렸습니다. 즉 남은 위험은 그대로 남아 있습니다. 표본으로 확인했다고 전수 확인한 것처럼 적지 않으려고 이 단락을 씁니다.

수치로 남기면 이렇습니다. -parse 31/31 통과 → -typecheck 31/31 실패 → 정의까지 심고 재검증 통과 → 형태별 실제 빌드 2건 통과.

관련 글