TRENDSHIFT 딥다이브 · 일간 #10 · 2026-06-18

Universal Android Debloater (UAD-NG) 딥다이브
— 루팅 없이 ADB로 안드로이드 '쓰레기 앱'을 솎아내는 Rust GUI

UAD-NG(Universal Android Debloater Next Generation)루팅(root) 없이, PC에 폰을 USB로 연결한 채 ADB 명령만으로 안드로이드 기본탑재 블로트웨어(쓸데없는 시스템 앱)를 비활성화·삭제하는 Rust 크로스플랫폼 데스크톱 앱이다. 핵심은 커뮤니티가 5,300개 넘는 패키지를 직접 검수해 "지워도 안전 / 주의 / 위험" 등급을 매긴 목록(uad_lists.json). 사용자는 GUI에서 체크박스로 고르고 버튼 한 번 누르면, 내부적으로 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)
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 하드웨어 / 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

한 문장으로 이 프로젝트가 뭘 하는지부터.

UAD-NG = "루팅 없이, 검수된 목록을 보면서, 폰의 기본탑재 잡앱을 안전 등급별로 골라 비활성화·삭제하는 Rust 데스크톱 앱." 새 안드로이드 폰을 켜면 제조사(삼성·샤오미 등)·통신사·구글이 미리 깔아 둔 앱이 수백 개 들어 있다. 이 중 상당수는 백그라운드에서 돌며 배터리·메모리를 갉아먹고, 추적·광고로 프라이버시를 침해한다. UAD-NG는 PC에 폰을 연결하고 GUI에서 체크 → 버튼 한 번으로 이런 앱을 정리한다. 루팅이 필요 없다는 게 결정적이다(루팅은 보증 무효·뱅킹앱 차단·보안 위험을 부른다).

🧹 한 줄 비유

"공장 출고 옵션을 뜯어고치는 정비사"가 아니라, "검증된 체크리스트를 든 정리 전문가"

폰을 루팅하는 건 엔진룸을 통째로 뜯어 보증을 날려 먹는 위험한 개조다. UAD-NG는 그렇게 하지 않는다. 안드로이드가 이미 공식적으로 허락한 통로(ADB의 패키지 매니저)만 쓴다.

그리고 혼자 판단하지 않는다. 전 세계 사용자가 수년간 "이 앱은 지워도 폰이 멀쩡한가?"를 검증해 만든 체크리스트를 들고, 항목마다 "안전/주의/전문가/위험" 딱지를 보여 주며 정리한다. 아무거나 막 지우는 청소기가 아니라, 라벨을 읽고 분류하는 정리 전문가다.

용어
블로트웨어 (Bloatware)
사용자가 원치 않았는데 기기에 미리 깔려 나오는 앱. 제조사 데모, 통신사 앱, 잘 안 쓰는 구글 서비스 등. 'bloat(부풀다)'에서 온 말로, 저장공간·배터리·메모리를 부풀려 잡아먹는다는 뜻이다.
용어
ADB (Android Debug Bridge)
구글이 공식 제공하는 안드로이드 제어용 PC 도구. USB(또는 Wi-Fi)로 연결한 폰에 명령을 보내 앱 설치·삭제·로그 확인 등을 한다. UAD-NG는 이 adb 실행파일을 뒤에서 호출하는 'GUI 껍데기'에 가깝다.
용어
루팅 (Rooting)
안드로이드의 최고 관리자(root) 권한을 강제로 얻는 것. 무엇이든 할 수 있게 되지만, 제조사 보증 무효·뱅킹/페이 앱 차단·보안 취약·벽돌(부팅 불능) 위험이 따른다. UAD-NG의 핵심 강점은 바로 이 루팅이 전혀 필요 없다는 점이다.

구조를 한 줄로 그리면 이렇다 — [사용자가 GUI에서 체크] → [UAD-NG가 안전한 ADB 명령 문자열을 조립] → [PC의 adb가 USB로 폰에 전달] → [폰의 패키지 매니저가 실행]. 즉 UAD-NG 자체는 폰을 직접 만지지 않고, "어떤 명령을, 어떤 안전등급으로, 어떤 사용자 프로필에" 보낼지를 안전하게 결정·조립하는 두뇌 역할이다.

2왜 주목받는가

TrendShift 일간 10위 — ★7.6k를 모은 이유.

이 저장소는 2023년 10월 시작된 비교적 신생 프로젝트인데도 별 7,600개를 넘겼다. 핵심 동력은 "내 폰을 내가 통제하고 싶다"는 보편적 욕구와, 그걸 루팅이라는 위험 없이, 검증된 데이터로 풀어 줬다는 점이다. 이유를 다섯으로 정리한다.

① 비루팅 — 위험·보증 손실 없이 정리

경쟁 도구나 "커스텀 롬"은 흔히 루팅·부트로더 언락을 요구한다. 그러면 뱅킹앱이 막히고 보증이 날아간다. UAD-NG는 OEM이 공식 허용한 ADB 통로만 써서 일반 사용자도 안전하게 접근한다. 게다가 대부분의 동작이 '완전 삭제'가 아니라 현재 사용자(user 0)에서만 제거라, 공장초기화하면 되살아난다(되돌릴 길이 있다).

② 5,300개+ 커뮤니티 검수 목록 = 진짜 자산

이 프로젝트의 심장은 코드가 아니라 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 + GUI + CLI + 코어 = 깔끔한 재작성

원조는 파이썬/셸 기반이었지만, 차세대는 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 박스 참고).

3기술 스택 전체 지도

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줄이다.

용어
Cargo 워크스페이스 (Workspace)
여러 개의 Rust 패키지(크레이트)를 한 저장소에서 함께 빌드·관리하는 단위. 의존성 버전을 [workspace.dependencies]에 한 번만 적고 각 크레이트가 .workspace = true로 물려받는다. "공유 라이브러리 1개 + 그걸 쓰는 앱 여러 개" 구조에 딱 맞다.

코어 라이브러리 (uad-core) — 두뇌

GUI와 CLI가 공유하는 핵심 로직이 전부 여기 있다. 의존성이 의도적으로 가볍다.

크레이트역할
serde / serde_json패키지 목록 JSON ↔ Rust 구조체 변환(직렬화). uad_lists.json 파싱의 핵심.
ureq가벼운 HTTP 클라이언트. GitHub에서 최신 목록을 GET으로 받는다(비동기 런타임 불필요).
retry네트워크 실패 시 재시도(1초 간격 최대 60회). 목록 다운로드를 끈질기게 한다.
csv선택한 패키지 목록을 CSV로 내보내기(export).
toml설정 파일(config) 읽기/쓰기. 사용자 테마·전문가 모드 등을 저장.
dirsOS별 설정·캐시 폴더 경로를 안전하게 찾기(윈도/맥/리눅스 제각각).
chrono날짜·시간(로그 타임스탬프, 백업 파일명).
log로깅 추상화(실제 출력은 GUI의 fern이 담당).
flate2 + tar선택 기능(self-update). 새 릴리스를 받아 압축 해제·교체.

GUI (uad-gui) — 얼굴

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-lightOS의 다크/라이트 모드 감지 → 테마 자동 전환.
fern실제 로그 파일(uadng.log) 기록 + 컬러 콘솔 출력.
image로고·아이콘 이미지 로딩(img feature).
embed-resource빌드 의존성. 윈도 .exe에 아이콘·매니페스트를 박아 넣음.

CLI (uad-cli) — 자동화용 손

clap 4.5명령줄 인자 파서. derive 기능으로 구조체에 어트리뷰트만 달면 서브커맨드·플래그가 자동 생성.
clap_completebash/zsh/fish 등 셸 자동완성 스크립트 생성(completions 커맨드).
tokio (full)비동기 런타임. CLI의 main#[tokio::main]으로 돈다.
rustyline대화형 REPL 모드의 입력 편집(화살표·히스토리). uad repl.

빌드 · CI · 품질 게이트

.github/workflows에 3개 파이프라인이 있다. ci.yml은 매 PR마다 check · test · clippy · fmt 4종 린트를 돌린다(clippy::all을 에러로 취급). build_artifacts.ymllinux · 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"안전성·일관성 규칙이 빼곡하다. 폰을 망가뜨릴 수 있는 도구이니만큼, "위험한 코드를 컴파일 단계에서 막는다"는 철학이 설정에 그대로 박혀 있다.

4아키텍처 심화 분석

데이터가 GUI에서 폰까지 흐르는 길, 그리고 안전을 보장하는 핵심 패턴.

전체 구조도 — GUI ↔ ADB ↔ 디바이스

┌─────────────────────────────────────────────────────────────┐ │ 당신의 PC (호스트) │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ uad-gui │ │ uad-cli │ │ │ │ (iced GUI) │ │ (clap CLI) │ │ │ │ 바이너리: │ │ 바이너리: │ │ │ │ uad-ng │ │ uad │ │ │ └──────┬───────┘ └──────┬───────┘ │ │ │ 둘 다 같은 로직 호출 │ │ │ └─────────┬──────────────┘ │ │ ▼ │ │ ┌───────────────────────┐ │ │ │ uad-core │ ← 두뇌(공유 라이브러리) │ │ │ ───────────────── │ │ │ │ uad_lists.rs : 목록 │ 5,361개 패키지 + │ │ │ sync.rs : 상태전이 │ 안전등급 로드/파싱 │ │ │ adb.rs : 명령조립 │ 상태→ADB명령 매핑 │ │ └───────────┬───────────┘ │ │ │ 명령 문자열 조립 │ │ │ 예) "pm uninstall --user 0 com.foo" │ │ ▼ │ │ ┌───────────────────────┐ │ │ │ std::process::Command │ ← adb 실행파일을 │ │ │ ("adb", ...) │ 서브프로세스로 호출 │ │ └───────────┬───────────┘ │ └──────────────────────┼────────────────────────────────────────┘ │ USB 케이블 (또는 Wi-Fi ADB) ▼ ┌──────────────────────────────────────────────┐ │ 안드로이드 기기 (타깃) │ │ │ │ adbd (데몬) ──▶ pm (Package Manager) │ │ am (Activity Manager) │ │ │ │ │ ▼ │ │ 앱이 disable / uninstall 됨 │ └──────────────────────────────────────────────┘

핵심은 UAD-NG가 폰을 직접 건드리지 않는다는 점이다. 폰 쪽에서 실제로 일하는 건 안드로이드 내장 pm(패키지 매니저)·am(액티비티 매니저)이고, UAD-NG는 그저 "어떤 pm 명령을, 어떤 안전등급으로, 어떤 user에" 보낼지 결정해 adb에게 넘길 뿐이다.

핵심 패턴 ① — '얇은 래퍼'와 타입스테이트 빌더 (adb.rs)

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 → 애초에 만들 수 없음

핵심 패턴 ② — disable vs uninstall --user 0 (sync.rs)

UAD-NG의 안전성은 sync.rsapply_pkg_state_commands() 함수 하나에 압축돼 있다. "지금 상태"와 "원하는 상태", 그리고 안드로이드 SDK 버전에 따라 어떤 ADB 명령을 쏠지 정확히 분기한다. 주석엔 굵게 적혀 있다: "상태를 바꾸는 명령을 항상 맨 앞에 둘 것."

원하는 상태SDK 23+ (안드 6.0~)에서 실행되는 명령의미
Uninstalled
(삭제)
pm uninstall --user 0현재 사용자에서만 제거. 시스템 이미지는 그대로 → 공장초기화로 복구 가능.
Disabled
(비활성화)
pm disable-useram force-stoppm 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.rslist_users_idx_prot()로 사용자 목록을 읽고, is_protected_user()건드리면 안 되는 보호 사용자를 가려낸다. 또 capture_cross_user_states() / detect_cross_user_behavior()"user 0에서 지웠는데 다른 프로필에서도 사라졌다" 같은 의도치 않은 교차 영향을 감지해 사용자에게 알린다. 폰마다 제조사가 ADB 동작을 미묘하게 바꿔 놓는 현실을 방어적으로 다루는 코드다.

핵심 패턴 ④ — iced의 Elm 아키텍처 (gui.rs)

GUI는 UadGui 구조체(=상태)와 Message 열거형(=일어날 수 있는 사건)으로 돌아간다. update(&mut self, msg)가 메시지를 받아 상태를 바꾸고, view()가 상태를 화면으로 그린다. ADB처럼 오래 걸리는 작업은 Task::perform(async {...}, Message::···)비동기로 띄워 UI가 멈추지 않게 한다. nb_running_async_adb_commands 카운터로 "지금 몇 개의 ADB 작업이 도는 중인지"를 추적한다.

용어
Elm 아키텍처 (Model-View-Update, MVU)
상태(Model) 하나를 진실의 원천으로 두고, 사건(Message)이 들어오면 update가 새 상태를 만들고, view가 그 상태를 화면으로 변환하는 단방향 흐름. 상태 변경 경로가 update 한 곳뿐이라 추적·디버깅이 쉽다. iced가 이 방식을 따른다.

5디렉토리 구조 해부

어디에 뭐가 있는지 — 실제 트리를 따라가며.

universal-android-debloater-next-generation/ ├── Cargo.toml ← 워크스페이스 정의 + 공유 의존성/린트 ├── Cargo.lock ← 의존성 버전 잠금(재현 가능한 빌드) ├── build.rs ← 빌드 스크립트 ├── flake.nix / flake.lock ← Nix 재현 가능 개발환경 ├── clippy.toml ← 린트 세부 설정 │ ├── crates/ ← ★ 소스의 전부가 여기 │ ├── uad-core/ ← 공유 두뇌 라이브러리 │ │ └── src/ │ │ ├── lib.rs 모듈 선언 + 전역 경로(CONFIG_DIR/CACHE_DIR) │ │ ├── adb.rs ADB 명령 빌더(타입스테이트, 얇은 래퍼) [496줄] │ │ ├── sync.rs 상태전이·명령조립·멀티유저·에러번역 [647줄] │ │ ├── uad_lists.rs 패키지 목록 JSON 로드/파싱·등급 enum [269줄] │ │ ├── config.rs 설정(테마·전문가모드·백업폴더) 읽기/쓰기 │ │ ├── save.rs 폰 백업/복원(JSON 직렬화) │ │ ├── update.rs 자가 업데이트(릴리스 확인·교체) │ │ └── utils.rs 공용 헬퍼(디렉토리 생성 등) │ │ │ ├── uad-gui/ ← iced 데스크톱 앱 (바이너리: uad-ng) │ │ └── src/ │ │ ├── main.rs 진입점·로거 설정·GPU 우선순위 강제 │ │ ├── gui.rs UadGui 상태 + Message + update/view [475줄] │ │ ├── theme.rs 색 테마 정의 │ │ ├── style.rs 위젯 스타일 │ │ ├── views/ 화면 단위 │ │ │ ├── list.rs 앱 목록·필터·선택·내보내기(메인 화면) │ │ │ ├── settings.rs 설정 화면 │ │ │ └── about.rs 정보 화면 │ │ └── widgets/ 재사용 UI 부품 │ │ ├── package_row.rs 앱 한 줄(체크박스+등급뱃지) │ │ ├── navigation_menu.rs / modal.rs / text.rs │ │ │ └── uad-cli/ ← 명령줄 도구 (바이너리: uad) │ └── src/ │ ├── main.rs clap 서브커맨드 정의(devices/list/rm/...) │ ├── commands.rs 각 커맨드 실제 동작 [476줄] │ ├── repl.rs 대화형 REPL 모드(rustyline) [374줄] │ ├── filters.rs 목록 필터(상태/등급/리스트) │ └── device.rs / output.rs │ ├── resources/ │ ├── assets/ │ │ ├── uad_lists.json ← ★★ 5,361개 패키지 + 안전등급(프로젝트의 심장) │ │ ├── icon.ico / logo-*.png / icons.ttf │ │ └── manifest.rc 윈도 실행파일 매니페스트 │ └── screenshots/ 버전별 스크린샷 │ └── .github/workflows/ ├── ci.yml check·test·clippy·fmt 4종 린트 ├── build_artifacts.yml linux/macos/macos-intel/windows 빌드 └── release.yml 태그 → 릴리스 자동화

핵심만 짚으면 — 코드를 읽고 싶다면 crates/uad-core/src/sync.rsadb.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' 등급으로 사용자에게 경고
}

6학습 포인트 (기술별)

이 저장소를 읽으면 구체적으로 무엇을 배우는가.

① Rust로 '외부 프로그램을 안전하게 조종'하는 법

이 프로젝트의 가장 실용적인 교재 가치다. std::process::Command로 외부 CLI(adb)를 호출하고, stdout/stderr·종료코드를 받아 Result<String, String>로 다루는 패턴이 adb.rs에 모범적으로 들어 있다. 윈도에서 creation_flags(0x0800_0000)cmd 창이 깜빡 뜨는 것을 막는 실전 팁, String::from_utf8_lossy로 일부 OEM이 뱉는 비UTF-8 출력에도 패닉하지 않게 막는 방어 코드까지 배울 수 있다.

배울 점
"문자열 타입(stringly-typed)" 대신 "강한 타입(strongly-typed)"

패키지명을 그냥 String으로 두면 빈 문자열·이상한 값이 명령에 섞여 사고가 난다. UAD-NG는 PackageId·PmListPacksFlag 같은 전용 타입을 만들어, 유효하지 않으면 애초에 값이 만들어지지 않게 한다. "잘못된 상태를 표현 불가능하게 만든다(make illegal states unrepresentable)"는 Rust 격언의 살아 있는 예시다.

② iced로 순수 Rust GUI 만들기 (Elm 아키텍처)

웹·일렉트론 없이 네이티브 GUI를 Rust만으로 짜는 법을 gui.rs에서 배운다. 상태 구조체 + Message enum + update/view의 MVU 흐름, 그리고 무거운 작업을 Task::perform으로 비동기 처리해 UI를 멈추지 않게 하는 패턴이 깔끔하다. views/(화면)와 widgets/(부품)로 나눈 폴더 구조는 GUI를 모듈화하는 표준 감각을 길러 준다.

③ clap으로 견고한 CLI 설계

uad-cli/src/main.rs는 clap derive 매크로의 교과서다. enum Commandsdevices · 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]에 한 번만 적는 관리 방식도 함께 익힌다.

⑤ 안드로이드 내부(ADB·pm·멀티유저) 지식

코드를 따라가다 보면 자연스럽게 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(지워도 안전)'로 분류돼 있는지 대조해 보면, 이 도구가 해결하는 문제의 크기가 피부로 와닿는다.

7하드웨어 / 시스템 요구사항

무엇이 있어야 실제로 돌릴 수 있나.

항목요구사항
PC OSWindows · 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 디버깅을 켜야 한다(아래 함정 박스 참고).
인터넷(선택)최신 패키지 목록·업데이트를 받을 때만 필요. 없으면 내장된 목록으로 동작.
GPUiced가 wgpu(GPU)로 그린다. 듀얼 GPU PC는 WGPU_POWER_PREF=high를 자동 설정해 충돌을 피한다.
함정 — 첫 실행에서 가장 많이 막히는 지점
"기기가 안 보여요 / unauthorized"

USB 디버깅을 켰는데도 폰이 안 잡힌다면 십중팔구 두 가지다. ① 폰을 PC에 처음 연결하면 폰 화면에 "이 컴퓨터에서 USB 디버깅을 허용하시겠습니까?" 팝업이 뜬다 — 여기서 '허용'을 눌러야 상태가 unauthorized에서 device로 바뀐다. ② 케이블이 '충전 전용'이면 데이터가 안 통한다 — 데이터 전송용 케이블을 쓰고, 알림에서 USB 모드를 '파일 전송(MTP)' 등으로 바꿔 보라.

해결 — 연결 점검 순서
막히면 이 순서로 확인

1) 터미널에서 adb devices → 폰이 device 상태로 보이는지 확인(목록이 비면 ADB/드라이버 문제). 2) unauthorized면 폰 화면의 허용 팝업 확인(안 뜨면 케이블 재연결). 3) 그래도 안 되면 폰에서 개발자 옵션 → USB 디버깅 권한 취소 후 재연결. 4) 윈도라면 제조사 USB 드라이버 설치. UAD-NG는 이 모든 게 정상일 때만 기기를 표시한다.

8직접 해볼 수 있는 실습 과제

난이도별로 — 폰 없이도 할 수 있는 것부터.

🟢 난이도 1 — 소스 빌드해 보기 (폰 불필요)

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

🟢 난이도 2 — uad_lists.json 데이터 분석

코드를 안 건드려도 데이터로 노는 과제. resources/assets/uad_lists.json을 파이썬/jq로 열어, "Recommended 등급이 몇 개?", "list가 Google인 패키지는?", "내 폰 제조사(예: Samsung) 관련 패키지는 몇 개?"를 집계해 보라. 이 도구가 다루는 데이터의 규모와 큐레이션 품질을 직접 확인하는 과제다.

🟡 난이도 3 — 안전한 앱 하나 비활성화 → 복원 (실기기)

실제 폰으로, 가장 안전한 '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)
⚠️ 반드시 — 시스템 앱을 잘못 지우면
'Unsafe / Expert' 등급은 건드리지 말 것

실습 전 꼭 새겨라. android.ext.shared처럼 Unsafe로 표시된 핵심 시스템 컴포넌트를 지우면 부트루프(부팅 무한반복)에 빠지거나, 카메라·전화·계정 동기화 같은 기능이 통째로 망가질 수 있다. UAD-NG가 등급을 색으로 보여 주는 이유가 이것이다. 처음에는 Recommended만, 그것도 Disable로 시작하라. 중요한 변경 전에는 save.rs의 백업(현재 설치 상태를 JSON으로 저장) 기능을 먼저 써 두면 더 안전하다.

🟠 난이도 4 — CLI에 새 필터 추가하기

uad-cli/src/filters.rsmain.rslist 서브커맨드를 읽고, 예컨대 "설명(description)에 특정 단어가 든 패키지만 보여 주는 필터"를 직접 추가해 보라(이미 --search가 비슷하게 있으니 그것을 확장). clap 어트리뷰트 추가 → 필터 함수 구현 → 코어 데이터와 연결까지, 이 프로젝트의 전체 흐름을 한 바퀴 도는 과제다.

🔴 난이도 5 — adb.rs에 새 '얇은 래퍼' 메서드 추가

가장 깊은 과제. adb.rs의 설계 철학(메서드 1개=명령 1개, 파이프 금지)을 지키면서, 예를 들어 pm path <package>(앱 APK 경로 조회)를 노출하는 메서드를 PmCommand에 추가해 보라. 타입스테이트 빌더를 깨지 않고 확장하는 감각, 그리고 clippy pedantic 게이트를 통과시키는 경험을 얻는다.

9관련 기술 심화 학습 로드맵

이 저장소를 발판으로 4주에 걸쳐 무엇을 익힐까.

1주차 — ADB와 안드로이드 패키지 매니저 기초

도구를 이해하려면 먼저 adb 자체에 익숙해진다. Platform Tools를 설치하고 adb devices, adb shell, pm list packages, pm list users를 손으로 쳐 본다. "시스템 앱(-s)이란 무엇인가", "user 0의 의미", "disable과 uninstall의 차이"를 안드로이드 공식 ADB 문서로 정리한다. UAD-NG의 sync.rs 명령 표를 옆에 두고 대조하면 이해가 빠르다.

2주차 — Rust로 외부 프로세스 다루기 + 에러 처리

std::process::Command로 간단한 CLI 래퍼를 직접 만들어 본다(예: git이나 ffmpeg를 호출하는 작은 도구). stdout/stderr 캡처, 종료코드 분기, Result로 에러 전파, from_utf8_lossy로 안전한 디코딩까지 — UAD-NG adb.rs가 보여 준 패턴을 손으로 재현한다. 타입스테이트 빌더 패턴도 작은 예제로 흉내 내 본다.

3주차 — iced로 GUI 한 화면 만들기

iced 공식 예제(counter → todos)를 따라 하며 MVU(상태/Message/update/view)를 체득한다. 그다음 "리스트를 보여 주고, 체크박스로 선택하고, 버튼으로 동작"하는 작은 앱을 만들어 본다 — 바로 UAD-NG의 views/list.rs가 하는 일의 축소판이다. Task::perform으로 비동기 작업을 UI와 분리하는 법까지 가면 좋다.

4주차 — 워크스페이스 설계 + CI 품질 게이트

"코어 라이브러리 + 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 린트 게이트

10핵심 키워드 사전

이 문서에 나온 용어를 한자리에.

키워드
블로트웨어원치 않는데 미리 깔린 앱. 저장공간·배터리·메모리를 잡아먹는다.
디블로팅(Debloating)블로트웨어를 솎아내 폰을 가볍게 만드는 작업. UAD-NG의 본업.
ADBAndroid 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의 동작 방식.
clapRust CLI 인자 파서. derive 매크로로 서브커맨드·플래그 자동 생성.
타입스테이트 패턴단계마다 다른 타입을 반환해 잘못된 호출 순서를 컴파일에서 차단.
얇은 래퍼(thin wrapper)메서드 1개 = ADB 명령 1개. 마법·체이닝 없이 1:1 대응시켜 사고를 막는 철학.
dry-run실제 실행 없이 "무슨 일이 일어날지"만 보여 주는 모드. 위험한 도구의 안전장치.
Cargo 워크스페이스여러 크레이트를 한 저장소에서 함께 빌드·관리하는 단위.
detached fork원본과 완전히 분리돼 독립 운영되는 포크. UAD-NG는 죽은 원조 UAD의 분리 포크.
ShizukuPC 없이 폰에서 ADB급 권한을 쓰게 해 주는 방식(Canta 등이 사용). UAD-NG는 PC+ADB 방식.

11참고 링크

원문으로 더 깊이 — 공식 소스 위주.