스킬 모음
95ea931 기준입니다.android-skills는 Android 개발 작업별로 SKILL.md, 참고 문서, 예제 코드와 보조 도구를 묶은 저장소입니다. 이 프로젝트의 핵심 산출물은 실행 파일보다, 에이전트가 무엇을 확인하고 어떤 순서로 고칠지 정한 작업 절차입니다.
예를 들어 “AGP 9로 올려 줘”라는 요청은 버전 숫자 하나를 바꾸는 일처럼 보입니다. 실제로는 KSP·Hilt 호환성, 내장 Kotlin, 새로운 DSL, kapt와 BuildConfig 설정까지 확인해야 합니다. build-system/agp/agp-9-upgrade/SKILL.md는 이 점검 순서와 확인할 문서, 마지막 검증 명령을 명시합니다.
스킬을 읽고 파일을 찾거나 명령을 실행하는 주체는 외부 코딩 에이전트입니다. 이 저장소 자체가 LLM 서버를 띄우거나 Android Studio를 대신 구현하지는 않습니다. GitHub의 주 언어 표시는 Kotlin이지만, 저장소를 이해하려면 Markdown 지침과 Python 보조 코드도 함께 봐야 합니다.
숙련된 기사도 처음 보는 차에서는 나사의 위치와 점검 순서를 확인합니다. 스킬의 설명은 “어느 고장에 쓰는 매뉴얼인가”, 본문은 정비 순서, references/는 상세 도면입니다.
일부 작업에는 실제 공구도 들어 있습니다. Play 정책 점검에는 파일을 훑는 Python 스캐너가 있고, Glance 위젯에는 가져다 분석할 Kotlin 레이아웃이 있습니다. 다만 매뉴얼과 예제가 있다고 해서 수리 결과가 자동으로 보장되지는 않습니다.
glance-canonical-layouts를 선택합니다.SKILL.md에서 위젯 제공자·레이아웃·데이터·리소스의 대응 관계를 읽습니다.이 흐름의 2번은 README가 설명하는 자동 활성화 방식입니다. 자동 선택 로직이나 모델 API 호출 코드는 이 저장소에 없습니다. 여기서 확인할 수 있는 것은 선택에 쓰이는 설명과, 선택 이후 읽을 지침입니다.
스킬의 조건문을 읽고, Python 함수가 남기는 JSON을 추적하고, Kotlin의 상태 흐름을 연결해 봅니다. 최종 목표는 “AI가 그렇게 말했다”를 넘어 어느 파일과 어떤 증거 때문에 그 판단을 했는지 설명할 수 있게 되는 것입니다.
이 글의 선정 기준은 2026-10-23 GitHub Trending Weekly 4위입니다. 같은 날 GitHub 기준 스타는 9,303개, 포크는 594개입니다. 순위와 누적 숫자는 관심의 크기를 보여 주지만, 어떤 기능이 유입을 일으켰는지 증명하는 사용 통계는 아닙니다.
코드와 문서에서 읽을 수 있는 관심 요인은 구체적입니다. AGP 9 마이그레이션, Navigation 3, Wear 위젯, XR 안경, 온디바이스 AI처럼 버전과 환경 조건을 많이 따지는 주제가 한곳에 모여 있습니다. README.md는 평가에서 LLM이 약한 작업에 집중한다고 설명합니다. 다만 해당 커밋에는 그 주장을 재현할 평가 데이터나 점수표가 없습니다.
주목할 장점은 공식 문서를 요약한 양보다, 적용 조건과 검증 절차를 작업별로 함께 제공한다는 점입니다. 예를 들어 AGP 스킬은 KMP 프로젝트를 대상에서 제외하고, R8 스킬은 AGP·R8 버전에 따라 분석 경로를 세 갈래로 나눕니다.
.claude-plugin/plugin.json과 .codex-plugin/plugin.json에는 같은 33개 스킬 경로가 등록되어 있습니다. README는 Android CLI를 통한 설치도 안내합니다. 같은 기술 지침을 여러 도구에서 재사용할 접점이 있다는 뜻이며, 모든 에이전트에서 동일하게 동작한다는 시험 결과를 뜻하지는 않습니다.
Agent Skills 형식의 이름·설명·본문 분리는 팀 자체 가이드를 만들 때도 응용할 수 있습니다. “우리 앱에서만 지켜야 하는 마이그레이션 규칙”을 긴 채팅 기록에 묻어 두지 않고 파일로 관리할 수 있기 때문입니다.
| 접근 방식 | 잘하는 일 | android-skills의 장점과 경계 |
|---|---|---|
| 일회성 프롬프트 | 상황에 맞춰 빠르게 요청 | 반복할 절차를 파일·참조 자료로 남김. 개별 프로젝트 맥락은 여전히 필요 |
| 일반 코딩 에이전트 | 탐색·수정·도구 실행 | Android 판단 기준을 보충함. 에이전트 자체를 대체하는 제품은 아님 |
| 공식 문서 직접 읽기 | API 의미와 제약을 상세히 이해 | 작업 순서와 읽을 문서를 좁혀 줌. 원문 확인은 계속 필요 |
| Android CLI | SDK·기기·앱 작업을 명령으로 수행 | 스킬은 도구를 언제 쓰는지 안내. CLI 실행 구현은 별도 배포물 |
| 자체 스킬 모음 | 팀 규칙과 내부 환경에 최적화 | 공식 Android 자료를 출발점으로 활용. 사내 구조까지 자동으로 알지는 못함 |
이 표는 기능적 역할을 비교한 것입니다. 속도·정확도·토큰 비용을 동일 조건에서 측정한 순위표가 아닙니다. 특히 “공식”이라는 출처와 “모든 생성 코드가 정확하다”는 보장은 구별해야 합니다.
공식 문서가 교과서라면, 스킬은 특정 문제를 풀 때 펼치는 실험 안내서에 가깝습니다. 교과서에는 원리가 있고, 안내서에는 준비물과 순서가 있습니다. 좋은 실습에는 둘 다 필요합니다.
2026-03-16에 생성되어 2026-10-21까지 갱신된 저장소입니다. 최신 릴리스는 v1.0.16입니다. 그러나 버전이 1.x라는 사실만으로 보조 도구의 완결성이나 전체 작업의 재현성이 보장되지는 않습니다. 뒤에서 살펴볼 명령 불일치와 누락된 보조 스크립트는 이 구별이 왜 필요한지 보여 줍니다.
README는 피드백·버그·새 스킬 요청을 GitHub issue로 받고, 현재 외부 코드 기여는 받지 않는다고 명시합니다. 학습이나 내부 적용은 가능하지만, 일반적인 공개 PR 참여 흐름을 당연하게 기대하면 안 됩니다.
스킬이 앱에 추가하라고 안내하는 라이브러리 버전은, 이 저장소 전체의 실행 의존성 버전이 아닙니다. 이 구별 없이 Kotlin·Compose·Python을 한 덩어리의 앱 스택으로 설명하면 프로젝트의 성격을 놓치게 됩니다.
| 영역 | 실제 파일·기술 | 확인한 범위 |
|---|---|---|
| 서비스 백엔드 | 상주 API 서버·DB 설정 없음 | 정책 점검의 입력·중간 결과·보고서를 파일로 전달 |
| 실행 보조 도구 | play/play-policy-insights/scripts/의 Python 5개 | argparse, json, re, ElementTree 등 표준 라이브러리와 같은 폴더 모듈 사용 |
| 병렬 파일 검사 | concurrent.futures | scanner.perform_scan()의 작업 스레드 상한 8 |
| 웹 자료 수집 | urllib.request와 html.parser | Play 스토어 상세·데이터 보안 페이지 수집, 요청 제한 시간 10초 |
| 템플릿 처리 | template_engine.py | 외부 템플릿 라이브러리 없이 조건·반복·변수 치환 구현 |
추적 파일에는 package.json, pyproject.toml, requirements.txt, Cargo.toml, go.mod, Dockerfile이 없습니다. Python의 최소 버전도 별도 패키지 설정에 고정되어 있지 않습니다. 따라서 특정 Python 버전이나 설치 가능한 서버 패키지를 추정해 적어서는 안 됩니다.
React·Vue 같은 웹 프론트엔드와 npm 빌드 설정은 없습니다. 화면 코드의 중심은 system/glance-canonical-layouts/references/에 들어 있는 Kotlin 예제입니다. 이들은 홈 화면 위젯을 위한 Glance 코드이며, 저장소 루트에서 곧바로 빌드할 수 있는 Android 앱 모듈은 아닙니다.
| 참조 기술 | 문서에 명시된 버전·조건 | 근거 |
|---|---|---|
| Jetpack Glance | appwidget·material3·preview 계열 1.2.0 이상 | system/glance-canonical-layouts/SKILL.md의 의존성 절 |
| 스냅 스크롤 | Glance 1.3.0-alpha02 이상, compileSdk = 37, AGP 9.1.0 이상 | 같은 스킬의 기능별 조건 |
| Coil | 예시 io.coil-kt:coil:2.7.0 | 비트맵을 읽는 가짜 데이터 저장소를 복사할 때의 안내 |
| LiteRT-LM | litertlm-android:0.18.0, minSdk = 24 | device-ai/litert/litert-lm/SKILL.md |
| ML Kit Prompt API | genai-prompt:1.0.0-beta4 이상, API 26 이상 | device-ai/ml-kit-genai-prompt-api/SKILL.md |
위 표의 Android 라이브러리 값은 스킬 문서의 요구·예시입니다. 저장소에는 이를 한 번에 해결하는 Gradle 의존성 파일이나 잠금 파일이 없습니다. Android SDK·Kotlin·Compose의 단일한 “저장소 공통 버전”도 정해져 있지 않습니다.
build-system/agp/agp-9-upgrade/SKILL.md는 KSP 2.3.6 이상, Hilt 2.59.2 이상을 확인하라고 안내합니다. 포함된 build-system/agp/agp-9-upgrade/references/android/build/releases/agp-9-0-0-release-notes.md의 AGP 9.0 호환성 표는 다음과 같습니다.
| 도구 | 최소 버전 | 문서의 기본값 |
|---|---|---|
| Gradle | 9.1.0 | 9.1.0 |
| SDK Build Tools | 36.0.0 | 36.0.0 |
| JDK | 17 | 17 |
| Kotlin Gradle plugin | 2.2.10 | 2.2.10 |
| NDK | 최소값 미지정 | 28.2.13676358 |
여기서 AGP 9.1.0과 Gradle 9.1.0은 이름이 다른 도구의 버전입니다. 숫자가 같아도 바꿔 쓰면 안 됩니다. 이 표는 AGP 9.0 설명에 붙은 표이므로, 더 높은 AGP의 조건까지 자동으로 대표하지 않습니다.
.claude-plugin/plugin.json과 .codex-plugin/plugin.json의 실제 version은 모두 1.0.16입니다.ubuntu-latest에서 동작합니다. OS 이미지는 이 커밋에서 특정 버전으로 고정되어 있지 않습니다.actions/checkout은 11bd71901bbe5b1630ceea73d27597364c9af683에 고정되어 있고, 주석은 v4.2.2입니다.update-skills.yml은 공식 ZIP과 github-skills 브랜치를 합친 뒤 스킬 목록을 갱신하고 PR을 만듭니다. 예약 실행 설정은 주석 처리되어 있습니다.create-release.yml은 버전을 맞춘 뒤 git archive로 ZIP을 만들고 gh release create를 호출합니다.Perfetto의 확장자 없는 profilers/android-profiler/references/perfetto/bin/trace_processor는 미리 빌드된 도구를 선택·다운로드하는 Python 래퍼입니다. 내장 매니페스트는 v57.2를 가리킵니다. 이것을 저장소에 포함된 네이티브 실행 바이너리로 오해하지 않는 것이 좋습니다.
이 저장소의 구조는 “작업을 고르는 설명 → 필요한 참조 → 증거 수집 → 판단 → 검증”이라는 연결로 이해하면 쉽습니다. 모든 스킬에 실행 코드가 있는 것은 아닙니다. 아래에서 Python 파이프라인은 Play 정책 점검 스킬의 구체적인 사례입니다.
SKILL.md 맨 위의 YAML 형식 머리말에는 name, description, license, metadata가 있습니다. 에이전트는 설명으로 적용 가능성을 판단하고, 선택한 뒤 본문과 연결된 자료를 읽을 수 있습니다. 이것을 점진적 공개라고 이해하면 됩니다.
실제 예로 AGP 스킬은 진입점에서 마이그레이션 단계를 설명하고, BuildConfig 문제는 build-system/agp/agp-9-upgrade/references/buildconfig.md, kapt 문제는 build-system/agp/agp-9-upgrade/references/ksp-kapt.md로 보냅니다. 글을 단순히 짧게 자른 것이 아니라, 분기 조건과 세부 지식을 분리한 설계입니다.
# build-system/agp/agp-9-upgrade/SKILL.md의 구조를 줄인 예
---
name: agp-9-upgrade
description: ...
---
현재 AGP 버전과 KMP 사용 여부 확인
→ 의존성 확인
→ 내장 Kotlin 전환
→ 새 DSL 전환
→ kapt/KSP 확인
→ BuildConfig 확인
→ 설정 정리와 검증
초보자가 배울 점은 “상세 문서를 많이 연결하기”보다 언제 어떤 문서를 열어야 하는지 조건을 적기입니다. 이름과 설명만 보고도 대상 작업과 제외 조건을 구별할 수 있어야 합니다.
play/play-policy-insights/scripts/orchestrator.py의 main()은 CLI 진입점입니다. init과 aggregate라는 두 하위 명령을 등록합니다. 초기화 시 현재 경로 아래 .scratch/play_policy_insights_<uuid>를 만들고 파일 목록, 앱 식별자, target SDK, 권한을 수집합니다.
scanner.collect_target_files()는 파일을 manifests, gradles, source_code, all_files로 분류합니다. 하이브리드 앱의 package.json 또는 pubspec.yaml을 찾으면 부모 디렉토리까지 검사 루트를 넓힐 수 있습니다. 따라서 입력 디렉토리와 실제 읽기 범위를 같은 것으로 단정하면 안 됩니다.
scanner._scan_single_file()는 설정에 있는 패턴을 문자열 포함 검사로 찾습니다. Kotlin 컴파일러나 데이터 흐름 분석기를 호출하지 않습니다. 주석 일부를 제거하고, 발견한 파일 경로와 패턴을 증거 후보로 기록합니다.
# scanner.py의 핵심 조건을 발췌
for cat_name, signals in all_signal_categories.items():
for signal_id, patterns in signals.items():
for p in patterns:
if p in content:
# 파일 경로와 일치 패턴을 저장하고 다음 신호로 이동
...
break
설정 파일 play/play-policy-insights/resources/scanner_config.json의 PRECISE_LOCATION에는 ACCESS_FINE_LOCATION, FusedLocationProviderClient, latitude 같은 문자열이 들어 있습니다. latitude라는 변수명이 등장했다는 사실은 위치 정보가 외부로 전송됐다는 증명이 아닙니다. 이후 분석자가 실제 사용 경로와 전송 여부를 확인해야 합니다.
perform_scan()은 최대 8개 스레드로 파일을 검사하고, 결과를 집합으로 중복 제거한 뒤 정렬합니다. orchestrator.main()에도 최대 2개 스레드의 실행기가 있지만, 스캐너를 백그라운드에 맡기고 주 흐름에서 앱 식별 정보를 추출하는 구조입니다. 이것과 LLM 분석자를 병렬로 실행하는 것은 서로 다른 계층입니다.
| 단계 | 담당 함수·지침 | 역할 |
|---|---|---|
| 설정 해석 | load_gradle_properties(), load_version_catalog(), _resolve_to_int() | 속성·버전 별칭을 숫자로 해석. 순환 참조는 방문 집합으로 차단 |
| 분석 입력 생성 | write_agent_prompts() | 자료를 골라 분석자별 프롬프트·JSON 생성 |
| 판단 | play/play-policy-insights/resources/goal_*.md | 외부 에이전트가 소스 근거를 검토하고 발견 사항 작성 |
| 수집·분할 | run_aggregation() | 발견 사항에 ID를 붙이고 검토자 입력 생성 |
| 재검토 | play/play-policy-insights/resources/critic.md | 확인·수동 검토·오탐 제거 판단 |
| 보고서 | generate_report.aggregate_findings() | 검토 결과와 스토어 선언을 결합해 보고서 데이터 구성 |
write_agent_prompts()는 데이터 유형당 증거를 최대 3개로 제한하고, 데이터 보안 작업을 max_evidence_per_worker = 6 단위로 나눕니다. run_aggregation()은 SUGGESTION을 검토 대상에서 제외하고 나머지를 chunk_size = 3개씩 묶습니다. 작업 크기를 제어해 에이전트가 확인할 근거를 좁히는 방식입니다.
SKILL.md는 분석자와 검토자를 각각 최대 3개까지 병렬 실행하도록 지시합니다. Python 코드가 LLM을 직접 생성하거나 호출하는 것은 아닙니다. 실행 능력이 있는 외부 에이전트가 프롬프트 파일을 읽고 분석을 수행하며, 능력이 없으면 순차 실행 절차를 따릅니다.
스킬 설명은 결정적인 보고서를 강조하지만, 발견 사항과 검토 판정에는 LLM 판단이 들어갑니다. 또한 generate_report.py의 aggregate_findings()는 해당 ID의 검토 판정이 없으면 VERIFIED와 높은 신뢰도를 기본값으로 넣습니다. 누락된 검토 결과를 성공으로 해석하지 않도록 입력 완결성을 별도로 확인해야 합니다.
play/play-policy-insights/scripts/template_engine.py에는 TextNode, VarNode, IfNode, EachNode가 있습니다. compile_template()은 태그를 순서대로 읽고 스택으로 중첩 구조를 구성합니다. render_template()은 만들어진 노드를 순회하며 결과 문자열을 모읍니다.
# resources/goal_data_safety.md의 활성화 조건
{{#IF_ANY data_sources, disclosure}}
{{ACTIVATE_GOAL}}
{{/IF_ANY}}
# 개념을 설명하기 위한 입력 예시
{"data_sources": {"EMAIL": ["Login.kt"]}, "disclosure": []}
# 조건이 참이면 표식이 남음
{{ACTIVATE_GOAL}}
write_agent_prompts() 내부의 _save_goal_instance()는 렌더링 결과에 {{ACTIVATE_GOAL}}이 남아 있는지 확인합니다. 있으면 표식을 제거하고 해당 작업의 프롬프트를 저장합니다. 템플릿 조건이 작업 선택에도 쓰이는 것입니다.
VarNode.render()는 값이 없는 변수 태그를 지우지 않고 원문으로 보존합니다. compile_template()은 닫히지 않은 블록의 여는 태그를 일반 텍스트로 되돌리지만, 내부 변수와 정상적으로 닫힌 하위 블록은 계속 렌더링합니다. 이 관대한 처리가 표식 활용에는 도움이 되지만, 잘못 쓴 변수명과 문법 오류도 조용히 남길 수 있습니다.
출력 대상은 Markdown입니다. VarNode는 값을 str()로 넣으며 HTML 정화를 제공하지 않습니다. 나중에 웹에 보고서를 표시하는 시스템을 만든다면, 표시 단계에서 이를 별도로 처리해야 합니다.
system/glance-canonical-layouts/references/collections/CheckListAppWidget.kt의 provideGlance()는 저장소에서 초기 목록을 읽고, provideContent 안에서 두 Flow를 구독합니다. WidgetContent()는 목록·선택 상태·콜백을 CheckListLayout()에 전달합니다.
// CheckListAppWidget.kt에서 핵심 부분만 발췌
override val sizeMode: SizeMode = SizeMode.Exact
val items by repo.items().collectAsState(initial = initialItems)
val checkedItems by repo.checkedItems().collectAsState(initial = emptyList())
WidgetContent(
items = items,
checkedItems = checkedItems,
checkItemAction = { key: String -> repo.checkItem(key) }
)
가짜 저장소인 system/glance-canonical-layouts/references/collections/data/FakeCheckListDataRepository.kt의 checkItem()은 체크 표시를 먼저 보여 주고, delay(500) 뒤 항목을 제거합니다. 즉시 사라지는 것보다 탭이 처리됐음을 알아보기 쉽게 만든 예제입니다. 실제 백엔드 요청은 없습니다.
이 저장소는 메모리의 MutableStateFlow와 위젯 ID별 맵을 사용하며, 예제에서는 GlobalScope.launch로 작업합니다. 프로세스가 끝난 뒤의 영속성, 실패 복구, 앱 생명주기에 맞춘 취소까지 구현된 제품용 데이터 계층으로 받아들이면 안 됩니다.
업데이트 워크플로는 숨김 디렉토리 아래 스킬을 목록에서 제외합니다. 릴리스 노트의 skills_at()도 같은 기준을 따르고, 경로가 바뀌어도 머리말의 이름이 같으면 이동으로 분류합니다. 파일 위치와 콘텐츠의 정체성을 분리해 다루는 작은 설계 사례입니다.
| 위치 | 담는 것 | 읽는 목적 |
|---|---|---|
SKILL.md | 적용 조건과 작업 단계 | 이 작업에 맞는지 먼저 판단 |
references/ | 세부 가이드·API 예제·샘플 | 현재 분기에서 필요한 구현 확인 |
resources/ | 정책 분류·템플릿·분석자 지침 | 데이터 구조와 판단 기준 확인 |
scripts/ | 반복 작업을 수행하는 실행 코드 | 입력·출력·실패 조건 추적 |
profilers/android-profiler/recording/·profilers/android-profiler/analysis/ | 프로파일러의 하위 작업 절차 | 성능 자료를 수집하는 단계와 해석하는 단계 구별 |
모든 스킬이 위 폴더를 모두 갖추지는 않습니다. 예를 들어 system/edge-to-edge/에는 진입 문서가 있고, 정책 점검에는 실행 도구까지 있습니다. 빈 폴더를 미리 맞춘 프레임워크보다, 작업마다 필요한 부품을 묶은 구성입니다.
커밋 95ea931의 Git 추적 파일 424개를 대상으로 확장자별 내용의 줄바꿈 수를 합산했습니다. 아래 값에는 빈 줄·주석·문서의 코드 블록이 포함됩니다. 복잡도나 직접 작성한 실행 코드의 양을 나타내는 지표는 아닙니다.
| 분류 | 파일 수 | 줄 수 | 해석 |
|---|---|---|---|
| Markdown | 315 | 79,003 | 지침·참조 문서·코드가 들어 있는 문서 |
Kotlin .kt | 45 | 7,905 | Glance 참조 코드 |
Python .py | 5 | 3,131 | Play 정책 점검 도구 |
| XML | 42 | 857 | 위젯 리소스·설정 |
| JSON | 6 | 1,325 | 플러그인·마켓플레이스·검사 설정·정책 |
| YAML | 2 | 321 | GitHub Actions |
| Perfetto 설정 | 7 | 235 | .pftxt 기록 설정 |
| 기타 | 2 | 514 | LICENSE 201줄 + 확장자 없는 Python 래퍼 313줄 |
| 합계 | 424 | 93,291 | 문서와 예제를 모두 포함한 물리적 줄 수 |
SKILL.md 33개의 합은 8,865줄입니다. Markdown 중 *.kt.md라는 이름의 파일은 51개이며, 이 표에서는 Markdown에 포함했습니다. 확장자 없는 Perfetto 래퍼까지 Python으로 보면 관련 내용은 3,444줄입니다. 이처럼 집계 규칙을 밝히면 주 언어 표시와 물리적 줄 수가 다른 이유를 이해할 수 있습니다.
README.md에서 프로젝트의 목적과 설치·활성화 개념을 파악합니다..claude-plugin/plugin.json에서 전체 스킬 목록을 읽습니다.SKILL.md 하나를 골라 입력 조건과 검증 단계를 표시합니다.가장 좋은 학습 단위는 라이브러리 이름 하나가 아니라, 입력 하나가 어떤 판단과 출력으로 바뀌는지 설명할 수 있는 작은 흐름입니다. 다음 기술들은 서로 다른 출발점을 제공하지만, 모두 파일·함수·검증 기준으로 연결됩니다.
build-system/agp/agp-9-upgrade/SKILL.md는 대상인 비KMP Android 프로젝트, 현재 버전 확인, 단계별 변경, 종료 검증을 나눕니다. 좋은 지침에는 “무엇을 하라”뿐 아니라 “언제 시작할 수 있고 어떤 결과면 끝나는가”가 있어야 한다는 점을 배울 수 있습니다.
실습 아이디어: “위젯을 추가한다”를 입력 조건·참조 파일·변경 대상·완료 조건 네 칸으로 분해해 보세요. “잘 보이는지 확인”보다 “위젯 선택기에 나타나고, 두 크기에서 제목이 잘리지 않는다”가 더 검증하기 좋은 문장입니다.
scanner.collect_target_files()의 dirs[:] 갱신은 os.walk()가 내려갈 디렉토리를 줄입니다. 설정의 ignored_directories에는 .git, build, node_modules, tests, ios 등이 있습니다. 검사를 빠르게 하고 잡음을 줄이는 선택이지만, 제외 경로의 코드도 분석됐다고 말해서는 안 됩니다.
_scan_single_file()는 C 계열 주석과 XML 주석을 일부 제거합니다. 그러나 정규식으로 문자열·주석을 다루는 방식은 언어 전체의 구문 분석과 다릅니다. 동적으로 생성한 호출, 간접 참조, 특이한 문자열 문법을 완전히 이해한다고 볼 수 없습니다.
실습 아이디어: 같은 latitude가 실제 변수, 주석, 문자열에 등장하는 세 경우를 적고 제거·검출 과정을 손으로 추적하세요. 검출 결과를 “증거 후보”와 “실제 동작의 증명”으로 구별해 적으면 더 좋습니다.
orchestrator._resolve_to_int()는 libs.versions. 같은 접두사를 제거하고, 사용자 정의 값·properties·버전 카탈로그를 찾아 재귀적으로 숫자를 구합니다. visited 집합은 같은 이름을 다시 만났을 때 None을 반환하도록 합니다.
# 실제 함수를 이해하기 위한 가상 설정
입력: libs.versions.targetSdk.get()
catalog: targetSdk = androidApi
properties: androidApi = 35
예상 추적: 접두사·호출 제거 → targetSdk → androidApi → 35
순환 예: targetSdk → androidApi → targetSdk
예상 결과: 방문한 이름을 다시 만나면 None
실습 아이디어: 중간 별칭 하나가 사라진 경우와 순환한 경우를 구별해 기록해 보세요. 둘 다 숫자를 못 얻더라도, 사용자에게 알려 줄 원인은 다릅니다. 정규식 기반 휴리스틱이 실제 Gradle 평가 결과를 대신할 수 없다는 점도 함께 기억합니다.
FakeCheckListDataRepository.items()와 checkedItems()는 내부의 MutableStateFlow를 Flow<List<...>> 형태로 노출합니다. 화면은 변경 가능한 저장소 자체보다 관찰할 흐름을 받습니다. CheckListLayout()는 상태와 onCheck 콜백을 매개변수로 받으므로 표시 로직과 저장 로직의 경계를 찾기 쉽습니다.
실습 아이디어: 항목 하나를 체크한 순간, 250ms 뒤, 500ms 지연 이후의 items와 checkedItems를 표로 적으세요. 네트워크 실패를 추가한다면 어느 상태를 되돌릴지, 앱이 종료되면 무엇을 저장해야 할지도 설계해 볼 수 있습니다.
jetpack-compose/jetpack-compose-performance/references/state-and-stability.md는 Composition·Layout·Draw 단계를 구별합니다. 스크롤처럼 자주 바뀌는 값을 화면 구성 시점에 읽는 대신, 위치나 그리기를 계산하는 단계에서 읽도록 지연하는 예제가 핵심입니다.
// 참조 문서의 위치 계산 예제
// 구성 단계에서 상태 읽기
val offset = scrollState.value
Box(modifier = Modifier.offset(x = offset.dp, y = 0.dp))
// 위치 계산 람다 안에서 상태 읽기
Box(modifier = Modifier.offset { IntOffset(scrollState.value, 0) })
이 예제는 읽기 시점을 설명합니다. 앞 코드는 dp, 뒤 코드는 픽셀 기반 IntOffset이므로 실제 변경에서는 좌표 단위도 확인해야 합니다. 모든 매개변수를 무조건 람다로 감싸면 빨라진다는 뜻도 아닙니다. 참조 문서는 구성 단계에서 바로 쓸 값에는 직접 값을 전달하라고 설명합니다.
실습 아이디어: 상태 읽기를 빨간색, 상태 쓰기를 파란색으로 표시한 종이 흐름도를 그려 보세요. 레이아웃에서 쓴 값을 구성 단계가 다시 읽는 구조가 있는지 찾으면 재실행 고리를 이해하기 쉬워집니다.
template_engine.compile_template()은 중첩 블록을 스택으로 처리하고, EachNode는 반복마다 컨텍스트를 복사해 ITEM 또는 KEY·VALUE를 넣습니다. 컴파일과 렌더링을 분리한 구조는 설정 파일 처리나 간단한 DSL에도 응용할 수 있습니다.
실습 아이디어: 없는 변수, 닫는 태그 누락, 잘못된 닫는 태그 세 가지를 구별하고 현재 구현이 어떤 문자열을 보존하는지 예상하세요. “예외가 안 났다”와 “의도한 출력이 나왔다”는 다른 완료 조건입니다.
update-skills.yml은 스킬 경로를 정렬·중복 제거하고 양쪽 매니페스트를 갱신합니다. 마켓플레이스 항목에서 skills·strict 같은 오래된 필드를 제거하는 코드도 있습니다. 플러그인 목록의 역할과 실제 컴포넌트 정의의 역할을 구별하는 호환성 작업입니다.
실습 아이디어: 스킬이 하나 추가될 때 변해야 할 파일과, 버전만 올릴 때 변해야 할 파일을 나누세요. “본문은 최신인데 설치 매니페스트에는 빠졌다” 같은 콘텐츠 배포 오류를 예방하는 체크리스트를 만들 수 있습니다.
스킬을 읽는 데 필요한 환경과, 스킬이 안내하는 Android 기능을 실행하는 환경은 별개입니다. 저장소 공통의 CPU·RAM·GPU 최소 사양은 선언되어 있지 않습니다. 다음 표는 소스에 있는 요구와 학습용 판단을 구분합니다.
| 하려는 일 | 필요한 환경 | 근거·주의점 |
|---|---|---|
| 문서·코드 읽기 | 텍스트 편집기, 선택적으로 Git·검색 도구 | SDK·GPU·에뮬레이터 없이 구조 학습 가능 |
| HTML 자료 보기 | 일반 웹 브라우저 | 본문은 정적 HTML. 지정 폰트 로딩에는 네트워크 필요 |
| 스킬 활성화 | 해당 형식을 지원하는 외부 에이전트 | 모델 계정·도구 접근·컨텍스트 조건은 호스트 환경에 따름 |
| 정책 점검 도구 | Python 3 실행 환경, 읽을 Android 앱 소스 | 최소 Python 부버전 미고정. 보고 단계는 스토어 네트워크 요청 가능 |
| Android 앱 빌드 | 대상 앱과 호환되는 JDK·Gradle·Android SDK | AGP 9.0 참조 표는 JDK 17·Gradle 9.1.0. 다른 AGP는 별도 확인 |
| 위젯 확인 | 위젯 호스트가 있는 Android 기기 또는 에뮬레이터 | Gradle·Manifest·리소스를 갖춘 별도 앱 프로젝트가 필요 |
| LiteRT-LM 추론 | 호환 Android 기기와 모델 파일 공간 | 문서는 수백 MB~수 GB 모델을 APK 밖에 둘 것을 안내 |
| Perfetto 분석 | Python 래퍼·호환 OS/CPU·트레이스 | 첫 실행 시 도구 다운로드와 홈 디렉토리 캐시 쓰기 발생 |
학습을 문서와 손 추적으로 시작한다면 별도의 고성능 장비를 전제할 필요가 없습니다. 반대로 에뮬레이터, IDE, 앱 빌드, 모델 추론을 한꺼번에 사용하면 요구량이 크게 늘어납니다. 저장소에는 이를 하나의 RAM 숫자로 정리한 공식 사양이나 성능 측정이 없습니다.
LiteRT-LM 스킬의 minSdk = 24는 앱 패키지의 최소 API 조건입니다. 모든 API 24 기기에서 선택한 모델이 충분한 속도와 메모리로 실행된다는 보장은 아닙니다. ML Kit Prompt API도 API 26 조건에 더해 모델의 FeatureStatus와 기능 지원 여부를 확인하도록 안내합니다.
정책 도구는 현재 경로 아래 .scratch/에 중간 결과를 저장합니다. 하지만 Perfetto 래퍼의 download_or_get_cached()는 ~/.local/share/perfetto/prebuilts/를 사용합니다. 모든 보조 도구가 같은 디렉토리 경계를 지킨다고 가정하면 안 됩니다.
play_store_scraper.fetch_html()은 기본적으로 인증서 검증을 사용하며 요청 제한 시간은 10초입니다. 스토어 상세 페이지를 못 읽으면 scrape_app_details()의 is_published는 거짓이 됩니다. 네트워크 오류를 “앱이 실제로 미출시 상태”라고 해석하지 않도록 원인 구분이 필요합니다.
요리책을 읽는 데는 책상만 있으면 되지만, 오븐 요리를 만들 때는 오븐이 필요합니다. 온디바이스 AI 스킬이 있다고 해서, 이 저장소를 읽는 모든 사람이 GPU나 대형 모델을 준비해야 하는 것은 아닙니다.
각 과제는 “어떤 결과를 만들었는가”뿐 아니라 “왜 그 결과가 나오는가”를 소스 근거로 설명해야 끝납니다. 아래 과제는 저장소 코드 실행 없이도 진행할 수 있습니다. 명령 예시는 저장소 루트에서 파일을 읽고 검색하는 용도입니다.
목표: agp-9-upgrade의 시작 조건과 종료 조건을 정리합니다. SKILL.md를 읽고 대상·제외 대상·의존성 조건·변경 단계·검증 명령을 다섯 칸에 적습니다.
산출물: KMP 여부와 현재 AGP 버전으로 분기하는 작은 결정 트리, KSP·Hilt 최소 버전 표, 완료 조건 3개입니다.
완료 기준: “현재 AGP가 8이고 KMP는 아니다”라는 입력에 첫 행동을 설명하고, 문서가 허용하는 사용자 예외 조건도 찾습니다. IDE 동기화·help·build --dry-run을 실제 APK 테스트와 구별할 수 있어야 합니다.
# 문서에서 시작 조건과 검증 절 찾기
rg -n 'Requirements|KMP|Verification|KSP|Hilt' \
build-system/agp/agp-9-upgrade/SKILL.md
목표: scanner.py와 scanner_config.json을 읽고 latitude, ACCESS_FINE_LOCATION, getCurrentPosition을 찾아봅니다.
산출물: 실제 코드의 변수명, 주석 안의 단어, 큰따옴표 문자열, 네트워크 전송 코드가 함께 있는 경우를 가상 입력으로 적은 네 줄짜리 결과 표입니다.
완료 기준: 주석 제거 전후를 적고, “위치 신호를 검출했다”와 “개인정보 전송을 입증했다”가 다른 이유를 설명합니다. 문자열 검출만으로 전송 여부를 결정하지 않습니다.
// 손으로 검사할 가상의 Kotlin 조각
val latitude = 0.0
// latitude라는 단어만 있는 주석
val message = "latitude"
// 첫째·셋째 줄이 문자열 검사에 남더라도,
// 이 조각에는 외부로 값을 보내는 동작이 없습니다.
목표: compile_template()과 EachNode.render()를 따라 아래 예제의 노드 구조와 최종 문자열을 예상합니다.
산출물: IfNode 안에 TextNode·EachNode가 있는 트리, 정상 입력의 결과, 목록이 비었을 때의 결과, 닫는 태그가 누락됐을 때의 결과입니다.
완료 기준: 반복의 컨텍스트에서 ITEM이 어디서 생기는지, 정의되지 않은 {{MISSING}}이 왜 남는지 소스 함수로 설명합니다.
# 가상 템플릿
{{#IF items}}목록
{{#EACH items}}- {{ITEM}}{{/EACH}}
{{/IF}}
{{MISSING}}
# 입력: {"items": ["권한 확인", "검증 결과 기록"]}
# 예상 핵심 출력: 두 항목과 원문 그대로 남은 {{MISSING}}
목표: CheckListAppWidget.provideGlance()에서 FakeCheckListDataRepository.checkItem()까지 연결합니다.
산출물: 체크 전·체크 직후·500ms 지연 이후의 목록과 체크 키, UI에 보일 상태를 정리한 표입니다. 초기 목록에 키 "0"·"1" 두 개만 있다고 가정합니다.
완료 기준: 키 "0"이 체크된 채 잠시 남았다가 제거되는 이유를 설명합니다. 프로세스 종료와 작업 실패에 대응하려면 영속 저장·작업 취소·재시도 중 무엇을 추가할지 근거를 적습니다.
| 관찰 시점 | items의 키 | checkedItems | 해석 |
|---|---|---|---|
| 체크 전 | 0, 1 | 빈 목록 | 두 항목 표시 |
| 첫 상태 변경 후 | 0, 1 | 0 | 0은 처리 중임을 체크로 표시 |
| 지연 후 변경 완료 | 1 | 빈 목록 | 0은 목록에서 제거됨 |
이 표는 함수의 순차 상태 변경을 단순화한 것입니다. 스레드 스케줄링과 Flow 수집 때문에 실제 화면 프레임이 정확히 어느 밀리초에 바뀌는지까지 보장하는 시간표는 아닙니다.
목표: run_aggregation()과 generate_report.aggregate_findings()의 연결을 읽고, 중요한 발견 사항에 검토 결과가 없을 때 어떤 상태여야 할지 설계합니다.
산출물: 필수 검토 대상 ID 목록, 실제 검토 ID 목록, 둘의 차이를 확인하는 의사 코드, 누락 시 보고서 행동을 적은 설계 문서입니다. 현재 저장소를 수정하지 않아도 됩니다.
완료 기준: 의도적으로 검토를 생략한 SUGGESTION과, 검토 결과가 사라진 CRITICAL을 구별합니다. 후자는 자동 확인 대신 MANUAL_REVIEW 또는 명시적인 미완료 상태로 보내는 방안을 제시합니다.
# 개선 아이디어를 나타내는 의사 코드 — 현재 구현이 아님
required_ids = 검토가 필요한 발견 사항의 ID 집합
received_ids = 유효한 검토 결과의 ID 집합
missing_ids = required_ids - received_ids
if missing_ids:
보고서 상태 = "검토 미완료"
누락된 ID를 결과에 기록
else:
검토 결과를 반영해 보고서 조립
추가 점검 사례로 빈 결과, 깨진 JSON, 중복 ID, 스토어 요청 실패를 넣어 보세요. 기능을 더하는 설계보다, “입력이 빠져도 성공처럼 보이지 않게 한다”는 조건을 명확히 만드는 것이 핵심입니다.
학습 순서는 Android 기술 이름을 모두 외우는 순서보다, 조건을 읽고 상태를 추적하고 근거를 검증하는 순서가 효과적입니다. 주당 3~5시간을 가정한 학습 제안이며, 사전 경험에 따라 한 주를 두 주로 나누어도 됩니다.
| 주차 | 주제·읽을 곳 | 이번 주의 산출물 |
|---|---|---|
| 1주차 | Git 탐색·Markdown·YAMLREADME.md, 플러그인 매니페스트, AGP 스킬 | 33개 스킬 지도와 스킬 1개의 사전·사후 조건표 |
| 2주차 | Python 자료구조·파일 탐색·정규식scanner.py, scanner_config.json | 파일 분류표와 검출·오탐·누락 사례 5개 |
| 3주차 | 파서·스택·트리·템플릿template_engine.py, goal_data_safety.md | 중첩 템플릿의 AST와 정상·오류 입력별 예상 결과 |
| 4주차 | Kotlin 함수·코루틴·Flow·상태 전달 체크리스트 위젯의 제공자·레이아웃·가짜 저장소 | 이벤트에서 화면까지의 흐름도와 시간별 상태표 |
| 5주차 | Android 빌드·버전 호환성·성능 AGP 스킬, Compose 성능 참조, R8 스킬 | 도구별 버전 구분표와 분석 경로 결정 트리 |
| 6주차 | 에이전트 작업 분할·결과 검토·콘텐츠 배포orchestrator.py, generate_report.py, 워크플로 2개 | 누락 처리 개선 설계와 팀용 스킬 초안 1개 |
4주차 전에 Kotlin의 data class, 람다, nullable 타입, suspend를 익히면 코드를 읽기가 수월합니다. Android에서는 앱 모듈·Manifest·리소스 ID·minSdk·targetSdk·compileSdk의 차이를 먼저 정리하세요. 세 SDK 값은 모두 API 숫자이지만 목적이 다릅니다.
Compose는 상태를 입력으로 화면을 기술하는 방식을, Glance는 홈 화면 위젯이라는 별도 제약을 가진 UI 방식을 이해하는 데 초점을 둡니다. 이름과 문법이 비슷하다는 이유로 앱 화면의 모든 Compose 기능을 Glance에서도 사용할 수 있다고 가정하지 않습니다.
온디바이스 AI 경로는 device-ai/litert/litert-lm/SKILL.md를 시작점으로 삼을 수 있습니다. 이 스킬은 단일 스레드 실행기 하나가 Engine과 Conversation을 소유하게 하고, 생성·스트리밍·취소·종료를 같은 실행기에 모으도록 안내합니다. UI 기능보다 자원 소유권을 먼저 설계하는 사례입니다.
“우리 앱의 테스트 설정 확인”처럼 범위를 좁힌 스킬 초안을 작성해 보세요. 선택 조건, 읽을 파일, 허용할 변경, 결과물 형식, 실패 시 행동, 완료 검증을 적습니다. 다른 사람이 문서만 보고 같은 확인 순서를 재현할 수 있으면 좋은 출발입니다.
| 용어 | 쉬운 설명 |
|---|---|
| Agent Skills | 에이전트가 특정 작업을 수행할 때 읽는 지침·자료 묶음의 형식입니다. |
SKILL.md | 스킬의 이름·설명·적용 조건·절차를 담은 진입 문서입니다. |
| Frontmatter | 문서 맨 위 --- 사이에 적는 구조화 메타데이터입니다. |
| 호스트 에이전트 | 스킬을 읽고 실제 파일 탐색·편집·도구 실행을 담당하는 외부 프로그램입니다. |
| 컨텍스트 | 모델이 현재 답변이나 작업을 위해 참고하는 입력 내용입니다. |
| 플러그인 매니페스트 | 이름·버전·포함된 스킬 경로 등 설치할 구성요소를 선언한 파일입니다. |
| 마켓플레이스 | 플러그인을 어디서 찾을지 알려 주는 목록입니다. 실행 코드 자체와는 역할이 다릅니다. |
| AGP | Android Gradle Plugin. Gradle에 Android 앱 빌드 작업을 추가하는 도구입니다. |
| Gradle·DSL | Gradle은 빌드 실행 도구, DSL은 빌드 설정을 기술하는 문법입니다. |
| KGP·내장 Kotlin | KGP는 Kotlin Gradle plugin입니다. AGP 9의 내장 Kotlin 전환은 Kotlin 빌드 설정의 소유 방식 변화와 관련됩니다. |
| KSP·kapt | 코드의 선언·어노테이션을 처리해 추가 코드를 만드는 도구들입니다. 대상 라이브러리의 지원 방식이 중요합니다. |
| KMP | Kotlin Multiplatform. 여러 플랫폼에서 코드를 공유하는 프로젝트 방식입니다. 이 AGP 스킬의 대상에서는 제외됩니다. |
| R8·keep 규칙 | R8은 코드 축소·최적화를 수행하고, keep 규칙은 제거·이름 변경 등에서 보존할 대상을 지정합니다. |
| ANR | 앱이 응답하지 않는 상태입니다. 진단에는 메인 스레드와 대기 원인의 근거가 필요합니다. |
| Glance | 홈 화면 위젯 UI를 Kotlin 선언형 방식으로 구성하는 Jetpack 기술입니다. |
| Composable | 입력 상태를 바탕으로 UI를 기술하는 함수입니다. 실행 환경에 맞는 UI API를 써야 합니다. |
| Flow·StateFlow | 시간에 따라 바뀌는 값을 관찰하는 흐름입니다. StateFlow는 현재 상태를 갖는 형태입니다. |
코루틴·suspend | 작업을 중단·재개할 수 있게 구성하는 수단입니다. suspend만 붙인다고 자동으로 백그라운드 스레드가 되지는 않습니다. |
| AST·인터프리터 | 문법을 트리로 표현한 것이 AST이고, 그 구조를 따라 의미를 수행하는 것이 인터프리터입니다. |
| 휴리스틱 | 모든 경우를 정확히 계산하기보다, 규칙과 단서로 유용한 후보를 찾는 방법입니다. |
| 오탐·누락 | 문제가 없는데 있다고 한 경우와, 실제 문제가 있는데 찾지 못한 경우입니다. |
| Critic | 분석자가 낸 결론을 실제 소스와 다시 대조하는 검토 역할입니다. |
| 결정적 처리 | 같은 입력과 조건에서 같은 결과를 내는 처리입니다. 정렬된 보고서와 LLM 판단의 재현성은 구별해야 합니다. |
| 청크 | 자료를 처리 가능한 크기로 나눈 조각입니다. 이 도구는 증거 6개·검토 사항 3개 등의 단위를 씁니다. |
| Perfetto | 스케줄링·CPU·메모리·프레임 같은 실행 흔적을 기록하고 분석하는 도구입니다. |
| SHA-256 | 파일 내용의 지문을 만드는 해시입니다. Perfetto 래퍼는 내려받은 파일이 예상 내용인지 비교합니다. |
| API 수준·SDK 값 | minSdk는 설치 가능한 최소 수준, compileSdk는 컴파일 API 범위, targetSdk는 대상 플랫폼 동작과 연결됩니다. |
| 항목 | 상태 |
|---|---|
| 선정 위치 | GitHub Trending Weekly #4 |
| 스타 / 포크 | 9,303 / 594 — 2026-10-23 GitHub 기준 |
| 생성일 | 2026-03-16 |
| GitHub 주 언어 | Kotlin |
| 최신 릴리스 | v1.0.16 · 2026-10-21 |
| 최근 push | 2026-10-21 |
| 최근 커밋 | 95ea931 · 2026-10-21 · Bump plugin version to v1.0.16 |
| 스킬 규모 | 18개 분야 · 33개 SKILL.md · 진입 문서 합 8,865줄 |
| 전체 파일 규모 | 추적 파일 424개 · 물리적 줄 수 93,291줄 |
| 주요 내용 규모 | Markdown 79,003줄 · Kotlin 7,905줄 · Python 확장자 파일 3,131줄 |
| 라이선스 | Apache-2.0 |
| 기여 방식 | README 기준 issue 피드백·요청 가능, 외부 코드 기여는 현재 받지 않음 |
GitHub 숫자와 릴리스 정보는 날짜가 붙은 스냅샷입니다. 파일 수·줄 수는 커밋 95ea931의 내용 기준이며, 빈 줄·주석·문서 예제를 포함합니다. 전체 이력의 커밋 수나 기여자 수는 이 자료의 규모 지표에 포함하지 않았습니다.
| 문서·기대 | 이 커밋에서 확인되는 차이 |
|---|---|
| 순차 정책 집계 명령 | play/play-policy-insights/SKILL.md의 Mode B는 --aggregate를 안내합니다. 실제 orchestrator.main()은 옵션이 아닌 aggregate 하위 명령을 등록합니다. |
| R8 정량 분석 보조 도구 | performance/r8-analyzer/SKILL.md의 Path A는 설치 경로인 .agents/skills/r8-analyzer/scripts/convert_pb_to_json.py와 .agents/skills/r8-analyzer/scripts/analyze.py 실행을 요구하지만, 해당 스크립트는 이 커밋에 없습니다. |
| 검토를 거친 결과 | generate_report.aggregate_findings()는 검토 판정이 없으면 자동 확인 기본값을 적용합니다. 입력 검증 없이 “모두 재검토됐다”고 볼 수 없습니다. |
| 공식 지침 기반 정확도 | README는 모델이 약한 영역을 평가에 따라 선정한다고 설명합니다. 이 커밋에는 정확도 개선 폭을 재현할 평가 묶음이 없습니다. |
이 차이들은 전체 프로젝트를 무효로 만들기보다, 스킬도 일반 코드처럼 버전과 입력·출력 계약을 검토해야 함을 보여 줍니다. 특히 명령 이름, 필요한 파일의 존재, 실패 시 상태는 문장만 읽을 때 놓치기 쉽습니다.
분석 근거는 커밋 95ea931의 문서·함수·설정 연결입니다. 앱 빌드 성공률, 실제 기기 동작, 정책 판단 정확도, 모델별 비용·성능을 측정한 결과는 아닙니다. 스킬의 라이브러리 예시 버전도 현재 앱의 의존성 그래프와 대조해야 합니다.
문자열 스캐너는 런타임 데이터 흐름을 입증하지 못하며, 스토어 HTML과 네트워크 상태에 따라 비교 자료가 불완전할 수 있습니다. 정책 보고서를 공식 심사 승인이나 완전한 검증 결과로 받아들이면 안 됩니다.
위젯의 가짜 저장소는 실제 서버·영속 저장·실패 복구를 구현하지 않습니다. Perfetto 래퍼처럼 다운로드와 홈 경로 쓰기를 수행하는 도구도 있으므로, 실제 적용 전에는 대상 환경의 실행·파일 접근 범위를 확인해야 합니다.