본문 바로가기

업무 자동화 · 도구 검증 · 서비스 운영

반복 업무를 줄이는 방법,
직접 시험하고 기록합니다.

예약 명단 정리부터 알림 자동화, 앱 운영까지.
원본 예제와 확인표, 실험 결과를 함께 나눕니다.

첫 번째 실험 · 예약 명단

이름이 같으면
같은 예약일까요?

B102 · 방문자B9월 10일
B103 · 방문자B9월 11일

다른 예약입니다. 둘 다 남겨야 합니다.

가상 명단 12행으로 확인한 결과 →
개발 문제 해결

adb 명령어 모음: 기기 연결 안 될 때부터 apk 설치와 로그 추출까지

by 코딩히어로 2026. 9. 11.
300x250
반응형

안드로이드 스튜디오 실행 목록에 폰이 안 보이면 저는 IDE를 먼저 꺼다 켜곤 했습니다. 지금은 터미널에서 adb devices부터 칩니다. 이 한 줄이 케이블 문제인지, 승인 문제인지, 서버 문제인지를 갈라 주기 때문입니다. 이 글은 연결이 안 될 때 확인하는 순서에서 시작해 apk 설치, 로그 추출, 화면 캐처, 무선 디버깅까지 실무에서 실제로 치는 명령을 순서대로 정리한 것입니다. 아래 출력 중 기기가 없어도 나오는 것은 이 맥에서 직접 실행해 붙였고, 기기가 있어야 나오는 출력은 공식 문서와 adb --help 기준으로 설명했습니다.

adb devices 출력을 먼저 읽습니다

진단은 버전 확인과 목록 확인 두 줄이면 시작됩니다. 제 맥에 설치된 platform-tools에서 실행한 결과입니다.

adb version
adb devices -l
Android Debug Bridge version 1.0.41
Version 37.0.0-14910828
Installed as /opt/homebrew/bin/adb
Running on Darwin 25.6.0 (arm64)

List of devices attached

지금은 기기를 꺽지 않았으므로 목록이 비어 있습니다. 여기서 갈립니다. 목록 자체가 비면 USB 단계에서 끊긴 것이고, 줄은 나오는데 상태값이 이상하면 그 다음 단계에서 막힌 것입니다. -l을 붙이면 제품명과 모델, 연결된 USB 경로까지 함께 나와서 기기가 여러 대일 때 구분하기 쉽습니다.

상태값은 네 가지만 기억하면 됩니다. device는 정상입니다. unauthorized는 기기가 이 컴퓨터를 아직 신뢰하지 않은 상태입니다. 공식 문서는 Android 4.2.2 이상에서 RSA 키 수락 대화상자를 승인해야 adb 명령이 실행된다고 설명합니다. 폰 화면을 켜서 잠금을 풀고 대화상자를 확인하면 대부분 풀립니다. offline은 연결은 됐지만 통신이 되지 않는 상태이고, no permissions는 리눅스에서 udev 규칙이 없을 때 자주 나옵니다.

기기가 없을 때 다른 명령을 치면 에러 문구가 상태를 그대로 말해 줍니다.

$ adb get-state
error: no devices/emulators found

$ adb shell getprop ro.build.version.release
adb: no devices/emulators found

안 잡힐 때 되짚는 순서

저는 위에서 아래로 훑습니다. 순서를 지키는 이유는 앞 단계를 건너뛰면 뒤 단계에서 원인을 잘못 짚기 때문입니다.

첫째, 폰의 개발자 옵션과 USB 디버깅이 켜져 있는지 뵅니다. 빌드 번호를 여러 번 눌러 개발자 옵션을 연 뒤 USB 디버깅을 켜니다. 둘째, USB 연결 모드를 확인합니다. 충전 전용으로 붙으면 목록에 아예 안 뜰니다. 파일 전송 모드로 바꿔 보면 상태가 달라지는 경우가 많습니다. 셋째, 케이블과 포트를 바꿔 봅니다. 데이터 선이 없는 충전 전용 케이블이 섞여 있으면 아무리 설정을 만져도 잡히지 않습니다. 넷째, 윈도우라면 제조사 USB 드라이버를 다시 설치합니다.

여기까지 해도 그대로면 서버를 다시 올립니다. adb는 데몬을 5037 포트에 띄워 두고 그 데몬을 통해 기기와 통신합니다.

adb kill-server
adb start-server
adb server-status

실행하면 데몬이 다시 뜨는 로그가 나옵니다.

* daemon not running; starting now at tcp:5037
* daemon started successfully

adb server-status는 지금 서버가 어떤 설정으로 떠 있는지 알려 줍니다. 실제 출력에서 경로 항목만 줄인 결과입니다.

usb_backend: LIBUSB
mdns_backend: LIBADBMDNS
version: "37.0.0"
build: "14910828"
mdns_enabled: true
burst_mode: false

version이 예전 버전이면 platform-tools를 올리는 것이 빠릅니다. 신형 기기일수록 구버전 adb에서 잡히지 않는 일이 있습니다. unauthorized가 계속 반복되면 폰의 개발자 옵션에서 USB 디버깅 승인을 취소한 뒤 다시 연결하거나, PC의 ~/.android/adbkey 쌍을 지우고 서버를 재시작해 키를 새로 만들게 합니다. 서버 재시작 후에는 폰 화면에서 승인 대화상자를 다시 받아야 합니다.

apk 설치와 삭제, 패키지명 찾기

설치 옵션은 외우기보다 adb --help를 한 번 보는 편이 정확합니다. 아래는 이 맥에서 실행한 도움말 중 설치 부분을 그대로 가져온 것입니다.

install [-lrtsdg] [--instant] PACKAGE
    -r: replace existing application
    -t: allow test packages
    -d: allow version code downgrade (debuggable packages only)
    -g: grant all runtime permissions
uninstall [-k] PACKAGE
    '-k': keep the data and cache directories

실무에서 자주 쓰는 조합은 세 가지입니다. 데이터를 유지한 채 덮어쓸 때는 -r, QA용으로 버전을 낮춰 깔 때는 -d, 권한 팝업을 매번 누르기 싫을 때는 -g입니다.

adb install -r app-debug.apk
adb install -r -d -g app-debug.apk
adb uninstall -k com.example.app
adb shell pm list packages | grep example
adb shell pm path com.example.app

패키지명이 기억나지 않을 때는 pm list packages로 훑고, 설치된 apk 경로가 필요하면 pm path로 확인한 뒤 adb pull로 받아옵니다. 기기 없이 설치를 시도하면 apk 경로를 보기도 전에 아래 줄이 먼저 나옵니다. 파일명을 의심하기 전에 연결부터 확인하라는 신호입니다.

adb: no devices/emulators found

설치가 실패하면 PackageManager가 INSTALL_FAILED_로 시작하는 이유를 돌려줍니다. 매뉴얼 기준으로 서명이 다르면 UPDATE_INCOMPATIBLE, 버전 코드가 낮으면 VERSION_DOWNGRADE입니다. 앞은 기존 앱을 지우고 다시 깔아야 하고, 뒤는 디버그 빌드에 한해 -d로 넘어갑니다. 설치는 되는데 특정 기기에서만 실행 직후 죽는다면 apk 안의 네이티브 라이브러리를 의심할 차례입니다. 저는 그 점검 순서를 Android 16KB 페이지 크기 오류 글에 따로 정리해 두었습니다.

로그와 상태를 좁혀서 봅니다

logcat에는 다른 프로세스의 로그도 함께 나옵니다. 이미 발생한 오류를 조사할 때는 먼저 남아 있는 로그를 저장합니다. adb logcat -c는 선택된 로그 버퍼를 지우고 종료하므로, 처음부터 실행하면 필요한 오류 기록도 사라질 수 있습니다.

adb logcat -d -v time > logcat-before-clear.txt

위 명령은 현재 읽을 수 있는 로그를 파일로 저장하고 끝납니다. 모든 과거 로그를 보존하는 백업은 아니므로, 필요한 오류가 파일에 담겼는지 먼저 확인하세요. >는 같은 이름의 파일을 덮어쓰므로 기존 파일을 남기려면 새 이름을 씁니다. 새 재현을 시작하면서 이전 기록이 더 이상 필요 없을 때만 adb logcat -c로 비운 뒤 다시 수집합니다.

아래는 목적에 맞게 한 줄씩 선택하는 예입니다. 계속 출력되는 명령은 Ctrl+C로 멈춘 다음 다른 명령을 실행합니다.

adb logcat -v time
adb logcat --pid=$(adb shell pidof -s com.example.app)
adb logcat "*:E"
adb logcat -d > logcat.txt

"*:E"는 별표가 셸에서 파일명 패턴으로 해석되지 않도록 따옴표로 감싼 것입니다. 모든 태그에서 Error 이상을 표시합니다. --pid는 지정한 숫자 PID를 기준으로 거르므로 앱 프로세스가 다시 시작되면 PID를 다시 확인해 명령을 실행합니다. pidof 결과가 비어 있으면 해당 프로세스가 실행 중인지부터 확인하세요.

이 보충 설명은 Android Logcat 공식 문서와 AOSP의 logcat 옵션 정의를 바탕으로 확인했습니다(2026-10-02). 이번 수정에서 실제 기기로 실행한 결과는 아닙니다. 지원 옵션은 기기의 Android 버전에 따라 다를 수 있습니다.

-v time은 각 줄 앞에 시각을 붙여 재현 시점과 맞춰 보기 좋습니다. --pid에 pidof 결과를 넣으면 그 앱 프로세스의 로그만 남습니다. *:E는 에러 이상만 거릅니다. -d는 지금까지 쌓인 로그를 한 번 내리고 끝내므로 파일로 받아 첨부하기 좋습니다. 실제로 통신 오류 하나를 잡을 때 저는 이 방식으로 Cleartext HTTP traffic ... not permitted 줄을 찾아냈고, 그 원인과 예외 설정은 Cleartext HTTP traffic 오류 글에 정리했습니다.

상태 확인은 dumpsys와 getprop입니다.

adb shell dumpsys activity activities
adb shell dumpsys meminfo com.example.app
adb shell getprop ro.build.version.release
adb bugreport ./bugreport

dumpsys activity activities는 지금 어떤 액티비티가 최상단인지 알려 줍니다. 화면 전환이 의도대로 안 될 때 먼저 봅니다. dumpsys meminfo는 앱 프로세스의 메모리 구성을 보여줍니다. 다만 이건 기기 위 앱의 수치이고, 편집이나 빌드 자체가 느린 문제와는 다릅니다. IDE 쪽이 느린 상황이라면 Android Studio 메모리 설정 글이 더 맞습니다. adb bugreport는 로그와 시스템 상태를 zip으로 묶어 줍니다. 재현이 어려운 이슈를 넘길 때 이것 하나면 대체로 충분합니다.

작은 함정이 하나 있습니다. 기기 없이 adb logcat --help를 치면 도움말이 뜨지 않고 기기가 붙을 때까지 기다립니다. 이 글을 쓰다가 실제로 그 상태로 멈춰서 명령을 끊어야 했습니다. 도움말은 기기를 연결한 뒤에 보는 편이 낫습니다.

파일 전송, 화면 캐처, 무선 디버깅, 재현

파일은 push와 pull 두 개면 됩니다. 화면은 캐처와 녹화가 따로 있습니다.

adb push ./sample.json /sdcard/Download/sample.json
adb pull /sdcard/Download/report.txt ./
adb exec-out screencap -p > screen.png
adb shell screenrecord --time-limit 30 /sdcard/demo.mp4
adb pull /sdcard/demo.mp4 ./

exec-out screencap -p는 기기에 파일을 남기지 않고 바로 PC로 받습니다. 저는 이 방식이 캐처 후 pull보다 손이 덜 갑니다. 녹화는 공식 문서 기준으로 기본 3분 제한이 있고 오디오는 담기지 않습니다. 상태바나 하단 버튼이 콘텐츠와 겹치는 문제처럼 눈으로 봐야 하는 이슈는 캐처를 남겨 두면 비교가 쉽습니다. 그 겹침을 확인하는 순서는 targetSdk 36 WindowInsets 글에 적어 두었습니다.

무선 디버깅은 Android 11 이상에서 페어링 코드 방식을 씁니다. 기기의 개발자 옵션에서 무선 디버깅을 켜고 페어링 코드 화면에 뜨 IP와 포트를 그대로 넣습니다. 페어링 포트와 연결 포트는 다르니 각각 확인해야 합니다.

adb pair 192.168.0.10:41234
adb connect 192.168.0.10:37000
adb devices

무선이 잘 안 붙으면 mDNS 상태를 봅니다. 아래는 기기 없이 실행한 결과라 목록이 비어 있습니다.

$ adb mdns check
mdns daemon version [adb discovery 0.0.0]

$ adb mdns services
List of discovered mdns services

앞의 server-status에서 mdns_enabled: true였는지도 같이 확인합니다. 이 값이 false면 페어링 화면이 떠도 워크스테이션이 기기를 찾지 못합니다. PC와 폰이 같은 무선 네트워크에 있어야 한다는 조건도 자주 놓칩니다.

재현에 쓰는 명령도 있습니다. 화면을 직접 열거나 입력을 흉내 내는 용도입니다.

adb shell am start -n com.example.app/.MainActivity
adb shell am start -a android.intent.action.VIEW -d "myapp://item/12"
adb shell input tap 540 1200
adb shell input text "test.user.01"
adb shell settings put global window_animation_scale 0

딥링크 검증은 am start가 가장 빠르고, 반복 입력이 필요한 회귀 확인에는 input이 편합니다. 애니메이션 배율을 0으로 두면 UI 테스트가 안정적입니다. 다만 settings put은 기기 전역 설정을 바꾸므로 개인 상용 기기가 아니라 개발용 기기에서만 쓰는 편이 안전합니다.

명령 역할 자주 쓰는 옵션
adb devices 연결 상태와 상태값 확인 -l
adb install apk 설치 -r, -d, -g, -t
adb uninstall 앱 제거 -k
adb logcat 기기 로그 확인 -c, -d, -v time, --pid
adb push / pull 파일 주고받기 -a (pull)
adb shell dumpsys 시스템 서비스 상태 activity, meminfo
adb pair / connect 무선 디버깅 연결 IP:PORT
adb -s 기기 여러 대 중 지정 -d, -e

옵션 전체와 각 명령의 정확한 정의는 Android 공식 adb 문서가 기준입니다.

자주 묻는 질문

adb 명령어가 command not found로 나옵니다

adb 자체가 없는 것이 아니라 PATH에 없는 경우가 대부분입니다. Android Studio를 설치했다면 SDK 폴더 아래 platform-tools에 들어 있습니다. 그 경로를 셸 설정 파일의 PATH에 추가하고 터미널을 새로 열면 됩니다. 저는 맥에서 별도로 받은 platform-tools를 쓰고 있고, which -a adb로 /opt/homebrew/bin/adb 하나만 잡히는 것을 확인했습니다. 경로가 여러 개 잡히면 버전이 섞여 이상한 동작이 나올 수 있으니 하나로 정리하는 편이 좋습니다.

adb devices에는 나오는데 Android Studio 실행 목록에는 안 보입니다

IDE가 자체 adb 서버를 따로 쥐고 있어서 생기는 충돌일 때가 많습니다. 터미널에서 adb kill-server를 하면 IDE 쪽 연결도 함께 끊기므로, 서버를 정리한 뒤 Android Studio의 기기 목록을 새로 고치면 다시 잡힙니다. 그래도 안 되면 IDE가 참조하는 SDK 경로의 platform-tools 버전과 터미널에서 쓰는 adb 버전이 같은지 adb version으로 비교해 봅니다.

기기가 여러 대일 때 하나만 지정하려면 어떻게 하나요

adb devices -l로 시리얼을 확인한 뒤 adb -s 시리얼 install app.apk처럼 앞에 붙이면 됩니다. USB 기기 한 대만 대상으로 할 때는 -d, 에뮬레이터만 대상으로 할 때는 -e가 더 짧습니다. 같은 기기로 여러 명령을 연달아 칠 때는 export ANDROID_SERIAL=시리얼을 한 번 해 두면 매번 붙이지 않아도 됩니다.

300x250
반응형

댓글