BookaMac 엔지니어링 기록

클라우드 Mac에서 재현 가능한 iOS 위치 회귀 테스트 구축하기

클라우드 Mac에서 재현 가능한 iOS 위치 회귀 테스트 구축하기

배달 주소 선택, 매장 서비스 범위 판정, 지오펜스 알림 같은 기능은 개발 환경에서는 몇 번만 조작해도 정상적으로 통과하지만, 무인 CI 파이프라인에서는 간헐적으로 실패하기 쉽습니다. 원인은 대개 위치 계산 알고리즘이 아니라 시뮬레이터에 이전 작업의 좌표나 권한이 남아 있거나, 이전에 시작한 경로가 여전히 실행 중인 데 있습니다. 클라우드 Mac에서 위치 테스트를 재현 가능하게 만들려면 ‘위치’를 시뮬레이터의 환경적 배경이 아니라 명시적으로 생성하고 폐기해야 하는 테스트 입력으로 다뤄야 합니다.

검증 목표를 세 계층으로 분리하기

하나의 UI 테스트가 좌표 계산, 시스템 위치 서비스 연동, 화면 검증을 모두 담당하게 해서는 안 됩니다. 다음과 같이 세 계층으로 나누는 편이 더 안정적입니다.

  1. 일반 단위 테스트에서는 직접 생성한 좌표를 전달해 거리, 영역, 상태 전환을 검증합니다.
  2. 소수의 시뮬레이터 테스트에서는 시스템 위치 데이터가 실제로 앱에 전달되고 예상 화면을 표시하는지 확인합니다.
  3. 실제 기기에서는 백그라운드 깨우기, 신호 편차, 배터리 소모, 센서 관련 동작을 검수합니다.

먼저 비즈니스 계층에 매우 좁은 입력 경계를 정의할 수 있습니다. 예를 들어 위치 서비스가 좌표, 시간, 권한 상태만 출력하도록 설계합니다. 지오펜스 판정 로직을 뷰 컨트롤러 곳곳에 직접 분산시키지 마십시오. 이렇게 하면 대부분의 경계 조건은 시뮬레이터를 실행하지 않고도 검증할 수 있으며, 시스템 연동 부분만 simctl location에 의존하게 됩니다.

시뮬레이션 좌표는 앱이 특정 위치를 어떻게 처리하는지는 입증할 수 있지만, 실제 무선 환경에서도 같은 주기로 위치 이벤트가 전달된다는 사실까지 입증하지는 못합니다.

작업마다 독립된 시뮬레이터 준비하기

먼저 Xcode 경로를 고정한 다음, 현재 머신에 실제로 설치된 기기 유형과 런타임을 확인합니다. Xcode를 업그레이드하면 식별자가 바뀔 수 있으므로 예제의 런타임 식별자를 영구적으로 하드 코딩하지 마십시오.

sudo xcode-select -s /Applications/Xcode.app
xcrun simctl list devicetypes
xcrun simctl list runtimes

파이프라인에서는 독립된 CoreSimulator 기기 세트를 생성하고, 발급된 UDID를 현재 작업에 저장해야 합니다. 아래의 기기 유형과 런타임 식별자는 앞선 명령이 실제로 반환한 값으로 교체해야 합니다.

set -euo pipefail

DEVICE_SET="$PWD/.simulator-location-tests"
DEVICE_TYPE="com.apple.CoreSimulator.SimDeviceType.iPhone-16"
RUNTIME="com.apple.CoreSimulator.SimRuntime.iOS-18-0"

rm -rf "$DEVICE_SET"
UDID="$(xcrun simctl --set "$DEVICE_SET" create GeoTest "$DEVICE_TYPE" "$RUNTIME")"
xcrun simctl --set "$DEVICE_SET" boot "$UDID"
xcrun simctl --set "$DEVICE_SET" bootstatus "$UDID" -b

독립 기기 세트는 공용 시뮬레이터를 매번 초기화하는 방식보다 문제를 추적하기 쉽습니다. 실패한 작업의 기기 디렉터리는 일시적으로 보존하고, 성공한 작업의 디렉터리는 즉시 삭제할 수 있습니다. 병렬 작업이 이미 부팅된 동일한 기기를 두고 경합하는 문제도 방지할 수 있습니다.

고정 좌표와 권한 주입하기

고정 좌표는 ‘영역 내부’, ‘경계 통과’, ‘목표 지점에서 멀리 떨어짐’ 같은 결정론적 조건을 검증하는 데 적합합니다. 앱을 설치하고 권한을 미리 설정한 뒤 좌표를 지정하고 테스트 대상을 실행해야 합니다. 앱 실행 후에 권한을 변경하면 첫 화면에 이미 권한이 없는 상태가 기록되었을 수 있습니다.

BUNDLE_ID="com.example.LocationDemo"

xcrun simctl --set "$DEVICE_SET" privacy "$UDID" grant location "$BUNDLE_ID"
xcrun simctl --set "$DEVICE_SET" location "$UDID" set 1.290270,103.851959

xcodebuild test \
  -workspace LocationDemo.xcworkspace \
  -scheme LocationDemo \
  -destination "platform=iOS Simulator,id=$UDID" \
  -resultBundlePath "$PWD/TestResults/LocationTests.xcresult"

경계 테스트에 하나의 지점만 사용해서는 안 됩니다. 최소한 영역 내부, 경계 부근, 영역 외부에 해당하는 세 좌표 그룹을 준비해야 합니다. 또한 case1, case2처럼 의미를 감추는 이름 대신 테스트 데이터 자체에 예상 결과가 드러나도록 구성하십시오.

시나리오 입력 설계 권장 검증 항목
영역 내부 중심까지의 거리가 임계값보다 확실히 작음 영역 내부 상태가 안정적으로 유지됨
경계 부근 임계값 양쪽에 각각 한 지점 배치 비교 규칙과 반올림 방식이 일치함
영역 외부 중심까지의 거리가 임계값보다 확실히 큼 영역 내부 동작이 실행되지 않음
권한 꺼짐 위치 권한 철회 복구 가능한 안내 상태를 표시함

앱이 마지막 위치를 캐시한다면 각 테스트 케이스 전에 앱 데이터를 삭제하거나 테스트 실행 인수로 영구 캐시를 비활성화해야 합니다. 그렇지 않으면 새 좌표가 이전 값으로 덮어써질 수 있습니다.

GPX로 이동 순서 검증하기

연속 이동은 짧고 읽기 쉬운 GPX 파일로 테스트해야 합니다. 핵심 상태 변화를 일으키는 경유지만 남기고 실제 이동 경로 전체를 복제하지 마십시오. 다음 파일은 영역 외부에서 내부로 이동하는 경로를 나타냅니다.

<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.1" creator="location-ci">
  <wpt lat="1.320000" lon="103.820000">
    <name>outside</name>
  </wpt>
  <wpt lat="1.290270" lon="103.851959">
    <name>inside</name>
  </wpt>
</gpx>

경로를 실행할 때는 초 단위로 정확한 대기 시간에 의존하지 말고 이벤트 순서를 중심으로 검증해야 합니다.

xcrun simctl --set "$DEVICE_SET" location "$UDID" start "$PWD/Fixtures/enter-region.gpx"
xcodebuild test \
  -workspace LocationDemo.xcworkspace \
  -scheme LocationRouteTests \
  -destination "platform=iOS Simulator,id=$UDID"
xcrun simctl --set "$DEVICE_SET" location "$UDID" stop

앱에서는 outside → approaching → inside처럼 민감 정보를 제거한 상태 변화 순서를 기록하고, 각 변화에 단조 증가 시간을 첨부할 수 있습니다. 전체 좌표를 장기 로그에 기록하지 마십시오. 실패 증거에는 테스트 케이스 이름, 예상 영역, 실제 상태, 제한된 소수 자릿수만 남기면 충분합니다.

상태를 정리하고 실패 증거 보존하기

정리 작업이 성공 분기에서만 실행되어서는 안 됩니다. 스크립트가 시작되면 즉시 종료 핸들러를 등록해 경로를 중지하고 위치를 초기화한 뒤 기기를 종료해야 합니다. 기기 세트 삭제 여부는 테스트 결과에 따라 결정합니다.

cleanup() {
  xcrun simctl --set "$DEVICE_SET" location "$UDID" stop 2>/dev/null || true
  xcrun simctl --set "$DEVICE_SET" location "$UDID" clear 2>/dev/null || true
  xcrun simctl --set "$DEVICE_SET" shutdown "$UDID" 2>/dev/null || true
}
trap cleanup EXIT

실패 시에는 xcresult, 앱 로그, 사용한 GPX 파일, 기기 및 런타임 식별자, 테스트 시작 전 권한 상태를 보관해야 합니다. 스크린샷은 판단을 보조할 수는 있지만 구조화된 검증을 대신할 수 없습니다. 동일한 좌표에서 반복적으로 실패한다면 먼저 기기 세트가 실제로 격리되었는지, Bundle ID가 올바른지, 앱 실행 전에 권한이 부여되었는지 확인한 다음 비즈니스 계산 로직을 점검하십시오.

안정적인 위치 테스트 파이프라인은 최종적으로 다음 조건을 충족해야 합니다. 테스트 입력을 쉽게 읽을 수 있어야 하고, 시뮬레이터 상태는 일회성으로 사용해야 하며, 경로는 반드시 중지되어야 하고, 비즈니스 로직은 시스템 위치 서비스와 분리해 단위 테스트할 수 있어야 합니다. 그래야 실패가 발생했을 때 팀이 확인하는 것은 재현할 수 없는 “위치가 가끔 부정확하다”는 설명이 아니라 명확한 상태 차이입니다.

자주 묻는 질문

simctl 위치 주입만으로 실제 기기 테스트를 대체할 수 있나요?

대체할 수 없습니다. 앱 내부 상태 전환과 화면 검증에는 적합하지만 백그라운드 깨우기, 실제 신호 변동, 전력 소비는 실제 기기에서 별도로 확인해야 합니다.

전체 CI에서만 위치 테스트가 불안정해지는 이유는 무엇인가요?

공유 시뮬레이터의 이전 좌표, 권한, 실행 중인 GPX 경로가 남는 경우가 많습니다. 작업별 기기 세트를 사용하고 테스트 전후 상태를 명시적으로 초기화해야 합니다.

고정 좌표와 GPX 경로는 각각 언제 사용해야 하나요?

고정 좌표는 지역과 경계값의 결정적 검증에, GPX 경로는 연속 이동과 상태 순서 검증에 사용합니다. 경로 테스트에서 초 단위 도착 시간은 단언하지 않는 편이 안전합니다.

전용 물리 Mac mini

검증된 워크플로를 365일 지속 운영되는 클라우드 Mac으로 옮기세요

용도에 맞춰 BookaMac M4 또는 BookaMac M4 Pro를 선택하고, 주문 시 지역·대여 기간·스토리지 추가 옵션을 확인하세요.

대여 요금제 선택