클라우드 Mac에서 iOS 딥링크 회귀 테스트 구축

클라우드 Mac에서 iOS 딥링크 회귀 테스트 구축

iOS 앱에서 로그인 콜백, 이벤트 페이지, 주문 상세 화면, 알림 도착 지점을 딥링크로 처리하면 라우팅을 한 번 리팩터링한 것만으로도 링크가 홈 화면에 머물거나, 앱이 이미 열린 상태에서 같은 화면이 중복으로 쌓일 수 있습니다. 링크 몇 개를 사람이 직접 눌러 보는 방식은 그 시점에 동작했다는 사실만 확인할 뿐, 콜드 스타트, 포그라운드 상태, 인코딩된 매개변수, 잘못된 입력까지 검증하지 못합니다. 더 안정적인 방법은 클라우드 Mac에서 시뮬레이터 환경을 고정하고 각 딥링크의 최종 라우팅 결과를 CI가 읽을 수 있는 형태로 만드는 것입니다.

테스트 스크립트 곳곳에서 URL을 조합하지 마세요. 먼저 입력, 예상 라우트, 앱 상태를 명시한 라우팅 목록을 관리해야 합니다.

시나리오 입력 예상 결과
상품 상세 linklab://product/42 product/42
검색 매개변수 linklab://search?q=swift%20ui search?query=swift ui
식별자 누락 linklab://product/ error/invalid-product
알 수 없는 경로 linklab://unknown error/not-found

목록의 예상 값은 특정 버튼의 제목이 아니라 라우터가 해석한 표준 형식이어야 합니다. 그래야 UI 문구가 바뀌어도 오탐이 발생하지 않으며, 테스트 결과만으로 경로, 매개변수, 상태 복원 중 어디에서 문제가 생겼는지 바로 파악할 수 있습니다.

딥링크 게이트가 검증하는 것은 “입력이 올바른 비즈니스 라우트에 도달했는가”이지, “시스템이 URL 하나를 받아들였는가”가 아닙니다. 명령이 성공했다고 해서 올바른 화면이 표시된 것은 아닙니다.

같은 라우트에 대해 최소한 콜드 스타트와 포그라운드 상태 사례를 각각 준비하세요. 인증이 관련된 경우에는 로그인 상태와 비로그인 상태도 구분해야 합니다. 단, 테스트 계정 상태는 스크립트가 명시적으로 설정해야 하며 이전 테스트 사례의 상태를 이어받아서는 안 됩니다.

디버그 빌드에 관측 가능한 프로브 추가하기

명령줄에서 링크를 열 수는 있지만 앱이 최종적으로 어떤 화면을 표시했는지 안정적으로 판단하기는 어렵습니다. DEBUG 빌드에서는 공통 라우팅 진입점이 정규화된 결과를 캐시 디렉터리에 기록하도록 할 수 있습니다. 릴리스 빌드에는 이 로직을 포함하지 말고, 토큰이나 전체 쿼리 매개변수도 기록하지 마세요.

#if DEBUG
func recordResolvedRoute(_ route: String) {
    let payload: NSDictionary = [
        "route": route,
        "recordedAt": Date().timeIntervalSince1970
    ]
    let directory = FileManager.default.urls(
        for: .cachesDirectory,
        in: .userDomainMask
    )[0]
    let file = directory.appendingPathComponent("link-probe.plist")
    payload.write(to: file, atomically: true)
}
#endif

프로브는 URL Scheme, Universal Link, 알림 이동이 공통으로 거치는 라우터 뒤에 배치해야 합니다. 라이프사이클 콜백마다 따로 기록하면 테스트는 콜백이 호출되었다는 사실만 입증할 뿐, 매개변수가 검증을 거쳐 실제 비즈니스 화면에 정상적으로 도달했는지는 확인하지 못할 수 있습니다.

결과를 원자적으로 유지하기

매번 실행하기 전에 이전 파일을 삭제하고, 기록할 때는 원자적 교체 방식을 사용하세요. 그렇지 않으면 앱이 비정상 종료되거나 파일을 너무 일찍 읽었을 때 이번 테스트가 이전 결과를 잘못 읽을 수 있습니다. 라우트 키, 오류 유형, 타임스탬프만 기록하고 사용자 입력은 먼저 비식별화하는 것이 좋습니다.

시뮬레이터를 고정하고 라우팅 매트릭스 실행하기

CI의 시작점으로 모호한 booted를 사용하지 마세요. 먼저 기기 이름과 시스템 버전으로 UDID를 확인하고, 대상 시뮬레이터만 실행 중인지 검증한 다음 시스템 부팅이 완료될 때까지 기다려야 합니다. 아래 예시는 파이프라인에서 이미 SIMULATOR_UDID를 확보했다고 가정합니다.

set -euo pipefail

xcrun simctl shutdown all
xcrun simctl boot "$SIMULATOR_UDID"
xcrun simctl bootstatus "$SIMULATOR_UDID" -b

xcodebuild \
  -scheme LinkLab \
  -configuration Debug \
  -sdk iphonesimulator \
  -derivedDataPath .ci/DerivedData \
  build

APP_PATH=".ci/DerivedData/Build/Products/Debug-iphonesimulator/LinkLab.app"
xcrun simctl install "$SIMULATOR_UDID" "$APP_PATH"

설치한 뒤 먼저 데이터 컨테이너를 가져오고, 각 사례마다 프로브를 삭제한 후 링크를 열어 결과를 폴링하세요. 긴 고정 시간의 sleep은 사용하지 마세요. 앱 실행 속도가 달라지면 불필요하게 시간을 낭비하거나, 기다린 뒤에도 파일을 읽지 못할 수 있습니다.

BUNDLE_ID="com.example.linklab"
DATA_DIR="$(xcrun simctl get_app_container \
  "$SIMULATOR_UDID" "$BUNDLE_ID" data)"
PROBE="$DATA_DIR/Library/Caches/link-probe.plist"

rm -f "$PROBE"
xcrun simctl openurl "$SIMULATOR_UDID" "linklab://product/42"

for attempt in 1 2 3 4 5 6 7 8 9 10; do
  test -f "$PROBE" && break
  sleep 1
done

test -f "$PROBE"
ACTUAL="$(/usr/libexec/PlistBuddy -c "Print :route" "$PROBE")"
test "$ACTUAL" = "product/42"

포그라운드 사례는 앱을 처음 연 뒤 URL을 계속 전송합니다. 콜드 스타트 사례는 먼저 simctl terminate를 실행합니다. 실패한 각 사례에 대해 입력, 예상 값, 실제 값, 테스트 로그를 저장하되, 비밀 정보가 포함된 전체 앱 컨테이너를 아카이브해서는 안 됩니다.

URL Scheme은 라우터를 안정적으로 검증하는 데 적합하지만 Universal Link의 엔드투엔드 검사를 대신할 수는 없습니다. Universal Link는 연결 도메인 권한, HTTPS 사이트 파일, 콘텐츠 유형, 앱 식별자, 시스템 캐시에도 의존합니다.

첫 번째 단계에서는 커밋마다 공통 라우터를 직접 호출해 경로 정규화, 퍼센트 인코딩, 중복 매개변수, 빈 값, 알 수 없는 라우트를 빠르게 검증합니다. 두 번째 단계에서는 통제된 테스트 도메인의 실제 HTTPS 링크를 사용하고, 앱을 다시 설치한 뒤 콜드 스타트와 포그라운드 전환을 확인합니다. 사이트 연결 파일이 변경되면 반드시 두 번째 단계를 실행해야 하며 단위 테스트만 수행해서는 안 됩니다.

흔한 실수는 연결 파일을 수정한 직후 링크를 반복해서 누르고, 이전 캐시의 결과를 새 설정의 결과로 판단하는 것입니다. 문제를 조사할 때는 앱 빌드 번호, 시스템 버전, 설치 순서, 테스트 도메인의 응답 헤더를 기록하세요. 먼저 시스템이 링크를 앱에 전달했는지 확인한 다음 비즈니스 라우팅을 점검해야 합니다.

실패 증거와 정리 작업을 게이트에 포함하기

신뢰할 수 있는 게이트는 0이 아닌 종료 상태만 반환해서는 안 됩니다. 유지보수 담당자가 어느 계층에서 문제가 발생했는지 빠르게 판단할 수 있어야 합니다. 실패 요약에는 최소한 테스트 사례 이름, 앱 상태, 입력 유형, 예상 라우트, 실제 라우트, 프로브 파일 생성 여부가 포함되어야 합니다. 파일이 없다면 설치, 라이프사이클 진입점, 링크 선언부터 확인하세요. 파일은 있지만 라우트가 잘못되었다면 파서와 탐색 상태를 점검해야 합니다.

병합 전 체크리스트는 다음과 같이 간결하게 유지할 수 있습니다.

  • 시뮬레이터 모델과 시스템 버전을 파이프라인에서 고정합니다.
  • 콜드 스타트와 포그라운드 상태를 각각 실행합니다.
  • 각 테스트 사례 전에 앱 프로세스 또는 프로브 상태를 정리합니다.
  • 특수 문자, 빈 매개변수, 알 수 없는 경로에 대한 반례를 포함합니다.
  • Universal Link는 별도의 HTTPS 검증을 수행합니다.
  • 실패 산출물에 자격 증명, 토큰, 사용자 데이터를 포함하지 않습니다.
  • 작업이 끝나면 시뮬레이터를 종료하고 임시 빌드 디렉터리를 삭제합니다.

마지막으로 xcrun simctl shutdown "$SIMULATOR_UDID"를 실행하고, 워크스페이스 정리 정책에서 .ci/DerivedData를 처리하도록 하세요. 라우팅 계약, 관측 가능한 프로브, 상태 격리가 모두 갖춰져야 딥링크 테스트가 단순히 “링크가 열린다”는 확인을 넘어 탐색 회귀를 차단하는 엔지니어링 게이트로 발전할 수 있습니다.

자주 묻는 질문

simctl openurl만으로 Universal Link 설정을 검증할 수 있나요?

아닙니다. 앱의 라우팅 동작은 확인할 수 있지만 HTTPS 도메인, 연결 파일, 서명된 권한, 시스템 캐시는 별도 환경에서 검증해야 합니다.

딥링크를 콜드 스타트와 포그라운드 상태에서 모두 테스트해야 하는 이유는 무엇인가요?

두 상태가 서로 다른 생명주기 콜백을 사용할 수 있기 때문입니다. 한쪽만 검사하면 최초 라우팅 누락이나 중복 화면 전환을 놓칠 수 있습니다.

ZoomMini 클라우드 Mac

빌드, 테스트 및 실험에 사용할 독점 물리 노드 선택

두 가지 M4 구성이 싱가포르, 도쿄, 서울 및 홍콩 노드를 지원합니다. 실제 이용 가능 여부는 주문 시 반환되는 결과를 기준으로 합니다.

모델 선택 및 주문