본문 바로가기

파이썬으로 PC에서 모바일(블루스택, 녹스) 조작하기 -총정리-

by 머니해커_개발자 2023. 4. 15.

안녕하세요 머니해커입니다.

최근 모바일 환경에서 트래픽 작업을 하거나 화면을 제어하는 외주를 자주 받습니다.
모바일 조작 작업이 재밌기도 하고, 지금까지 짠 코드 템플릿들도 많기 때문에 가볍게 받습니다.

그래서, 이번 게시글에서는 파이썬으로 모바일 환경(블루스택 BlueStacks 5, Nox)을 조작할 수 있도록
환경 설정부터 한글 입력 등 모든 과정을 차근차근 알려드리려 합니다.

시작합니다.

1. 모바일 조작 환경 설정 - ADB, 녹스(NOX) 설치

가장 먼저, 컴퓨터와 가상 시뮬레이션(블루스택 or 녹스)를 연결할 수 있도록 ADB를 다운로드해줍니다.

ADB 설치 : https://developer.android.com/studio/releases/platform-tools?hl=ko

ADB는 안드로이드 디버그 브릿지(Android Debug Bridge)라고 해서, 우리가 스마트폰을 PC와 USB로 연결하면 쉽게 디버깅할 수 있잖아요? 그걸 가능하게 해주는 콘솔이라 보시면 됩니다.
이 ADB를 쓰면 PC와 블루스택/녹스가 연결됩니다.

ADB를 다운로드하셨다면, 환경변수에 ADB 다운로드 경로를 추가합니다.

윈도우 > 환경 변수 검색하시면 됩니다.
Path에서 새로만들기 하여, 다운로드한 adb 경로를 입력합니다.
cmd에서 adb라고 입력해보세요. 버전이 나오면 세팅 완료입니다.

cmd 콘솔에서 adb라고 입력했을 때, 다음과 같이 버전이 나오면 세팅 완료라고 보시면 됩니다.

이후, 녹스나 블루스택을 설치해서 이어서 진행합니다.

2. 해상도 설정 - 고정사용

녹스 설치가 완료되고 실행을 하시면, 해상도 설정을 해줍니다.

해상도 설정(녹스 화면, 블루스택도 동일)

녹스 프로그램 가장자리에 있는 설정 > 디스플레이 '스마트폰' 누르셔서 설정하시면 됩니다.
저는 720x1280 화면이 익숙해서 해당 해상도를 사용합니다만, 여러분께서 가로로 사용하실 땐 테블릿 모드를 해서 사용하시면 됩니다.

이번 설정이 중요한데요, 해상도를 한 번 설정하면 거기에 맞게 좌표를 설정해서 코딩하기 때문에
이후 해상도를 바꿔서 코딩하거나 하면 이전과 같은 결과물을 기대할 수 없습니다.

720x1280에서 맞춰 작업한 매크로를 1080x1920에서 돌릴 수 없다는 얘기입니다.

3-1. Pure-Python-ADB 라이브러리 사용

모바일 환경을 조작할 수 있는 라이브러리가 몇 개 있습니다만, 몇개 사용해보니 잦은 오류 발생으로 인해 pure-python-adb (ppadb)를 사용하게 됐습니다.

다음 명령어를 터미널에 입력해 ppadb 라이브러리를 설치할 수 있습니다.

pip install pure-python-adb

이후, 시뮬레이터로 돌아가 ADB 설정을 키고, 포트번호를 확인합니다. 저는 127.0.0.1:5555라고 되어있는데
아시다시피 localhost:포트번호입니다. 해당 포트번호로 ADB 사용이 가능해집니다.

ADB 설정을 키셨다면 블루스택을 재부팅하고
프롬프트 혹은 터미널을 키셔서 다음과 같은 명령어를 입력합니다.

adb 명령어 사용으로 연결 확인

adb devices //접속된 디바이스 확인. 디바이스가 없으면 공란으로 나옴
adb start-server //서버가 공란이면 서버를 스타트(adb를 통해 명령어 실행 가능하도록 설정)
adb devices //다시 접속된 디바이스 확인. 또 공란으로 뜨면 다음 단계 진행
adb kill-server //서버를 강제종료시킴
adb start-server //서버를 재실행시킴
adb devices //접속된 디바이스 재확인. 이때 디바이스 이름 확인 가능

3-2. Python에서 ADB 연결 및 명령 실행

adb 명령어를 사용해 연결된 디바이스 확인이 되면, 이어서 파이썬에서 코드를 사용해 접속하고 파이썬 코드를 작성합니다. 바로 파이썬 코드 확인하시겠습니다.

from ppadb.client import Client as AdbClient

deviceport = 5555 #블루스텍에서 adb 설정창에 있던 번호

# adb settings
adb = AdbClient(host="127.0.0.1", port=5037)
devices = adb.devices()
if not devices:
    print("디바이스를 찾을 수 없습니다.")
    quit()
device = devices[0]

if devices is not None:
    print("Adb detected")
else:
    print("Adb not detected")
    exit(0)

adb settings 부분에서 ADB 세팅을 진행하고, 이어서 adb device가 존재하면 detected를 출력합니다.
이때, 블루스택을 멀티로 실행시켜서 작업하실 수도 있는데요, 그럴 땐 devices[0], devices[1], devices[2] 이렇게 리스트로 이용하시면 됩니다.

adb 명령어로 스마트폰을 조작하려면(매크로) 다음과 같이 device.shell()을 이용해 명령어를 입력하시면 됩니다.

device.shell("am start -n com.android.chrome/com.google.android.apps.chrome.Main")
#ADB에 명령어를 보내 크롬 앱을 킴

ADB 명령어는 종류가 많아 직접 레퍼런스를 찾아보시길 권장드리며, 다음과 같은 예시를 적어두겠습니다.
1) 현재 디바이스에서 크롬 앱이 실행중인지 확인하는 코드

active_apps = device.shell('dumpsys activity activities | grep mResumedActivity')
progress = 0 # 크롬 앱 실행 중인지 확인하고, 실행 중이면 프로세스 종료
if 'com.android.chrome' in active_apps: #크롬 패키지가 현재 실행중인 앱들(덤프중)에 존재한다면
    device.shell('am force-stop com.android.chrome') #크롬을 강제중지한다.
else:
    progress = 1 #크롬을 강제중지할 필요 없음

2) 검색바를 클릭하여 구글을 입력하는 코드

device.shell("input tap 355 370")  #검색바를 클릭(탭)하는 ADB 명령어
time.sleep(2)
device.shell("input text 'rnrmf'") # '구글'을 영타로 입력하는 ADB 명령어
time.sleep(3)

이때, "input tap x좌표 y좌표"를 입력하게 되는데요, shell() 부분이 먹히지 않는다면 f"input tap x y" 이렇게 입력하시면 됩니다.

이어서, 좌표를 확인하려면 블루스택 설정 > 고급 기능 설정 > 디버깅 입력 > 포인터 위치 표시 하시면 됩니다.

해당 x, y 포인터 표시 설정을 하면, 위와 같이 x, y값이 화면 상단에 표시됩니다.
이 좌표를 따서 코드에 입력하시면 됩니다.

3-3. 영어 자판(Gboard)에서 한글 입력 문제 시

여러분께서 만약, 파이썬의 input()을 통해 한글을 입력받고, 블루스택에서 그대로 치시면 오류가 발생합니다.
이때 발생하는 오류가 한 두개가 아닌데요, 이러한 조건에서 문제를 해결하는 방법이 있어 알려드립니다.

먼저, 파이썬에서 input() 명령어를 통해 한글을 입력받고, 이 한글을 그대로 자판으로 입력하고자 하시면 아무런 값이 나오지 않습니다.

> 가령 '네이버'을 입력받고 그대로 device.shell("input text '네이버' ") 하시면, 결과적으로 ' '이렇게 나옵니다.

이 한글 문제를 해결하려면 구글 앱 중 Gboard를 설치하신 뒤, 한글을 기본 언어로 설정해놓으시면 됩니다.
하지만 여기서 또 문제가 발생하는데요, input()에서 '네이버'라고 입력 시, 'spdlqj'라고 저장되지 않고, 'ㄴㅔㅇㅣㅂㅓ' 이렇게 저장이 됩니다. 그걸 또 ADB로 입력하면, 제대로 인식을 못해 ' '이렇게 됩니다.

한글 자판의 고질적인 문제, 자모 분리(자음 모음 분리)

이건 유니코드 문제인데, 가령 'ㅟ'를 입력받으면 대체할 영어가 없어 발생하는 오류입니다.
해결 방법은 많은 것 같은데, 제가 해결한 방법은 한글 자모 분리 후, 자음과 모음을 모두 영어에 매핑하는 방법입니다.

from jamo import h2j, j2hcj #자모 분리시켜주는 라이브러리
import subprocess
korean_alphabet = { #한글 자모를 모두 영어에 매핑시킴.
    'ㄱ': 'r',
    'ㄴ': 's',
    'ㄷ': 'e',
    'ㄹ': 'f',
    'ㅁ': 'a',
    'ㅂ': 'q',
    'ㅅ': 't',
    'ㅇ': 'd',
    'ㅈ': 'w',
    'ㅊ': 'c',
    'ㅋ': 'z',
    'ㅌ': 'x',
    'ㅍ': 'v',
    'ㅎ': 'g',
    'ㅏ': 'k',
    'ㅑ': 'i',
    'ㅓ': 'j',
    'ㅕ': 'u',
    'ㅜ':'n',
    'ㅠ':'b',
    'ㅗ': 'h',
    'ㅛ': 'y',
    'ㅡ': 'm'
    ,'ㅣ':'l'
    ,'ㅐ':'o'
    ,'ㅔ':'p'
    ,'ㅚ':'hl'
    ,'ㅟ':'nl'
    ,'ㅒ':'O'
    ,'ㅖ':'P'
    ,'ㅘ':'hk'
    ,'ㅙ':'ho'
    ,'ㅝ':'nj'
    ,'ㅞ':'np'
    ,'ㅢ':'ml'
}

input()을 받을 때부터 한글을 영어로 매핑해 저장하는 방식입니다.

temp = input("검색할 키워드: ")
jamo_str = j2hcj(h2j(temp)) #자모분리
result = to_english(jamo_str) #자모 -> 영어로 매핑
search_keywords.append(result)

4. 시현 영상

앞서 언급한 shell() 명령어를 통해 작성한 코드의 구동 영상입니다.

입력받은 키워드를 블루스택에 검색하는 영상

5. 드리는 말씀

블루스택을 쓰다보면 기대하는 결과물과 실제 결과물이 다른 경우가 많은데요, 이 때문에 테스트를 여러번 해서 시간 관리가 어려워지는 문제가 생깁니다.
이러한 문제중 가장 큰 것이 한글 입력 문제입니다. 저도 처음엔 안 풀려서, 물리적으로 QEWRTY 자판을 한글로 바꾸고 x, y 좌표를 매핑하는 하드코딩 방식을 쓰려 했는데요, 이건 도저히 프로그래밍하는 사람 같지가 않아서 최소로 매핑하는 방식을 사용했습니다.
저기서 더 최적화는 되겠지만 그건 여러분의 손에 맡기며, 한글을 다루는 문제는 한 번쯤 깊게 고민해보시기 바랍니다. 한 번 문제를 해결하고 나면 다음부터는 쉽습니다!

긴 글 읽어주셔서 감사합니다.

댓글

최신글 전체

이미지
제목
글쓴이
등록일