adb shell pm uninstall --user 0 같은 안전한 명령이 실행된다. 옛 UAD(파이썬·셸 기반)의 '차세대 분리 포크'로, GUI(iced)·CLI(clap)·코어 라이브러리로 깔끔히 모듈화돼 있다. (저장소: Universal-Debloater-Alliance/universal-android-debloater-next-generation · Rust(edition 2024) · iced 0.14 GUI · ★7.6k · 라이선스 GPL-3.0)한 문장으로 이 프로젝트가 뭘 하는지부터.
UAD-NG = "루팅 없이, 검수된 목록을 보면서, 폰의 기본탑재 잡앱을 안전 등급별로 골라 비활성화·삭제하는 Rust 데스크톱 앱." 새 안드로이드 폰을 켜면 제조사(삼성·샤오미 등)·통신사·구글이 미리 깔아 둔 앱이 수백 개 들어 있다. 이 중 상당수는 백그라운드에서 돌며 배터리·메모리를 갉아먹고, 추적·광고로 프라이버시를 침해한다. UAD-NG는 PC에 폰을 연결하고 GUI에서 체크 → 버튼 한 번으로 이런 앱을 정리한다. 루팅이 필요 없다는 게 결정적이다(루팅은 보증 무효·뱅킹앱 차단·보안 위험을 부른다).
폰을 루팅하는 건 엔진룸을 통째로 뜯어 보증을 날려 먹는 위험한 개조다. UAD-NG는 그렇게 하지 않는다. 안드로이드가 이미 공식적으로 허락한 통로(ADB의 패키지 매니저)만 쓴다.
그리고 혼자 판단하지 않는다. 전 세계 사용자가 수년간 "이 앱은 지워도 폰이 멀쩡한가?"를 검증해 만든 체크리스트를 들고, 항목마다 "안전/주의/전문가/위험" 딱지를 보여 주며 정리한다. 아무거나 막 지우는 청소기가 아니라, 라벨을 읽고 분류하는 정리 전문가다.
adb 실행파일을 뒤에서 호출하는 'GUI 껍데기'에 가깝다.구조를 한 줄로 그리면 이렇다 — [사용자가 GUI에서 체크] → [UAD-NG가 안전한 ADB 명령 문자열을 조립] → [PC의 adb가 USB로 폰에 전달] → [폰의 패키지 매니저가 실행]. 즉 UAD-NG 자체는 폰을 직접 만지지 않고, "어떤 명령을, 어떤 안전등급으로, 어떤 사용자 프로필에" 보낼지를 안전하게 결정·조립하는 두뇌 역할이다.
TrendShift 일간 10위 — ★7.6k를 모은 이유.
이 저장소는 2023년 10월 시작된 비교적 신생 프로젝트인데도 별 7,600개를 넘겼다. 핵심 동력은 "내 폰을 내가 통제하고 싶다"는 보편적 욕구와, 그걸 루팅이라는 위험 없이, 검증된 데이터로 풀어 줬다는 점이다. 이유를 다섯으로 정리한다.
경쟁 도구나 "커스텀 롬"은 흔히 루팅·부트로더 언락을 요구한다. 그러면 뱅킹앱이 막히고 보증이 날아간다. UAD-NG는 OEM이 공식 허용한 ADB 통로만 써서 일반 사용자도 안전하게 접근한다. 게다가 대부분의 동작이 '완전 삭제'가 아니라 현재 사용자(user 0)에서만 제거라, 공장초기화하면 되살아난다(되돌릴 길이 있다).
이 프로젝트의 심장은 코드가 아니라 uad_lists.json이다. 실제로 세어 보면 5,361개 패키지가 등록돼 있고, 각 항목에 "왜 있는 앱인지, 지우면 무슨 일이 나는지, 어느 등급인지"가 사람 손으로 적혀 있다. 등급 분포는 Recommended 3,004 · Advanced 1,143 · Expert 924 · Unsafe 296. 출처별로는 OEM(제조사) 4,236, Misc 433, AOSP 272, Carrier(통신사) 243, Google 183. 이 데이터는 Canta·AppManager 같은 다른 프로젝트도 가져다 쓴다.
원조 UAD(0x192/universal-android-debloater)는 사실상 유지보수가 멈췄다. 그래서 커뮤니티가 'Universal-Debloater-Alliance'라는 조직을 만들어 완전 분리 포크(detached fork)로 이어받았다. README가 직접 "this is a detached fork"라고 밝힌다. 멈춘 도구를 살아 있는 커뮤니티가 인수했다는 서사 자체가 신뢰를 준다.
원조는 파이썬/셸 기반이었지만, 차세대는 Rust로 재작성됐다. 단일 바이너리로 배포돼 설치가 쉽고(파이썬 런타임 불필요), 코어 로직(uad-core)·GUI(uad-gui)·CLI(uad-cli)를 워크스페이스로 3분할해 같은 안전 로직을 GUI와 명령줄에서 공유한다. 메모리 안전성 덕에 "잘못된 명령으로 폰을 망가뜨릴" 부류의 버그가 구조적으로 줄어든다.
README가 못 박는다: UAD-NG는 사용자 데이터를 일절 수집·전송하지 않는다. 외부로 나가는 연결은 단 둘 — GitHub에서 최신 패키지 목록을 받는 GET 요청과 업데이트 확인뿐이다. 프라이버시 도구가 정작 사용자를 추적하면 자가당착인데, 이 도구는 그 함정을 코드로 피한다.
| 접근법 | 루팅 필요? | 특징 / 한계 |
|---|---|---|
| UAD-NG | 아니오 | ADB만 사용. 검수된 목록·안전등급·되돌리기. PC 연결 필요. |
| 커스텀 롬(LineageOS 등) | 보통 예(부트로더 언락) | 가장 깨끗하지만 난도·위험 최상. 보증 무효·뱅킹앱 차단. |
| Canta / App Manager | 아니오(Shizuku) | 폰에서 직접. PC 불필요하나 별도 권한 설정(Shizuku) 필요. UAD-NG 목록을 차용. |
수동 adb 타이핑 | 아니오 | 완전 무료지만 패키지명을 직접 알아내야 하고, 안전등급 정보가 없어 위험. |
UAD-NG는 폰을 뚫거나 시스템 파티션을 영구 삭제하지 않는다. 대부분의 '삭제'는 pm uninstall --user 0 — 현재 사용자 프로필에서만 앱을 떼어내는 동작이라, 시스템 이미지(/system) 자체는 그대로다. 그래서 공장초기화하면 앱이 되돌아온다. 진짜 위험은 도구가 아니라 "무엇을 지우느냐"에 있다(아래 4·8번 섹션의 danger 박스 참고).
Cargo.toml 3개를 직접 뜯어본 실제 의존성 지도.
이 저장소는 Cargo 워크스페이스다. 루트 Cargo.toml의 [workspace] members에 세 크레이트가 묶여 있다 — crates/uad-core(공유 로직), crates/uad-gui(데스크톱 GUI, 바이너리명 uad-ng), crates/uad-cli(명령줄, 바이너리명 uad). Rust edition 2024를 쓰고, 전체 소스는 약 8,365줄이다.
[workspace.dependencies]에 한 번만 적고 각 크레이트가 .workspace = true로 물려받는다. "공유 라이브러리 1개 + 그걸 쓰는 앱 여러 개" 구조에 딱 맞다.GUI와 CLI가 공유하는 핵심 로직이 전부 여기 있다. 의존성이 의도적으로 가볍다.
| 크레이트 | 역할 |
|---|---|
| serde / serde_json | 패키지 목록 JSON ↔ Rust 구조체 변환(직렬화). uad_lists.json 파싱의 핵심. |
| ureq | 가벼운 HTTP 클라이언트. GitHub에서 최신 목록을 GET으로 받는다(비동기 런타임 불필요). |
| retry | 네트워크 실패 시 재시도(1초 간격 최대 60회). 목록 다운로드를 끈질기게 한다. |
| csv | 선택한 패키지 목록을 CSV로 내보내기(export). |
| toml | 설정 파일(config) 읽기/쓰기. 사용자 테마·전문가 모드 등을 저장. |
| dirs | OS별 설정·캐시 폴더 경로를 안전하게 찾기(윈도/맥/리눅스 제각각). |
| chrono | 날짜·시간(로그 타임스탬프, 백업 파일명). |
| log | 로깅 추상화(실제 출력은 GUI의 fern이 담당). |
| flate2 + tar | 선택 기능(self-update). 새 릴리스를 받아 압축 해제·교체. |
| iced =0.14.0 | 순수 Rust GUI 프레임워크. Elm 아키텍처(Model-View-Update) 기반. 버전을 =로 고정(API가 자주 바뀌어서). advanced 기능 활성화. |
| wgpu (기본 feature) | iced의 GPU 렌더링 백엔드. 그래서 main.rs가 듀얼 GPU 충돌을 막으려 WGPU_POWER_PREF=high를 강제한다. |
| rfd | 네이티브 파일 대화상자(Rust File Dialog). 백업 폴더 선택 등에 사용. |
| dark-light | OS의 다크/라이트 모드 감지 → 테마 자동 전환. |
| fern | 실제 로그 파일(uadng.log) 기록 + 컬러 콘솔 출력. |
| image | 로고·아이콘 이미지 로딩(img feature). |
| embed-resource | 빌드 의존성. 윈도 .exe에 아이콘·매니페스트를 박아 넣음. |
| clap 4.5 | 명령줄 인자 파서. derive 기능으로 구조체에 어트리뷰트만 달면 서브커맨드·플래그가 자동 생성. |
| clap_complete | bash/zsh/fish 등 셸 자동완성 스크립트 생성(completions 커맨드). |
| tokio (full) | 비동기 런타임. CLI의 main이 #[tokio::main]으로 돈다. |
| rustyline | 대화형 REPL 모드의 입력 편집(화살표·히스토리). uad repl. |
.github/workflows에 3개 파이프라인이 있다. ci.yml은 매 PR마다 check · test · clippy · fmt 4종 린트를 돌린다(clippy::all을 에러로 취급). build_artifacts.yml은 linux · macos · macos-intel · windows 4개 타깃으로 바이너리를 빌드한다(self-update 버전 / 미포함 버전 둘 다). release.yml은 태그가 붙으면 아티팩트를 모아 릴리스를 만든다.
코드 품질에 유난히 깐깐하다. 루트 Cargo.toml의 [workspace.lints.clippy]를 보면 undocumented_unsafe_blocks = "forbid"(설명 없는 unsafe 블록 금지), panic_in_result_fn = "warn", pedantic 경고 활성화, disallowed_methods = "deny" 등 안전성·일관성 규칙이 빼곡하다. 폰을 망가뜨릴 수 있는 도구이니만큼, "위험한 코드를 컴파일 단계에서 막는다"는 철학이 설정에 그대로 박혀 있다.
데이터가 GUI에서 폰까지 흐르는 길, 그리고 안전을 보장하는 핵심 패턴.
핵심은 UAD-NG가 폰을 직접 건드리지 않는다는 점이다. 폰 쪽에서 실제로 일하는 건 안드로이드 내장 pm(패키지 매니저)·am(액티비티 매니저)이고, UAD-NG는 그저 "어떤 pm 명령을, 어떤 안전등급으로, 어떤 user에" 보낼지 결정해 adb에게 넘길 뿐이다.
adb.rs 맨 위 주석이 설계 철학을 강하게 못 박는다: ADB 명령은 "얇은 래퍼(thin wrapper)"여야 하고, "마법 없음 / 커스텀 명령 없음 / 파이프 체이닝 없음"을 지킨다. 즉 메서드 1개 = ADB 명령 1개로 1:1 대응시켜, "여러 명령을 섞다가 폰이 중간 상태에 빠지는" 사고를 원천 차단한다.
이를 위해 타입스테이트 패턴(type-state)을 쓴다. ACommand(adb) → .shell() → ShellCommand → .pm() → PmCommand처럼, 각 단계가 다른 타입을 반환한다. 그래서 "adb 없이 pm을 호출"하는 식의 잘못된 조합은 컴파일조차 되지 않는다. 잘못된 명령을 런타임이 아니라 빌드 타임에 막는 것이다.
// adb.rs — 타입이 단계별로 바뀌며 잘못된 조합을 컴파일에서 차단
ACommand::new() // "adb"
.shell(serial) // "adb -s <serial> shell" → ShellCommand 반환
.pm() // "... pm" → PmCommand 반환
.list_packages_sys(..); // "... list packages -s"
// 그리고 패키지명은 '검증된 타입'으로만 다룬다:
pub struct PackageId(Box<str>); // 유효한 패키지명이라는 불변식을 보장
PackageId::new("com.foo".into()); // 형식이 틀리면 None → 애초에 만들 수 없음
UAD-NG의 안전성은 sync.rs의 apply_pkg_state_commands() 함수 하나에 압축돼 있다. "지금 상태"와 "원하는 상태", 그리고 안드로이드 SDK 버전에 따라 어떤 ADB 명령을 쏠지 정확히 분기한다. 주석엔 굵게 적혀 있다: "상태를 바꾸는 명령을 항상 맨 앞에 둘 것."
| 원하는 상태 | SDK 23+ (안드 6.0~)에서 실행되는 명령 | 의미 |
|---|---|---|
| Uninstalled (삭제) | pm uninstall --user 0 | 현재 사용자에서만 제거. 시스템 이미지는 그대로 → 공장초기화로 복구 가능. |
| Disabled (비활성화) | pm disable-user → am force-stop → pm clear | 앱을 끄고(force-stop) 데이터를 비우되, 설치는 유지. 더 보수적·안전. |
| Enabled (복원) | cmd package install-existing 또는 pm enable | 지운 앱을 되살리거나, 꺼둔 앱을 다시 켬. |
uninstall --user 0 = "내 책상에서 치우기" — 창고(시스템)엔 원본이 남아 있어서, 책상을 갈아엎으면(공장초기화) 다시 올라온다.
disable = "콘센트 뽑고 서랍에 넣기" — 물건은 그대로 있지만 전원이 꺼져 아무 일도 안 한다. 더 가볍고 되돌리기 쉽다. 그래서 설정에 disable_mode 옵션이 있어, 삭제 대신 비활성화를 기본으로 쓸 수 있다.
더 인상적인 건 옛 안드로이드까지 챙긴다는 점이다. SDK 21~22(롤리팝)는 pm hide, 그 이하는 pm block으로 폴백한다. 그리고 make_friendly_error_message()는 삼성 Knox의 DELETE_FAILED_USER_RESTRICTED 같은 암호 같은 에러를 사람이 읽을 수 있는 안내로 번역한다("이 패키지는 제조사가 제한했습니다. 삭제 대신 비활성화를 시도해 보세요").
안드로이드는 한 폰에 여러 사용자 프로필(직장 프로필 등)을 둘 수 있다. sync.rs는 list_users_idx_prot()로 사용자 목록을 읽고, is_protected_user()로 건드리면 안 되는 보호 사용자를 가려낸다. 또 capture_cross_user_states() / detect_cross_user_behavior()로 "user 0에서 지웠는데 다른 프로필에서도 사라졌다" 같은 의도치 않은 교차 영향을 감지해 사용자에게 알린다. 폰마다 제조사가 ADB 동작을 미묘하게 바꿔 놓는 현실을 방어적으로 다루는 코드다.
GUI는 UadGui 구조체(=상태)와 Message 열거형(=일어날 수 있는 사건)으로 돌아간다. update(&mut self, msg)가 메시지를 받아 상태를 바꾸고, view()가 상태를 화면으로 그린다. ADB처럼 오래 걸리는 작업은 Task::perform(async {...}, Message::···)로 비동기로 띄워 UI가 멈추지 않게 한다. nb_running_async_adb_commands 카운터로 "지금 몇 개의 ADB 작업이 도는 중인지"를 추적한다.
어디에 뭐가 있는지 — 실제 트리를 따라가며.
핵심만 짚으면 — 코드를 읽고 싶다면 crates/uad-core/src/sync.rs와 adb.rs 둘이면 충분하다. 여기에 "안전한 디블로팅"의 모든 판단이 들어 있다. 데이터가 궁금하면 resources/assets/uad_lists.json을 열어 보라 — 패키지 하나가 어떻게 기술되는지(설명·의존성·등급) 한눈에 보인다. GUI/CLI는 이 두뇌를 부르는 얇은 껍데기다.
// uad_lists.json 한 항목의 실제 모양 (AOSP 공유 라이브러리 = 지우면 부트루프!)
"android.ext.shared": {
"list": "Aosp",
"description": "Android Shared Library, removing may cause a bootloop.",
"dependencies": [],
"neededBy": [],
"labels": [],
"removal": "Unsafe" // ← 빨간 'Unsafe' 등급으로 사용자에게 경고
}
이 저장소를 읽으면 구체적으로 무엇을 배우는가.
이 프로젝트의 가장 실용적인 교재 가치다. std::process::Command로 외부 CLI(adb)를 호출하고, stdout/stderr·종료코드를 받아 Result<String, String>로 다루는 패턴이 adb.rs에 모범적으로 들어 있다. 윈도에서 creation_flags(0x0800_0000)로 cmd 창이 깜빡 뜨는 것을 막는 실전 팁, String::from_utf8_lossy로 일부 OEM이 뱉는 비UTF-8 출력에도 패닉하지 않게 막는 방어 코드까지 배울 수 있다.
패키지명을 그냥 String으로 두면 빈 문자열·이상한 값이 명령에 섞여 사고가 난다. UAD-NG는 PackageId·PmListPacksFlag 같은 전용 타입을 만들어, 유효하지 않으면 애초에 값이 만들어지지 않게 한다. "잘못된 상태를 표현 불가능하게 만든다(make illegal states unrepresentable)"는 Rust 격언의 살아 있는 예시다.
웹·일렉트론 없이 네이티브 GUI를 Rust만으로 짜는 법을 gui.rs에서 배운다. 상태 구조체 + Message enum + update/view의 MVU 흐름, 그리고 무거운 작업을 Task::perform으로 비동기 처리해 UI를 멈추지 않게 하는 패턴이 깔끔하다. views/(화면)와 widgets/(부품)로 나눈 폴더 구조는 GUI를 모듈화하는 표준 감각을 길러 준다.
uad-cli/src/main.rs는 clap derive 매크로의 교과서다. enum Commands에 devices · list(ls) · uninstall(rm) · enable(restore) · disable · info · update · completions · repl 서브커맨드를 어트리뷰트만으로 정의하고, visible_alias로 별칭을, --dry-run으로 "실제로 실행하지 않고 무슨 일이 일어날지만 보여 주기"를 제공한다. 위험한 도구일수록 dry-run이 왜 중요한지 체감할 수 있다.
같은 안전 로직(uad-core)을 GUI와 CLI가 함께 쓰는 구조는 실무에서 매우 흔한 요구다. "비즈니스 로직은 라이브러리로 분리하고, UI는 얇게"라는 원칙을 Cargo 워크스페이스로 실현한 좋은 사례다. 의존성을 [workspace.dependencies]에 한 번만 적는 관리 방식도 함께 익힌다.
코드를 따라가다 보면 자연스럽게 pm list packages -s(시스템 앱 나열), pm uninstall --user 0, pm disable-user, pm list users 같은 실전 ADB 명령과, 안드로이드의 멀티유저·SDK 버전별 동작 차이를 배운다. 안드로이드 개발자·파워유저에게 직접적으로 쓸모 있는 지식이다.
"내 폰의 시스템 앱 세어 보기" — UAD-NG 없이 ADB만으로 adb shell pm list packages -s | wc -l를 쳐 보라. 수백 개가 나온다. 그다음 UAD-NG의 uad_lists.json에서 그중 몇 개가 'Recommended(지워도 안전)'로 분류돼 있는지 대조해 보면, 이 도구가 해결하는 문제의 크기가 피부로 와닿는다.
무엇이 있어야 실제로 돌릴 수 있나.
| 항목 | 요구사항 |
|---|---|
| PC OS | Windows · macOS(Apple Silicon/Intel) · Linux — CI가 이 4타깃으로 바이너리를 빌드한다. |
| ADB(필수) | PC에 adb(Android SDK Platform Tools)가 설치돼 PATH에 있어야 한다. UAD-NG는 이걸 호출만 한다. |
| USB 드라이버 | 윈도는 제조사 USB 드라이버가 필요할 수 있다(맥/리눅스는 대개 불필요). |
| 안드로이드 폰 | 거의 모든 버전 지원. 코드가 SDK 19(킷캣)~최신까지 분기 처리. SDK 23+(안드 6.0~)가 가장 매끄럽다. |
| USB 디버깅 | 폰에서 개발자 옵션 → USB 디버깅을 켜야 한다(아래 함정 박스 참고). |
| 인터넷(선택) | 최신 패키지 목록·업데이트를 받을 때만 필요. 없으면 내장된 목록으로 동작. |
| GPU | iced가 wgpu(GPU)로 그린다. 듀얼 GPU PC는 WGPU_POWER_PREF=high를 자동 설정해 충돌을 피한다. |
USB 디버깅을 켰는데도 폰이 안 잡힌다면 십중팔구 두 가지다. ① 폰을 PC에 처음 연결하면 폰 화면에 "이 컴퓨터에서 USB 디버깅을 허용하시겠습니까?" 팝업이 뜬다 — 여기서 '허용'을 눌러야 상태가 unauthorized에서 device로 바뀐다. ② 케이블이 '충전 전용'이면 데이터가 안 통한다 — 데이터 전송용 케이블을 쓰고, 알림에서 USB 모드를 '파일 전송(MTP)' 등으로 바꿔 보라.
1) 터미널에서 adb devices → 폰이 device 상태로 보이는지 확인(목록이 비면 ADB/드라이버 문제). 2) unauthorized면 폰 화면의 허용 팝업 확인(안 뜨면 케이블 재연결). 3) 그래도 안 되면 폰에서 개발자 옵션 → USB 디버깅 권한 취소 후 재연결. 4) 윈도라면 제조사 USB 드라이버 설치. UAD-NG는 이 모든 게 정상일 때만 기기를 표시한다.
난이도별로 — 폰 없이도 할 수 있는 것부터.
Rust 툴체인(rustup)만 있으면 된다. 클론 후 cargo build --release로 빌드해 보라. 워크스페이스라 세 크레이트가 한 번에 빌드된다. cargo run -p uad-cli -- --help로 CLI 도움말을 띄워 clap이 만든 서브커맨드 구조를 구경하는 것만으로도 좋은 첫 걸음이다.
git clone https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation
cd universal-android-debloater-next-generation
cargo build --release
# CLI 도움말 보기(폰 없이 가능)
cargo run -p uad-cli -- --help
cargo run -p uad-cli -- list --help
코드를 안 건드려도 데이터로 노는 과제. resources/assets/uad_lists.json을 파이썬/jq로 열어, "Recommended 등급이 몇 개?", "list가 Google인 패키지는?", "내 폰 제조사(예: Samsung) 관련 패키지는 몇 개?"를 집계해 보라. 이 도구가 다루는 데이터의 규모와 큐레이션 품질을 직접 확인하는 과제다.
실제 폰으로, 가장 안전한 'Recommended' 등급 앱 하나를 골라 삭제가 아니라 '비활성화(Disable)'로 해 보고, 다시 'Enable'로 복원해 보라. disable → enable 왕복은 데이터 손실 없이 되돌릴 수 있어, '되돌리기 가능한 변경'을 안전하게 체험하기 좋다. CLI라면 먼저 --dry-run으로 어떤 명령이 나갈지 확인부터.
# 먼저 dry-run으로 '무슨 명령이 나갈지'만 미리보기 (실행 안 함)
uad disable com.example.someapp --dry-run
# 비활성화 → 확인 → 복원
uad disable com.example.someapp
uad list --state disabled # 꺼졌는지 확인
uad enable com.example.someapp # 되돌리기(restore)
실습 전 꼭 새겨라. android.ext.shared처럼 Unsafe로 표시된 핵심 시스템 컴포넌트를 지우면 부트루프(부팅 무한반복)에 빠지거나, 카메라·전화·계정 동기화 같은 기능이 통째로 망가질 수 있다. UAD-NG가 등급을 색으로 보여 주는 이유가 이것이다. 처음에는 Recommended만, 그것도 Disable로 시작하라. 중요한 변경 전에는 save.rs의 백업(현재 설치 상태를 JSON으로 저장) 기능을 먼저 써 두면 더 안전하다.
uad-cli/src/filters.rs와 main.rs의 list 서브커맨드를 읽고, 예컨대 "설명(description)에 특정 단어가 든 패키지만 보여 주는 필터"를 직접 추가해 보라(이미 --search가 비슷하게 있으니 그것을 확장). clap 어트리뷰트 추가 → 필터 함수 구현 → 코어 데이터와 연결까지, 이 프로젝트의 전체 흐름을 한 바퀴 도는 과제다.
가장 깊은 과제. adb.rs의 설계 철학(메서드 1개=명령 1개, 파이프 금지)을 지키면서, 예를 들어 pm path <package>(앱 APK 경로 조회)를 노출하는 메서드를 PmCommand에 추가해 보라. 타입스테이트 빌더를 깨지 않고 확장하는 감각, 그리고 clippy pedantic 게이트를 통과시키는 경험을 얻는다.
이 저장소를 발판으로 4주에 걸쳐 무엇을 익힐까.
도구를 이해하려면 먼저 adb 자체에 익숙해진다. Platform Tools를 설치하고 adb devices, adb shell, pm list packages, pm list users를 손으로 쳐 본다. "시스템 앱(-s)이란 무엇인가", "user 0의 의미", "disable과 uninstall의 차이"를 안드로이드 공식 ADB 문서로 정리한다. UAD-NG의 sync.rs 명령 표를 옆에 두고 대조하면 이해가 빠르다.
std::process::Command로 간단한 CLI 래퍼를 직접 만들어 본다(예: git이나 ffmpeg를 호출하는 작은 도구). stdout/stderr 캡처, 종료코드 분기, Result로 에러 전파, from_utf8_lossy로 안전한 디코딩까지 — UAD-NG adb.rs가 보여 준 패턴을 손으로 재현한다. 타입스테이트 빌더 패턴도 작은 예제로 흉내 내 본다.
iced 공식 예제(counter → todos)를 따라 하며 MVU(상태/Message/update/view)를 체득한다. 그다음 "리스트를 보여 주고, 체크박스로 선택하고, 버튼으로 동작"하는 작은 앱을 만들어 본다 — 바로 UAD-NG의 views/list.rs가 하는 일의 축소판이다. Task::perform으로 비동기 작업을 UI와 분리하는 법까지 가면 좋다.
"코어 라이브러리 + GUI + CLI" 구조의 미니 프로젝트를 직접 Cargo 워크스페이스로 짜 본다. 공유 의존성을 [workspace.dependencies]로 관리하고, GitHub Actions로 fmt · clippy · test 게이트를 건다. UAD-NG의 ci.yml·build_artifacts.yml을 참고해 멀티플랫폼 빌드 매트릭스를 흉내 내면, 오픈소스 배포의 실제 흐름을 익히게 된다.
| 주차 | 핵심 주제 | 산출물 |
|---|---|---|
| 1주차 | ADB · pm · 멀티유저 | 손으로 친 ADB 명령 메모 + disable/uninstall 차이 정리 |
| 2주차 | Rust 프로세스 제어·에러 처리 | 외부 CLI 래퍼 미니 도구 |
| 3주차 | iced MVU GUI | 체크박스 리스트 + 버튼 동작 앱 |
| 4주차 | 워크스페이스 + CI | 코어/GUI/CLI 3분할 + Actions 린트 게이트 |
이 문서에 나온 용어를 한자리에.
| 키워드 | 뜻 |
|---|---|
| 블로트웨어 | 원치 않는데 미리 깔린 앱. 저장공간·배터리·메모리를 잡아먹는다. |
| 디블로팅(Debloating) | 블로트웨어를 솎아내 폰을 가볍게 만드는 작업. UAD-NG의 본업. |
| ADB | Android Debug Bridge. PC에서 폰을 제어하는 구글 공식 도구. UAD-NG가 호출. |
| pm / am | 안드로이드 내장 Package Manager / Activity Manager. 실제 앱 설치·삭제·중지를 수행. |
| 루팅(Root) | 최고 관리자 권한 획득. 위험·보증무효. UAD-NG는 불필요한 게 강점. |
| uninstall --user 0 | 현재 사용자에서만 앱 제거. 시스템 이미지는 유지 → 공장초기화로 복구 가능. |
| disable / disable-user | 앱을 끄되 설치는 유지. 더 보수적·되돌리기 쉬움. |
| 부트루프(Bootloop) | 부팅이 무한 반복돼 폰을 못 쓰는 상태. 핵심 시스템 앱을 잘못 지우면 발생. |
| 안전등급 | uad_lists.json의 removal 값: Recommended · Advanced · Expert · Unsafe. |
| list 분류 | 패키지 출처: Aosp · Carrier(통신사) · Google · Misc · Oem(제조사). |
| iced | 순수 Rust GUI 프레임워크. Elm 아키텍처(MVU) 기반. wgpu로 GPU 렌더링. |
| MVU(Elm 아키텍처) | Model(상태)-View(화면)-Update(갱신) 단방향 흐름. iced의 동작 방식. |
| clap | Rust CLI 인자 파서. derive 매크로로 서브커맨드·플래그 자동 생성. |
| 타입스테이트 패턴 | 단계마다 다른 타입을 반환해 잘못된 호출 순서를 컴파일에서 차단. |
| 얇은 래퍼(thin wrapper) | 메서드 1개 = ADB 명령 1개. 마법·체이닝 없이 1:1 대응시켜 사고를 막는 철학. |
| dry-run | 실제 실행 없이 "무슨 일이 일어날지"만 보여 주는 모드. 위험한 도구의 안전장치. |
| Cargo 워크스페이스 | 여러 크레이트를 한 저장소에서 함께 빌드·관리하는 단위. |
| detached fork | 원본과 완전히 분리돼 독립 운영되는 포크. UAD-NG는 죽은 원조 UAD의 분리 포크. |
| Shizuku | PC 없이 폰에서 ADB급 권한을 쓰게 해 주는 방식(Canta 등이 사용). UAD-NG는 PC+ADB 방식. |
원문으로 더 깊이 — 공식 소스 위주.