안드로이드 폰을,
한 줄씩.
Python, Node.js, 셸, AI 에이전트에서
실제 폰을 움직입니다.
ADB도, USB 케이블도, 루팅도 필요 없습니다.
pip install droidlinenpm install droidline- 아직 보낸 줄이 없습니다. 첫 줄을 보내거나 스크립트 전체를 실행해 보세요.
d.launch("dev.droidline.demo")droidline-fakephone이 띄우는 데모 앱과 같은 화면을 브라우저에서 흉내 냈습니다. 요청과 응답은 실제 줄 형식 그대로입니다.
폰이 먼저 접속합니다.
내 코드는 localhost에만 말합니다.
PC에서 droidline serve가 돌고, 폰의 Droidline 앱이 거기로 접속합니다. 그래서 폰은 포트를 열지 않고 adb forward도 필요 없습니다. 내 코드는 localhost:8780에 JSON 한 줄을 보내고 한 줄을 받습니다.
- 종단간 암호화
- 핸드셰이크 뒤의 모든 줄을 P-256 키 교환과 AES-256-GCM으로 암호화합니다.
- 기다림은 폰이 처리
- touch는 대상이 뜰 때까지 최대 10초 기다리고, 필요하면 알아서 좌표 탭으로 대체합니다.
- 권한 세 가지, 루팅 없음
- 접근성 서비스, 키보드, 필요할 때만 쓰는 앱별 VPN. Shizuku도 adb도 쓰지 않습니다.
좌표 대신
요소를 집으세요.
dump()는 화면을 트리로 돌려줍니다. 요소마다 text, id, desc가 있습니다. 속성과 값을 주면 touch가 요소를 기다렸다가 누르고, 클릭이 거부되면 그 요소의 bounds 중심을 대신 탭합니다.
폰에서 요소를 고르세요
dump()에 나오는 이 요소
요소를 고르면 속성이 보입니다.요소를 고르면 속성이 보입니다.어느 언어에서든
이름은 하나.
touch는 SDK에서도, 통신 규격에서도, CLI에서도, MCP 툴에서도 touch입니다. 모두 commands.json 한 파일에서 만들어지므로 명령을 하나 추가하면 모든 언어에 한 번에 반영됩니다.
pip install droidline
from droidline import connect
d = connect()
d.touch("text", "로그인")npm install droidline
import { connect } from "droidline";
const d = await connect();
await d.touch("text", "로그인");droidline touch text "로그인"curl -X POST localhost:8780/devices/_/touch \
-H 'content-type: application/json' \
-d '{"by":"text","value":"로그인"}'{"id":1,"cmd":"touch","by":"text","value":"로그인"}localhost:8780에 TCP 소켓을 열고, JSON 한 줄을 쓰고, 한 줄을 읽으면 됩니다.
가이드 읽기: 그 외 모든 언어{
"mcpServers": {
"droidline": { "command": "droidline", "args": ["mcp"] }
}
}MCP 클라이언트에 이 설정을 넣으세요. Claude, Cursor 같은 에이전트가 모든 명령을 툴로 쓰고, dump로 화면을 봅니다.
가이드 읽기: MCP선반 가득한 폰도
PC 한 대로.
PC 한 대가 여러 폰을 다룹니다. 한 폰에 보낸 명령은 순서대로, 다른 폰끼리는 동시에 실행됩니다.
shelf-01 온라인 lan
같은 와이파이: 폰이 UDP 브로드캐스트로 PC를 찾아 바로 접속합니다.
d = dl.device("shelf-01")
d.launch("com.android.chrome")- 일련번호 대신 이름폰에 이름을 한 번 붙이면(shelf-01) 모든 인터페이스에서 그 이름으로 부릅니다.
- 앱마다 프록시고른 앱만 로컬 VPN을 거쳐 socks5나 http로 보냅니다. 폰마다 업스트림 하나.
- 알림은 이벤트로wait_notification으로 하나를 기다리거나, 서명된 웹훅으로 전부 받습니다.
- 두 번 실행되지 않음모바일 데이터가 끊겼던 폰은 끊긴 지점부터 이어 가고, 보내던 명령을 반복하지 않습니다.
와이파이 밖에서도.
모바일 데이터의 폰은 통신사 NAT 뒤에 있어 들어오는 연결을 받을 수 없습니다. 그래서 폰은 항상 바깥으로 접속합니다. 내 네트워크에 맞는 경로를 고르세요. 폰은 경로를 순서대로 시도하고, 같은 와이파이가 잡히면 그쪽으로 돌아옵니다.
핸드셰이크 이후의 모든 줄은 종단간 암호화됩니다. 터널이나 릴레이는 암호문만 전달합니다. 원격 연결 가이드
- 다른 앱이 공개하지 않은 화면은 열 수 없습니다. launch는 그 앱의 시작 화면으로 엽니다.
- PIN, 패턴, 비밀번호 잠금 화면은 풀 수 없습니다.
- Android 15 이상에서는 알림 속 인증번호를 읽을 수 없습니다. 도착은 알 수 있고, 번호는 앱에서 읽습니다.
- 게임이나 일부 커스텀 UI처럼 요소를 내보이지 않는 화면은 요소로 누를 수 없습니다. 이런 곳은 tap 좌표와 color(x, y)를 씁니다.
- 설정 화면이 필요한 명령은 즉시 끝나지 않습니다. 강제 종료, 데이터 삭제, 네트워크 전환은 버튼을 대신 누르느라 몇 초 걸립니다.
ADB 도구, 폰 안 스크립트와
무엇이 다른가.
| Droidline | ADB 기반 도구uiautomator2 3.x, Appium UiAutomator2 | 폰 안에서 도는 스크립트Auto.js, AutoX.js | |
|---|---|---|---|
| ADB 필요 | 아니요 | 예 | 아니요 |
| 내 코드가 도는 곳 | 내 PC | 내 PC | 폰 |
| 언어 | 어떤 언어든: SDK, HTTP, 소켓 | 도구가 제공하는 클라이언트 라이브러리 | JavaScript |
| 더 잘하는 것 | 와이파이나 모바일 데이터로 여러 폰을 케이블 없이 | 앱 설치, adb shell, 계측 테스트, 비공개 화면 | PC 없이 단독 실행 |
처음에
많이 묻는 것.
ADB가 한 번이라도 필요한가요?
아니요. APK 파일로 앱을 설치하고, 폰 설정에서 권한을 켜고, QR 코드나 6자리 코드로 페어링하면 끝입니다. Android 13 이상에서는 앱의 제한된 설정을 한 번 허용해야 하며, 앱이 위치를 안내합니다.
폰이 모바일 데이터일 때도 되나요?
됩니다. 폰이 항상 바깥으로 접속하므로 통신사 NAT 뒤에서도 동작합니다. 포트포워딩, Cloudflare Tunnel 같은 터널, 또는 Cloudflare Workers나 VPS에 직접 배포한 릴레이를 쓰세요. 공용 릴레이는 운영하지 않습니다.
릴레이나 터널이 내 화면이나 명령을 볼 수 있나요?
없습니다. 두 줄짜리 핸드셰이크 뒤의 모든 줄은 내 PC와 내 폰만 가진 키로 AES-256-GCM 암호화됩니다. 릴레이가 보는 것은 기기 ID, 크기, 시각뿐입니다.
어떤 안드로이드 버전을 지원하나요?
Android 9(API 28) 이상입니다. 일부 기능은 더 높은 버전이 필요합니다. 앱별 프록시는 Android 10, 접근성 스크린샷은 Android 11부터이며, 그보다 낮으면 처음 한 번 화면 캡처 허용을 묻습니다.
구글 플레이에 있나요?
없습니다. 서명된 APK를 GitHub 릴리스에 올리고, 새 버전이 나오면 앱이 알려 줍니다.
루팅이나 Shizuku가 필요한가요?
필요 없습니다. 모든 기능이 접근성 서비스, Droidline 키보드, 안드로이드 VPN API로 동작하며, 모두 사용자가 허락하면 어떤 앱이든 쓸 수 있는 기능입니다.
AI 에이전트가 쓸 수 있나요?
있습니다. droidline mcp가 모든 명령을 MCP 툴로 내놓습니다. 에이전트는 dump로 화면을 보고 touch, input, scroll_to로 조작합니다.
명령 실행 중에 PC가 재시작되면 어떻게 되나요?
폰은 확인받지 못한 응답을 보관했다가 다시 접속하면 다시 보냅니다. 실행 중이던 명령은 두 번 실행되지 않습니다. 폰 앱 자체가 재시작됐다면 AGENT_RESTARTED 에러로 알려 주므로 직접 판단할 수 있습니다.
첫 폰을
페어링해 보세요.
- PC에서 서버 실행
droidline serve - 앱 설치 후 페어링
droidline pair - 명령 보내기
droidline touch text "OK"