
AI 에이전트로 Web과 스마트폰을 넘나드는 크로스 디바이스 테스트를 자동화하기【어른의 자유연구】
요약
Claude의 Computer Use 기능을 활용하여 Web과 스마트폰 앱을 넘나드는 크로스 디바이스 E2E 테스트를 자동화하는 PoC를 소개합니다. GKE 환경에서 Android 에뮬레이터를 사이드카로 구동하여 복잡한 인증 시나리오를 성공적으로 수행했습니다.
핵심 포인트
- Claude의 Computer Use를 이용한 크로스 디바이스 테스트 자동화
- GKE 내 Android 에뮬레이터 구동을 통한 클라우드 기반 테스트 환경 구축
- Web-모바일 간의 복잡한 인증 시나리오 자동 수행 확인
- AI 에이전트를 통한 DevOps 사이클의 테스트 병목 현상 해결 시도
요약(Abstract)
- Web과 스마트폰 앱을 넘나드는 E2E 테스트를 AI 에이전트(Claude의 computer use)로 자동화하는 PoC를 만들었습니다.
- 스마트폰 측은 Android 에뮬레이터를 GKE의 Job Pod 내에 사이드카(Sidecar)로 기동합니다(중첩 가상화 + KVM).
- 대표 시나리오인 「디바이스 코드 승인 로그인(Web에 표시된 코드를 스마트폰에서 입력·승인 → Web에서 로그인 완료)」이 GKE 상에서 13단계의 Tool 처리, 약 88초 만에 성공했습니다.
배경(Background)
AI 에이전트에 의한 DevOps의 가속화와 병목 현상의 이동
코딩 에이전트(Coding Agent)를 통해 구현 태스크가 빠르게 완료되는 시대가 오고 있습니다. 난해한 사양을 포함하는 기능이라도 구현만 한다면 하루가 채 걸리지 않는 일도 드물지 않을 것입니다.
그렇게 되면 DevOps 사이클 중에서 설계·구현 페이즈의 비중은 상대적으로 작아지고, 후속되는 테스트·배포·운영 페이즈의 비용이 눈에 띄게 됩니다. 구현은 순식간에 끝나는데 테스트와 확인에 시간이 걸리는 사태가 발생하고 있으며, 이것이 DevOps 사이클의 다음 병목(Bottleneck)이 되고 있다는 것이 저자의 실감입니다.

DevOps와 이를 둘러싼 AI 에이전트
아직 인간의 손길이 필요한 테스트 영역
Playwright 등의 E2E 테스트 도구의 충실함 덕분에 단일 Web 앱의 테스트는 상당히 자동화할 수 있게 되었습니다. 그럼에도 자동화가 어려운 영역은 남아 있습니다. 예를 들면 다음과 같습니다.
- Web뿐만 아니라 데스크톱 앱·태블릿·스마트폰 등 여러 디바이스를 넘나드는 테스트
- 인간의 실제 체험 피드백이 필요한 테스트(VR 등)
구체적인 예로 모바일 오더(예: 맥도날드, 스타벅스) 등을 들 수 있습니다. 스마트폰 앱이나 매장에 설치된 전용 단말기에서 주문을 서버로 보내고, 주방의 태블릿에서 해당 요청을 처리합니다. 스마트폰 앱과 Web을 넘나들기 때문에 Playwright 단독으로는 E2E 테스트를 할 수 없으며, 인간이 실제로 앱과 Web을 오가며 확인할 필요가 발생합니다.
Computer Use의 성능 향상
한편, 모델이 화면을 보고 마우스·키보드를 조작하는 Computer Use의 성능은 착실히 향상되고 있으며, 최근 Claude Opus 5에서는 Computer Use 계열 벤치마크에서 70%를 넘는 스코어가 보고되었습니다(집필 시점 기준. 자세한 내용은 공식 발표를 참조하십시오).

https://www.anthropic.com/news/claude-opus-5 에서 인용
「여러 디바이스를 넘나드는 조작」은 인간에게는 번거롭지만, 스크린샷을 보고 조작하는 에이전트에게는 「화면이 2장 있을 뿐」입니다. 그래서 이번에는 Web과 스마트폰 앱을 넘나드는 테스트를 AI 에이전트가 실행하게 하고, 이를 GKE 상의 일회성 Job으로 구동하여 실제로 DevOps 사이클의 일부로 편입할 수 있는지 확인하는 PoC를 만들어 보고자 합니다.
방법(Method)
실험의 소재: 크로스 디바이스 인증
이번에는 PoC이기도 하고 모바일 오더 그 자체를 만드는 것은 힘들기 때문에, 소재로는 「스마트폰과 Web을 넘나드는」 성질을 최소 구성으로 가진 크로스 디바이스 인증을 선택했습니다. TV나 스마트 스피커 로그인에서 익숙한 디바이스 코드 승인형(RFC 8628 스타일) 플로우입니다.
- Web(PC 측)에
XXXX-XXXX형식의 코드가 표시됨 - 스마트폰 앱 측에서 해당 코드를 입력하고 「승인」을 탭함 - Web 측이 자동으로 로그인 완료 화면으로 전환됨
인증은 소재일 뿐이며, 하네스(Harness) 자체는 시나리오에 의존하지 않는 범용 기구로서 설계했습니다.
테스트 대상 스택도 PoC용으로 직접 제작했습니다.
| 컴포넌트 | 구현 |
|---|---|
| API | Go (세션 관리·각종 인증 플로우) |
| ... |
전체 아키텍처
Android 에뮬레이터에는 KVM(하드웨어 가상화 지원)이 필요하며, 로컬의 단일 기기에서 실행할 수 있다 하더라도 팀이나 CI(지속적 통합) 환경에서 일회성으로 실행할 수 있는 형태가 되지는 않습니다. 테스트 대상 스택을 컨테이너로 기동하고, 테스트 러너(Test Runner)를 일회성 Job으로 실행하는 구성이 가장 합리적이라고 판단하여, Google Cloud의 Kubernetes 서비스인 GKE를 사용했습니다. 참고로 iPhone에서의 테스트도 시도해보고 싶었으나, iOS 시뮬레이터는 macOS 호스트가 필요하여 환경 준비가 어려웠기 때문에, 이번에는 스마트폰 디바이스를 Android로 한정했습니다.

테스트 대상 스택: 상기 web / api / dex / mailpit -
하네스 층 (Harness Layer) (TypeScript): Playwright(Web)와 adb(Android Debug Bridge. Android 단말을 외부에서 조작하는 표준 CLI)를 제어하는 유일한 층. UiDriver라는 단일 추상화를 통해 플랫폼 차이를 격리하고, 스크린샷 획득·탭(Tap)·텍스트 입력을 통일된 인터페이스로 제공합니다 -
에이전트 루프 (Agent Loop) (TypeScript): Anthropic Messages API의 computer use 도구를 사용하여, 스크린샷을 전달하고 다음 액션을 받는 루프 -
하네스의 설계
결정적인 조작은 하네스 측에서, 의사결정이 필요한 판단은 에이전트로서 역할을 나누어 설계합니다. 구체적으로는 테스트 사용자 생성과 같이 매번 동일한 결과가 나와야 하는 것은 하네스 측에서 담당하고, 화면을 보고 다음 조작을 결정하는 것은 에이전트의 역할로 정의했습니다.
UiDriver
에이전트 루프에서는 다음과 같이 UI 조작을 추상화하여 정의하며, web 조작과 android 조작을 이 인터페이스를 통해 실행하도록 하고 있습니다.
interface UiDriver {
readonly target: TargetKind; // "web" | "android"
screenSize(): ScreenSize;
...
Web 측은 Playwright를, Android 측은 adb를 사용합니다. adb는 input tap이나 input text와 같은 이벤트를 생성할 수 있습니다.
테스트 시나리오의 관리와 실행
테스트 시나리오는 「이름·대응 타겟(web / android / cross)·에이전트에게 전달할 프롬프트 생성 함수」의 세트로 선언적으로 정의하며, CLI의 --scenario / --target 옵션으로 선택하여 실행합니다. 프롬프트에는 화면에 보이는 정보만을 작성합니다.
interface Scenario {
name: string;
targets: RunTarget[];
...
에이전트 루프의 설계
Tool 설계
이번에는 AI 에이전트의 권한을 최대한 작게 유지하도록 설계했습니다. AI의 폭주, 외부 공격에 의한 보안 리스크, 테스트 재현성 등의 관점을 고려하여 에이전트(모델)에게 보여주는 도구는 다음 4가지로 압축했습니다.
: Anthropic 표준의 computer use 도구(computer, computer_20251124). screenshot / click / type / key / scroll 등을 실행합니다 -
: OTP나 IdP 비밀번호와 같은 비밀 정보를 입력하는 도구. fill_secret. 하네스 측이 값을 해결하여 직접 입력하며, 값 자체는 모델에게 전혀 반환하지 않습니다. 라이브 코딩(Live Coding) 시 .env 파일의 내용을 읽게 하지 않고 이용하는 것과 유사한 메커니즘을 구현하는 이미지입니다. -
: 에이전트의 완료 자기 보고 report_done -
: 크로스 디바이스 시나리오 한정. switch_device. web / android를 전환하며, 전환된 대상의 스크린샷을 반환합니다
위의 Tool 설계에 대한 핵심 포인트는 다음과 같습니다.
모델에는 스크린샷만 전달한다. 인간 테스터와 동일한 조건으로 "화면을 보고 조작"하게 만드는 구조입니다. DOM이나 data-testid, CSS 셀렉터는 일절 전달하지 않습니다. 이를 통해 구현 내부(Implementation Details)에 의존하지 않는 범용적인 테스트가 가능해집니다.
테스트의 합격 여부 판정은 모델의 자기 보고를 신뢰하지 않는다. report_done
의 「성공한 것 같습니다」는 판정에 사용하지 않고, 하네스(Harness)가 독립적으로 3점 어서트(3-point assert: 객관적이고 명확한 복수의 기대치를 스크립트로 결정적으로 확인)를 통해 합격 여부를 결정합니다. 에이전트가 "한 것 같은 기분"에 빠지는 것을 방지하는 메커니즘입니다.
참고
다음 블로그에서 Google이 멀티 에이전트(Multi-agent)를 통해 영화를 제작하려는 시도를 했다고 보고했습니다. 그중 일부 사건으로, AI 에이전트로부터 영화 제작 완료 보고가 있었으나 실제 결과물은 94 Byte 파일이었다고 합니다 (웃음). 이 사례를 통해서도 결과물을 검증하거나, 검증 가능한 메커니즘을 구현하는 것은 매우 중요합니다.
모크(Mock)와 테스트 전용 엔드포인트
실제 Google 로그인이나 실제 메일 전송을 자동화 대상으로 삼으면 외부 서비스에 폐를 끼칠 가능성이 있습니다. 그래서 외부 의존성은 모두 모크(Mock)로 교체했습니다.
SSO: Dex(가짜 IdP) + 일회용 정적 테스트 사용자
메일 OTP: Mailpit이 SMTP를 수신하고, REST API를 통해 본문에서 OTP를 추출
시각: 서버에 TEST_MODE 한정의 테스트용 API를 준비하여, "OTP 유효기간 만료" 등도 실제 시간을 기다리지 않고 서버 시각의 모크(Mock)로 재현
이에 맞춰 테스트 전처리 및 검증용으로 서버에 /__test/* 계열의 REST API 엔드포인트를 준비했습니다.
/__test/reset: 모든 데이터와 모든 세션을 삭제하여, run마다 완전히 깨끗한 상태에서 시작
/__test/seed: UI를 거치지 않고 테스트 사용자를 직접 생성
/__test/whoami 등: 테스트 합격 여부 판정을 위해 서버 측 세션의 실재를 UI 외부에서 확인
이것들은 환경 변수가 TEST_MODE=1일 때만 등록되며, 운영 환경에 준하는 구성에는 존재하지 않습니다.
GKE 상에서의 실행 토폴로지(Topology)
로컬(docker compose + 로컬 에뮬레이터)에서 동작하는 것을 GKE 상의 "run마다 일회용으로 사용하는 Job Pod"로 옮깁니다.
에뮬레이터 전용 노드와 중첩 가상화(Nested Virtualization) · KVM
Android 에뮬레이터를 실용적인 속도로 구동하려면 KVM(하드웨어 가상화 지원)이 필요하지만, GKE의 노드 자체가 VM입니다. 그래서 **VM 안에서 VM을 구동하는 "중첩 가상화(Nested Virtualization)"에 대응하는 노드 풀(Node Pool)"**을 만듭니다. 다음 명령과 같이 --enable-nested-virtualization 옵션을 부여하여 에뮬레이터 전용 노드 풀을 생성합니다.
gcloud container node-pools create emulator-pool \
--machine-type n2-standard-4 \
--enable-nested-virtualization \
...
- 중첩 가상화는 Intel 계열 머신 타입만 지원합니다 (집필 시점 기준)
- 테스트 실행 시에만 사용하므로 Spot VM + 오토스케일(Autoscale) 0~1대로 비용을 억제합니다
- 테인트(Taint: 지정한 Pod 이외에는 이 노드에 올리지 않기 위한 표식)를 사용하여, 이 풀을 에뮬레이터 Job 전용으로 격리합니다
컨테이너에서 /dev/kvm을 사용하게 하는 방법으로, 이번에는 hostPath 볼륨 + privileged: true라는 최단 경로를 선택했지만 주의점은 존재합니다. (리스크는 후술할 주의점을 참조).
에뮬레이터의 네이티브 사이드카(Native Sidecar)
에뮬레이터 컨테이너를 Job의 일반적인 컨테이너로 나란히 배치하면, 테스트 완료 후에도 에뮬레이터가 계속 동작하여 Job이 Complete 상태가 되지 않습니다. Kubernetes의 네이티브 사이드카(Native Sidecar, initContainers에 두면서 restartPolicy: Always를 지정하는 방식)로 기동함으로써, 메인 러너(Runner) 컨테이너가 종료되면 사이드카도 함께 종료되도록 합니다.
kubernetes Job 투입 및 결과 회수
에이전트 실제 실행은 시나리오와 타겟이 가변적이므로, Job 매니페스트(Manifest)를 템플릿화하고, 투입 스크립트가 ${SCENARIO} 등을 치환하여 kubectl create를 수행합니다. 실패 시 API 토큰 과금이 이중으로 소비되지 않도록 backoffLimit: 0
(재시도 없음) 등의 설정도 추가합니다.
apiVersion: batch/v1
kind: Job
spec
...
실행 로그나 결과 JSON은 Pod의 로컬 디스크에만 존재하기 때문에, 테스트 종료 후에도 일정 시간 동안 Pod를 유지하여 (sleep으로 HOLD), 그 사이에 kubectl cp로 성과물을 회수하여 리포트에 합류시킵니다.
⚠️ 주의점
외부 서비스에 대한 Bot 행위 회피
개발에 임하는 도중, 에이전트에 의한 자동 조작을 실제 외부 서비스로 향하게 할 경우 트러블이 발생할 가능성이 예상되었습니다. 예를 들어 다음과 같습니다.
- reCAPTCHA 등의 bot 탐지에 걸려 테스트 실시가 불가능해짐
- 많은 서비스의 이용 약관에서 Bot 행위(자동 로그인 등)를 금지 또는 제한함 (예: X (구 Twitter) 등)
- 외부 서비스의 인증 정보·자격 증명(Credential)을 테스트 환경에 흘리는 것 자체가 애초에 유출 리스크가 됨
따라서 자동 테스트에서는 기본적으로 모크(Mock) (이번 경우에는 Dex와 Mailpit)를 사용하는 것이 적절하다고 판단하여 설계했습니다.
관련 사례:
- 사이버 능력 검증 중에 OpenAI의 모델이 Hugging Face의 데이터베이스를 공격했다는 보도가 나온 사례도 있습니다. 에이전트에게 부여하는 도달 범위와 권한은 테스트에서도 최소한으로 좁혀야 합니다.
특권 컨테이너(Privileged Container)의 리스크
privileged: true 컨테이너는 노드를 실질적으로 탈취할 수 있는 권한을 가집니다. 이번에는 다음과 같은 조건에서 허용했습니다.
- Kubernetes의 taint로 격리된 전용 노드 풀에, 일회용 단일 Job Pod만 올라감 (동거하는 피해 대상이 없음)
- 노드 자체도 테스트 시에만 기동하는 Spot VM
장기간 운용할 경우에는 KVM device plugin으로 /dev/kvm을 할당 가능한 리소스(Allocatable resource)로 공개하고, Kubernetes Pod의 매니페스트에서 privileged: true를 제거하는 것이 본래 적절합니다.
참고:
결과(Result)
GKE 상에서의 실제 실행 결과
먼저 LLM을 사용하지 않는 결정적인 셀프 테스트(Harness 자체의 검증)가 GKE 상에서 Web 7/7, 모바일 10/10 성공했습니다. 그 위에 에이전트 실제 실행의 대표 3 run의 실측값이 다음과 같습니다 (모델은 Claude Sonnet 3.5, 각 1 run).
| 시나리오 | 타겟 | 결과 | 스텝 수 | 소요 시간 |
|---|---|---|---|---|
| demo (데모 로그인) | web | ✅ success | 8 | 약 25초 |
| otp (메일 OTP 등록·재로그인) | android | ✅ success | 25 | 약 116초 |
| device_code (크로스 디바이스 승인) | web + android | ✅ success | 13 | 약 88초 |
device_code run에서는 에이전트가 PC 화면에 표시된 코드 H8DW-58BK를 읽고, switch_device로 스마트폰으로 전환하여 코드를 입력·승인한 뒤, 다시 Web으로 돌아와 로그인이 완료되었음을 확인했습니다. 인간이 하는 것과 완전히 동일한 절차입니다.
비용 체감은 다음과 같습니다.
- 에이전트 1 run의 토큰 과금: 대략 $0.1~0.3 (프롬프트 캐싱 + 오래된 스크린샷 생략을 통해 절감)
- GKE 클러스터: 유휴 시 약 $1.5/월, 테스트 세션 중 약 $0.1~0.35/시간
동작 데모
Kubernetes Job의 모습은 시각화할 수 없지만, 로컬에서 실행했을 때의 이미지는 다음과 같습니다.
요약 및 향후 전망(Summary and Future Outlook)
Web과 스마트폰에 걸친 테스트를 AI 에이전트로 구현하는 PoC에 대해 소개해 보았습니다.
구현하며 느낀 점은 다음과 같습니다.
- 간단한 설정이지만, 스크린샷에 의존하여 에이전트가 디바이스를 전환하며 크로스 디바이스 (Cross-device) 플로우를 완수할 수 있다
- 툴 (Tool)을 잘 설계함으로써 AI 에이전트의 폭주와 보안 리스크 등을 회피하면서 kubernetes의 Job으로서 자동 테스트를 실행할 수 있다
- AI의 사고 횟수에 따라 다르겠지만, 비용은 1 run 당 $0.1~$0.3 정도로, CI에 통합할 수 있는 가능성이 있다
- 현재 기능을 더욱 확장한다면 고도화된 테스트 자동화를 실현할 수 있는 비전이 있다
몇 번이고 말씀드리지만, 이번에는 어디까지나 PoC이며 향후에는 다음과 같은 것들에도 도전해보고 싶습니다.
- 에이전트 행동의 감사 (Audit): 에이전트의 조작을 OpenTelemetry로 트레이스 (Trace) 하여, 나중에 감사할 수 있도록 함 (조만간 조사하여 속편 기사로 작성할 예정입니다)
- KVM device plugin화: privileged를 제거하고,
/dev/kvm을 할당 가능한 리소스 (allocatable resource)로 취급하여 보안 리스크를 더욱 낮춤 - 시나리오 확충: 푸시 승인형 로그인 등, 구현은 완료되었으나 아직 실행되지 않은 시나리오의 검증
- CI 통합: 예를 들어 PR이 머지(Merge)되는 타이밍(혹은 PR 전용 kubernetes namespace로 구분된 독자적인 검증 환경)에서 자동으로 테스트가 실행되도록 할 수 있다면 좋을 것 같습니다.
- 더 많은 권한을 안전하게 부여하고 싶음: 현재 테스트 메커니즘으로 할 수 있는 일은 한정되어 있습니다. 사실은 부하 테스트 (Load Test), 카오스 엔지니어링 (Chaos Engineering), 배타 제어 (Exclusive Control) 테스트 등 수많은 테스트를 자동화하고 싶습니다.
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기