실기기 테스트를 자동화하다 보면 검색창에 한글을 넣어야 할 때가 옵니다. 그런데 adb shell input text는 한글을 받으면 NullPointerException으로 죽습니다. ASCII 문자만 키 매핑이 있기 때문입니다. 저는 이 한 가지 때문에 앱 하나를 통째로 "자동 검증 불가"로 분류했다가, 나중에 키보드 자판을 좌표로 직접 탭하는 방법으로 뚫었습니다.
안드로이드 앱을 여러 개 운영하면서 배포 전 실기기 점검을 스크립트로 돌립니다. 그중 한 앱은 음식 이름을 검색해 칼로리를 찾는 앱이라, 검색 기능을 확인하려면 반드시 한글을 입력해야 했습니다.
adb shell input text "김치"
결과는 이렇습니다.
java.lang.NullPointerException
at com.android.commands.input.Input...
왜 ASCII만 되나
input text는 문자열을 그대로 앱에 꽂아 주는 명령이 아닙니다. 문자를 키 이벤트로 바꿔서 보냅니다. 그 변환표가 KeyCharacterMap인데, 이건 물리 키보드의 자판 배열을 표현한 것이라 알파벳·숫자·기호까지만 들어 있습니다.
한글에 해당하는 키는 표에 없습니다. 찾지 못한 자리가 null로 남고, 그걸 그대로 쓰려다 NullPointerException이 납니다. 버그라기보다 애초에 지원 범위 밖입니다. 그래서 문자열을 바꿔 넣는 우회(공백을 %s로 바꾸는 식)로는 해결되지 않습니다. 그건 ASCII 안에서의 이야기입니다.
뚫는 방법 — 자판을 좌표로 누른다
사람이 폰에서 한글을 칠 때 무엇을 합니까. 키보드를 띄우고 자판을 누릅니다. adb shell input tap은 좌표를 누르는 명령이고, 이건 한글과 아무 상관이 없습니다. 키보드 자판의 좌표를 알아내서 순서대로 누르면 됩니다.
1. 먼저 입력칸을 탭해서 키보드를 올린다
adb shell input tap 540 300 # 검색 입력칸
# 키보드가 올라올 시간을 준다
이게 빠지면 뒤의 탭이 전부 허공을 누릅니다.
2. 자판 좌표는 스크린샷을 눈으로 읽어서 구한다
uiautomator dump로 받은 XML에 자판 키가 하나도 나오지 않습니다. 평소처럼 노드를 찾아 좌표를 얻는 방법이 여기서는 통하지 않습니다.
그래서 스크린샷을 찍어 눈으로 읽습니다.
adb shell screencap -p /sdcard/kb.png
adb pull /sdcard/kb.png
받은 그림에서 각 키의 중심 좌표를 재면 됩니다. 제 기기(삼성 SM-N960N, 1080×2220, 천지인 자판) 기준으로는 이렇게 나왔습니다.
| 행 | 키 | 좌표 (x, y) |
|---|---|---|
| 모음 | ㅣ | 145, 1451 |
| 모음 | · | 409, 1451 |
| 모음 | ㅡ | 671, 1451 |
| 자음 1행 | ㄱ … ㅌ | 80 … 736, y=1628 |
| 자음 2행 | ㅂ … ㅊ | 80 … 736, y=1805 |
| 자음 3행 | ㅇ | 343, 1983 |
| 자음 3행 | ㅁ | 474, 1983 |
같은 행의 키들은 등간격이라 양 끝만 재면 가운데는 계산으로 나옵니다. 이 숫자를 그대로 쓰지는 마세요. 기기 해상도와 자판 종류(천지인이냐 쿼티냐)에 따라 전부 달라집니다. 방법이 요점이고 숫자는 예시입니다.
3. 자모 순서대로 탭한다
천지인은 모음을 ㅣ · ㅡ 조합으로 만들기 때문에, 칠 순서를 자모 단위로 풀어야 합니다. "김치"는 이렇게 됩니다.
# 김 = ㄱ ㅣ ㅁ
adb shell input tap 80 1628 # ㄱ
adb shell input tap 145 1451 # ㅣ
adb shell input tap 474 1983 # ㅁ
# 치 = ㅊ ㅣ
adb shell input tap 736 1805 # ㅊ
adb shell input tap 145 1451 # ㅣ
실제로는 파이썬 같은 걸로 자모를 분해해 좌표 목록으로 바꿔 두고 한 번에 보냅니다. 한 글자씩 adb를 새로 띄우면 느리니 adb shell 한 세션에서 이어 보내는 편이 낫습니다.
input text가 막힌 이유는 문자→키 변환표에 한글이 없어서입니다. 탭은 그 변환을 거치지 않고 화면 좌표만 보냅니다. 조합은 IME가 알아서 합니다 — 우리는 사람이 누르는 것과 똑같은 신호를 보낼 뿐입니다.
확인
입력이 실제로 들어갔는지는 화면으로 봅니다. 자동화 스크립트라면 스크린샷을 찍어 두거나, 검색 결과 목록이 떴는지를 uiautomator dump로 확인하면 됩니다. 입력칸 자체는 앱 윈도우라 덤프에 나옵니다 — 안 나오는 건 키보드뿐입니다.
adb shell uiautomator dump /sdcard/ui.xml
adb pull /sdcard/ui.xml
이 한 가지가 판단을 바꿨다
이 글을 쓰는 진짜 이유입니다. 저는 "한글 입력 자동화 불가"라는 결론을 근거로 앱 하나의 검증 계획을 통째로 축소했습니다. 검색이 핵심 기능인 앱이었는데 그 경로를 자동 점검에서 뺀 것입니다.
그런데 막혔던 건 input text라는 도구 하나였지 "한글 입력"이라는 목적이 아니었습니다. 한 층 아래로 내려가니 길이 있었습니다. 도구가 막히면 그 도구가 무엇을 대신해 주고 있었는지를 보고, 그걸 직접 하는 방법을 찾는 편이 빠릅니다.
덧붙이면 한글 입력을 자동화할 수 있게 되면서 한글로만 재현되는 결함도 볼 수 있게 됐습니다. 예를 들어 Compose의 TextField는 값을 비동기 저장소에 왕복시키면 한글 자모가 흩어지는데, 영어로 테스트하면 100% 통과합니다. 이런 건 한글을 실제로 쳐 봐야 걸립니다.
정리
input text는 문자를 키 이벤트로 바꿔 보냅니다. 그 변환표에 한글이 없어 NullPointerException이 납니다.- 문자열을 치환하는 우회로는 해결되지 않습니다. 지원 범위 밖입니다.
input tap으로 자판 좌표를 직접 누르면 됩니다. 조합은 IME가 합니다.- 키보드는 별도 윈도우라 uiautomator dump에 안 잡힙니다. 좌표는 스크린샷으로 읽습니다.
- 먼저 입력칸을 탭해 키보드를 올려야 합니다.
- 좌표는 기기·자판마다 다릅니다. 숫자가 아니라 방법을 가져가세요.
자주 묻는 것
ADB 키보드 같은 걸 설치하는 방법도 있지 않나요?
테스트용 IME를 설치해 기본 키보드로 바꾸고, 브로드캐스트로 문자열을 넣는 방식이 있습니다. 문자열을 통째로 넣을 수 있어 편하지만 기본 키보드를 바꿔야 하고, 끝나고 되돌리지 않으면 사용자의 폰 설정이 바뀐 채로 남습니다. 저는 남의 기기 설정을 건드리지 않는 쪽을 택했습니다. 조합 과정 자체를 확인하고 싶을 때도 실제 자판을 누르는 편이 실제에 가깝습니다.
쿼티 자판에서도 되나요?
됩니다. 좌표만 다시 재면 됩니다. 다만 쿼티는 키가 촘촘해서 좌표 오차에 민감하고, 자모를 키 단위로 분해해야 하는 것은 같습니다. 천지인은 키가 크고 수가 적어 좌표를 재기는 더 쉬웠습니다.
에뮬레이터에서는요?
같은 방법이 통합니다. 다만 에뮬레이터 기본 로케일은 영어라 한글 키보드가 아예 안 올라올 수 있습니다. setprop persist.sys.locale ko-KR 후 재시작하거나, 설정에서 한국어 키보드를 추가해 두고 좌표를 재세요.
