mobile-test-automation

1 개의 포스트

toss

토스의 디바이스 팜 만들기 (새 탭에서 열림)

네뷸라는 팀별로 흩어져 운영하던 실기기 테스트 환경을 중앙 플랫폼으로 통합해, 누구나 API 호출 한 번으로 실제 스마트폰을 제어할 수 있도록 만든 사내 디바이스 팜입니다. Appium 대신 자체 드라이버를 개발해 속도와 확장성을 높였고, 실시간 미러링·보안·24시간 운영 안정성까지 직접 구축했습니다. 그 결과 15대에서 시작한 팜은 100대를 넘어 수백 대 규모로 확장되며 전사 공용 테스트 인프라로 자리 잡았습니다. ## 팀별 디바이스 팜의 한계 - 각 팀이 맥북이나 맥미니에 5~10대의 기기를 직접 연결하고 관리했습니다. - Appium 설정, 기기 인식, OS 버전 대응, 연결 장애 복구를 팀마다 반복해야 했습니다. - 기기 관리와 테스트, 보안·컴플라이언스까지 개발자가 함께 맡아야 했습니다. - 팀별 자원이 격리되어 회사 전체의 기기를 효율적으로 공유하기 어려웠습니다. - 네뷸라는 이러한 운영 부담을 중앙화하고 전문 플랫폼으로 이전하기 위해 시작됐습니다. - 맥미니 5대와 기기 15대, 개발자 1명으로 시작해 1년간 100대 이상으로 성장했습니다. ## API 한 번으로 실기기 제어 - 사용자는 기기가 어느 호스트에 연결됐는지, ADB나 Xcode를 어떻게 설정했는지 알 필요가 없습니다. - `occupy` API로 조건에 맞는 기기를 점유한 뒤 액션 API를 호출하면 됩니다. - 예를 들어 다음과 같은 흐름으로 기기를 사용할 수 있습니다. - Android 기기 점유 - 좌표 `(540, 1200)` 클릭 - 테스트 종료 후 기기 반환 - 예약, 케이블 연결, 로컬 환경 설정을 숨기고 단순한 인터페이스를 제공하는 것이 네뷸라의 핵심 가치입니다. ## 네뷸라의 4계층 아키텍처 - **클라이언트** - 웹 프런트엔드, SDK·CLI, 직접 API 호출 등 다양한 접근 방식을 제공합니다. - **서버** - 기기 발견·점유·할당·테스트 실행을 관리하는 오케스트레이션 계층입니다. - 테스트 요청은 Kafka로 전달되고 여러 Runner가 분산 처리합니다. - `occupy · assign · release` 기반 분산 락으로 한 기기를 여러 테스트가 동시에 사용하는 문제를 방지합니다. - **에이전트** - iOS용 Mac mini와 Android용 Linux 호스트에서 실행됩니다. - ADB·Xcode로 연결된 기기를 자동 발견하고 서버 요청을 로컬 기기로 전달합니다. - **기기 계층** - 기기마다 controller server와 controller runner가 동작합니다. - 실제 화면 클릭, 텍스트 입력 등의 동작은 이 계층에서 수행됩니다. ## Appium 대신 개발한 Nebula Driver ### 빠른 명령 처리 - Appium과 Android 기기에서 명령 지연을 비교한 결과, 클릭·입력 작업에서 네뷸라가 10배 이상 빠른 경우가 있었습니다. - Appium은 동작 전 화면이 안정될 때까지 기다리는 `waitForIdle`을 사용해 견고성을 높입니다. - 네뷸라는 화면이 진행 중이어도 노드에 바로 명령을 전달하고 즉시 반환하는 속도 우선 방식을 택했습니다. - `waitForIdle`을 끄면 성능 격차가 2~3배 수준으로 줄어들지만, 실시간 조작이 중요한 네뷸라에는 속도 중심 설계가 적합했습니다. ### Stateless 구조 - Appium은 세션 기반이라 세션 생성에 약 15~40초가 걸릴 수 있습니다. - 기기 수가 늘수록 세션 생성 실패와 세션 관리 비용도 증가합니다. - 네뷸라는 기기 컨트롤러를 미리 실행해 두고, 상태 없는 HTTP 호출을 받는 구조를 사용합니다. - 세션 시작 비용과 세션 장애를 줄이고, 대규모 기기 운영에 유리한 구조를 만들었습니다. ### 사내 환경에 맞춘 확장 - 자체 인터페이스를 소유하므로 필요한 기능을 직접 추가할 수 있습니다. - 한글·이모지 입력을 지원하는 자체 IME를 구현했습니다. - 토스 앱 전용 신호 트리거를 추가할 수 있습니다. - 사내 앱센터와 연동해 pre-release 빌드를 기기에 바로 설치할 수 있습니다. - 보안 정책도 드라이버 규격 안에서 강제할 수 있습니다. - Android는 ADB·UiAutomation, iOS는 Swift·XCTest를 기반으로 구현하고, OpenAPI 스펙으로 Go·TypeScript 코드를 자동 생성했습니다. ## 실시간 화면 미러링 - 네뷸라는 정해진 테스트 스텝만 실행하는 도구가 아니라, 사용자가 화면을 보면서 동시에 조작할 수 있어야 했습니다. - 따라서 실시간 조작과 실시간 영상 스트리밍을 Android·iOS 모두에서 해결해야 했습니다. ### Android 미러링 - scrcpy는 Android 화면을 데스크톱 앱에 보여주는 데 적합하지만, 서버를 거쳐 여러 브라우저에 배포하는 구조에는 맞지 않았습니다. - 네뷸라는 scrcpy의 인코딩 방식을 참고하되 자체 미러링 경로를 구현했습니다. - `SurfaceControl`로 가상 디스플레이를 만들고 `MediaCodec`으로 H.264 영상을 인코딩합니다. - 인코딩된 영상은 브로드캐스터를 통해 여러 브라우저 시청자에게 전달됩니다. ### iOS 미러링 - iOS는 Android처럼 화면을 자유롭게 추출하기 어렵고 USB 사용 방식에도 제약이 있습니다. - 기존 QVH·Appium MJPEG 방식은 화면을 보면서 동시에 조작하는 요구를 충족하지 못했습니다. - QuickTime Player의 iOS 화면 캡처 방식과 유사한 경로를 USB 독점 없이 내재화했습니다. - 그 결과 조작과 미러링을 동시에 수행할 수 있게 됐습니다. - Android와 iOS 모두 실시간 H.264·브로드캐스팅 경로로 통일해 브라우저에서 여러 기기를 한 번에 볼 수 있습니다. ## 중앙화로 강화한 보안과 컴플라이언스 - 팀별 운영에서는 개발자가 테스트와 기기 관리, 보안 준수를 모두 책임져야 했습니다. - 네뷸라 팀은 사내 보안팀과 협력해 모바일 기기 팜 운영 기준을 정의했습니다. - 중앙 플랫폼에 보안 정책을 적용해 모든 기기에 동일한 기준을 일괄 반영할 수 있게 했습니다. - 사용자는 보안 요건이 적용된 환경에서 테스트에만 집중할 수 있습니다. ## 24시간 운영을 위한 안정성 ### 하드웨어 운영 - USB 연결 안정성, 케이블·허브 선택, 전원 공급, 서버실 설계를 직접 검증했습니다. - 물리적 장애를 완전히 제거할 수 없기 때문에 사람의 대응 체계와 이중화를 함께 준비하고 있습니다. ### 소프트웨어 운영 - 호스트별 컨트롤러와 미러링 프로세스를 오케스트레이션합니다. - 프로세스가 종료되더라도 자동으로 복구되도록 설계했습니다. - 서버·에이전트·컨트롤러·미러링 구성요소에 무중단 배포를 적용했습니다. - 기기 상태, 프로세스 상태, 서버 성능을 함께 관측하는 모니터링 체계를 구축하고 있습니다. - 여러 팀이 상시 사용하는 공용 인프라이므로 안정성을 핵심 기능으로 취급합니다. ## API 위에 만들어진 테스트 생태계 - 기기 15대에서 100대 이상, 수백 대 규모로 확장 중이며 24시간 운영됩니다. - 웹페이지에서는 미러링 화면을 보며 클릭으로 테스트 스텝을 만들 수 있습니다. - SDK로 E2E 테스트 코드를 작성하고, CLI로 터미널·CI/CD·AI 에이전트에서 기기를 제어할 수 있습니다. - 제품 로그가 기대대로 기록되는지 검수하는 시스템에도 활용됩니다. - AI 에이전트가 API를 호출해 테스트 스텝을 직접 판단하고 실행할 수도 있습니다. - 하나의 공개 API를 기반으로 여러 도구와 활용 사례가 자연스럽게 확장됐습니다. - 사용자 사례에 따르면 Appium 기반 테스트를 이전한 뒤 실행 속도가 크게 향상됐고, 수동 검수 시간이 30~40분에서 10분 이내로 줄었습니다. 네뷸라의 사례는 기기 수를 늘리는 것보다, 복잡한 하드웨어와 운영 문제를 단순한 API 뒤로 숨기는 것이 중요하다는 점을 보여줍니다. 비슷한 플랫폼을 구축한다면 초기부터 기기 점유 모델, 실시간 미러링, 보안 정책, 자동 복구와 무중단 배포를 함께 설계하고, 내부 사용자가 쉽게 확장할 수 있는 단일 API를 중심에 두는 것이 좋습니다.