인셔셔RSS 관심 있는 블로그, 뉴스, 기술 정보를 효율적으로 추적하고 읽으세요
원문 읽기 InertiaRSS에서 열기

추천 피드

博客园 - 司徒正美
V
V2EX
T
Tailwind CSS Blog
有赞技术团队
有赞技术团队
aimingoo的专栏
aimingoo的专栏
Apple Machine Learning Research
Apple Machine Learning Research
IT之家
IT之家
Blog — PlanetScale
Blog — PlanetScale
A
About on SuperTechFans
月光博客
月光博客
T
The Blog of Author Tim Ferriss
宝玉的分享
宝玉的分享
Martin Fowler
Martin Fowler
博客园 - 聂微东
The GitHub Blog
The GitHub Blog
V
Visual Studio Blog
WordPress大学
WordPress大学
酷 壳 – CoolShell
酷 壳 – CoolShell
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python)
구글 I/O 2026에서 발표된 수면 발표는 앱에 대한 우리의 생각을 바꿔줄 것입니다
Vrushali · 2026-05-24 · via DEV Community

이것은 Google I/O Writing Challenge


에 대한 제출입니다. 모두 Google I/O 2026에서 Compose First, Gemini 통합 및 Android 17의 적응형 레이아웃 강화에 대해 이야기했습니다. 공정합니다 — 그것들은 큽니다. 하지만 제가 멈출 수 없었던 발표는 거의 헤드라인을 받지 못했습니다: AppFunctions API.

안드로이드 앱을 10년 동안 만들었습니다. API들이 왔다 갔다 했습니다. 이것은 오늘 무엇을 하는지 때문이 아니라, 앱들이 어떻게 변화하고 있는지에 대한 신호 때문에 다릅니다..


AppFunctions이 정확히 무엇인지

AppFunctions의 핵심은 앱이 AI 보조 프로그램에 독립적이고 명명된 작업을 직접 노출하게 해줍니다. 생각해보세요:

  • "Create expense report"
  • "Book appointment"
  • "Send daily update"

사용자가 앱을 실행하거나 화면으로 이동하거나 흐름을 탭하는 대신, Gemini와 같은 에이전트가 대신 사용자가 액션을 직접 호출할 수 있습니다. 앱이 전혀 포그라운드로 나타나지 않아도 됩니다.

AppFunction을 등록하는 방법에 대한 개략적인 아이디어는 다음과 같습니다.

@AppFunction
suspend fun createExpenseReport(
    context: AppFunctionContext,
    params: CreateExpenseParams
): ExpenseResult {
    // your business logic here
    return ExpenseResult(id = repo.create(params))
}

전체 화면 모드로 입력 전체 화면 모드 종료

함수를 정의합니다. 매개변수를 정의합니다. AI는 함수를 언제와 어떻게 호출할지 결정합니다.


AppFunctions 통합 방법: 단계별 예제

구체적으로 만들어 보겠습니다. 0에서 Gemini-callable로 전체 통합을 보여드리겠습니다.

단계 1 — 의존성 추가

// build.gradle.kts (module)
dependencies {
    implementation("androidx.appfunctions:appfunctions:1.0.0-alpha01")
    ksp("androidx.appfunctions:appfunctions-compiler:1.0.0-alpha01")
}

전체 화면 모드로 입력 전체 화면 모드 종료

단계 2 — 입력/출력 타입 정의

AppFunctions은 타입화된 Kotlin 데이터 클래스를 사용합니다. 그들을 직렬화되도록 유지하십시오 — AI 에이전트는 자연어로부터 그들을 구성해야 합니다.

@Serializable
data class CreateExpenseParams(
    val title: String,
    val amount: Double,
    val category: String? = null  // nullable — agent may not always provide this
)

@Serializable
data class ExpenseResult(
    val id: String,
    val success: Boolean,
    val message: String
)

전체 화면 모드 입력 전체 화면 모드 종료

단계 3 — 함수에 주석 달기

사용@AppFunction 스스펜드 함수에서 작동합니다. 애너테이션 프로세서는 컴파일 시간에 등록 스키마를 생성합니다 — 불필요한 XML이 없습니다.

class ExpenseAppFunctions(
    private val repo: ExpenseRepository
) {

    @AppFunction
    suspend fun createExpenseReport(
        context: AppFunctionContext,
        params: CreateExpenseParams
    ): ExpenseResult {
        val id = repo.create(params)
        return ExpenseResult(
            id = id,
            success = true,
            message = "Expense created: ${params.title}"
        )
    }
}

전체 화면 모드로 전환 전체 화면 모드 종료

단계 4 — 에이전트 안전하게 만들기 (모두 건너뛰는 단계)

에이전트는 네트워크 실패 시 재시도할 수 있습니다. 무차별성이 없으면 한 사용자의 음성 명령이 중복 기록을 생성할 수 있습니다.

@AppFunction
suspend fun createExpenseReport(
    context: AppFunctionContext,
    params: CreateExpenseParams
): ExpenseResult {

    // Idempotency: return existing record if already created
    val existing = repo.findByTitle(params.title)
    if (existing != null) {
        return ExpenseResult(
            id = existing.id,
            success = true,
            message = "Already exists"
        )
    }
    return ExpenseResult(id = repo.create(params), success = true, message = "Created")
}

전체 화면 모드로 전환 전체 화면 모드 종료

참고: 더 세분적인 제어를 원하시면 AppFunctionContext.callerPackageName를 사용하여 각 에이전트별로 idempotency 키를 범위 지정하세요.

단계 5 — AndroidManifest에 등록

이 없으면 Gemini가 런타임에 함수를 발견할 수 없습니다.

<!-- AndroidManifest.xml -->
<service
    android:name=".ExpenseAppFunctions"
    android:exported="true"
    android:permission="android.permission.BIND_APP_FUNCTION_SERVICE">

    <intent-filter>
        <action android:name="androidx.appfunctions.AppFunctionService"/>
    </intent-filter>

</service>

전체 화면 모드로 전환 전체 화면 모드 종료

6단계 — Gemini가 이를 어떻게 처리하는지

등록 후, 사용자가 "£45 택시를 비용에 추가"라고 입력하면 이것이 배경에서 트리거됩니다 — UI 없이, 네비게이션 없이, 클릭 없이:

// Gemini constructs and executes this on-device
val result = appFunctionManager.executeAppFunction(
    targetPackage = "com.yourapp",
    functionId = "createExpenseReport",
    params = CreateExpenseParams(
        title = "Taxi",
        amount = 45.0,
        category = "Transport"
    )
)
// result.message → "Expense created: Taxi"

전체 화면 모드 입력 전체 화면 모드 종료

그것이 전체 루프입니다. 의존성에서 에이전트 호출 가능한 작업까지의 여섯 단계 — 그리고 실제로는 3단계와 4단계에 더 많은 작업이 들어가는 것이지, 1단계와


에 들어가는 것이 아닙니다.

이러한 정신적 전환에 대해 직접 말씀드리겠습니다.

지난 십 년 동안 안드로이드 앱들은 수동적인 도구들로 되어 왔습니다. 사용자가 그것들을 엽니다, 그것들과 상호작용하며, 그것들을 닫습니다. 앱은 기다립니다. 사용자가 다시 돌아옵니다. 그것이 모델입니다.

AppFunctions는 그 모델을 전혀 깨뜨립니다.

참고: 앱은 더 이상 UI가 아닙니다. 외부 지능에 의해 호출될 수 있는 기능의 집합입니다 - 사용자를 대신하여 여러 앱에 걸쳐 작동하며, 명시적인 네비게이션이 필요 없습니다.

그것은 점진적 업데이트가 아닙니다. 그것은 다른 패러다임입니다.

우리가 영원히 유지해 온 UX 가정에 대해 생각해 보세요:

  • 사용자를 안내하기 위해 설계된 온보딩 흐름? 에이전트가 그들을 건너뛰면 덜 관련성이 있습니다.
  • 이동 아키텍처? 여전히 중요하지만, 더 이상의 유일한 진입점은 아닙니다.
  • 상태 관리? 이제 UI 생애주기 외부에서 트리거된 액션을 고려해야 합니다.

아무도 이야기하지 않는 부분: 개발자에게 요구되는 것은 무엇인가

여기서 대부분의 커버리지가 공을 놓쳤다고 생각합니다.

모두 AppFunctions를 앱에 "추가"할 특징으로 프레임링합니다. 몇 가지 주석을 뿌리고 몇 가지 액션을 노출하면 끝. 하지만 그 프레임링은 아키텍처적 함의를 놓칩니다.

앱 로직은 에이전트 친화적이어야 합니다.

그 의미는 다음과 같습니다:

  • 단일 작업의 효과가 중요합니다.대리인이 전화할 수도 있습니다.createExpenseReport네트워크 이상이 발생하면 두 번씩 처리해야 합니다. 그런 상황을 처리해 보셨습니까?
  • 오류 메시지는 기계가 읽을 수 있어야 합니다.만약 함수가 실패하면, 에이전트는 복구하거나 보고하기 위해 구조화된 정보가 필요하며, 토스트는 필요하지 않습니다.
  • 허가 및 인증은 명시적으로 되어야 합니다. 사용자를 대신하는 에이전트는 여전히 범위를 존중해야 합니다. 당신이 주변 사용자 인증을 가정할 수는 없습니다.

이것은 "기능을 추가하는 것"보다 더 "서비스 레이어를 재고하는 것"입니다.


제 솔직한 의견: 흥미롭지만 준비되지 않았습니다

진실을 말하자면 — 광범위하게 말해 안드로이드 개발자 커뮤니티는 이 전환에 준비되어 있지 않습니다.

Compose로의 이전 중에 있습니다. 많은 팀들이 여전히 MVVM을 스파게티에서 분리하고 있습니다. 그리고 지금 우리에게 요청되는 것은 앱을 에이전트 호환 가능한 API로 생각하는 것입니까?

참고: "Google이 AppFunctions을 발표했다"와 "대부분의 생산 앱이 잘 설계된 AppFunctions을 노출한다" 사이의 격차는 월이 아닌 년으로 측정될 것입니다. 그리고 그럴 수 있지만—만약 우리가 아키텍처에 대해 생각하기 시작한다면지금.

이 곡선 앞서가는 개발자들은 오늘부터 비즈니스 로직을 최상위 API로 취급하기 시작하는 사람들입니다 — 깨끗한 사용 사례, 명확한 입력/출력 계약, 적절한 오류 처리. 아직 에이전트가 호출하기 때문이 아니라, 좋은 아키텍처가 미래에 이익을 돌려준다.


내 팀에게 내일 말할 일

내가 내일 I/O 2026에서 하나의 핵심 요약을 가지고 스탠업에 들어간다면, 그것은 이것이겠지:

지금 당장 당신의 사용 사례를 대리인 준비 상태를 위해 검토를 시작하십시오.

다음 스프린트에 AppFunctions을 출시하기 위해가 아니라, 질문을 하십시오: 만약 AI가 키보드에 사람이 없이 이 작업을 호출해야 한다면, 우리의 코드는 잘 작동할까요? 오류 상태는 의미가 있을까요? 인증 모델은 작동할까요?

그 질문만으로도 AppFunctions와 상관없이 해결해야 할 많은 아키텍처 부채가 드러납니다.


프라이버시에 대해 한마디 — 누군가는 말해야 합니다

AppFunctions에 대해 기대가 많지만, 이것이 열어내는 프라이버시 문제를 지적하지 않으면 당신에게 손해를 입힐 것입니다.

агент이 사용자가 명시적으로 탭하지 않고 앱을 호출할 수 있을 때, 동의 모델이 흐릿해집니다. 깊이 생각해야 할 몇 가지 사항:

  • 에이전트는 조용히 작동합니다. 사용자는 한 번 "택시 비용 추가"라고 말했을 때 — 그들은 자신의 기기에서 남은 데이터를 검토하지 않았거나 어떤 시스템이 조작되었는지 확인하지 않았습니다. 당신의 함수는 정확히 무엇을 말하는지만 해야 합니다.
  • 민감한 데이터는 명시적인 범위가 필요합니다. AppFunction이 금융, 건강, 또는 메시지를 다루면 출력을 공개 API 응답처럼 다루세요. 필요한 최소 데이터만 반환하고 그 이상은 하지 마세요.
  • 호출자의 패키지 이름을 검증하세요. AppFunctionContext는 호출하는 에이전트의 패키지를 제공합니다. 민감한 작업에 대해 신뢰할 수 있는 호출자를 허용하세요 — 모든 에이전트에게 평등한 신뢰를 가정하지 마세요.
  • 모든 호출을 기록하세요. 사용자는 대리인이 대신 했던 일을 볼 수 있어야 합니다. 첫날부터 감사 추적을 구축하세요.

좋은 소식: 안드로이드의 BIND_APP_FUNCTION_SERVICE 권한은 기본적으로 시스템에祝福받은 대리인인 Gemini만 함수를 호출할 수 있게 합니다. 플랫폼의 보안 장벽은 합리적입니다. 하지만 대리인 생태계가 성장할수록 공격 대상이도 커집니다.

참고: 모든@AppFunction는 공개 API 엔드포인트처럼 동작한다 — 입력을 검증하고 출력 범위를 설정하고 호출을 로깅한다. 주석은 간단하지만, 그것이 담당하는 책임은 아니다.

실제로 노출되는 것

이것은 개발자들에게 놀라운 사실이다: 당신은 소스 코드를 노출하지 않고, 당신은 실제로앱의 기능 표면을 노출하고 있다 — 그리고 그것은 자신만의 위험을 가지고 있다.

AppFunctions가 외부 세계에 드러내는 것:

  • createExpenseReport 또는 deleteUserAccount와 같은 함수 이름은 발견 가능한 공개 스키마로 작용합니다 — 올바른 도구를 가진 누구나 앱에 등록된 내용을 열거할 수 있습니다
  • 매개변수 유형은 내부 데이터 모델을 드러냅니다. CreateExpenseParams의 형태가 이름만큼이나 보이는 것입니다
  • 비즈니스 로직 경계 — 만약archiveAllRecords는 애플리케이션 기능으로 존재하며, UI를 출시하기 전에 그 능력을 홍보했다

실제 공격 시나리오를 고려해야 합니다

  • 탐색 앱이 등록된 기능을 나열하여 데이터 모델을 역공학하는 경우
  • 내부 인증 확인이 없는 관리자 수준의 기능이 신뢰할 수 없는 에이전트에 의해 호출되는 경우 — 앱 매니페스트 권한만으로는 충분하지 않습니다
  • 라이브 출시 전 경쟁사들이 당신의 함수 이름을 읽고 어떤 것들이 오는지 정확히 알고 있습니다

그에 대해 어떻게 해야 할까요:

  • 명시적으로 등록되지 않은 함수를 결코 등록하지 마세요설계되었습니다агент 호출 가능하도록 — 그러나 귀하의 저장소에 있는 모든 것이 그런 건 아닙니다@AppFunction
  • 인증 확인 추가내부에 모든 기능, 선언된 권한과 무관하게
  • 민감한 작업에 대해 일반적인 이름을 사용하세요. 기능 이름 자체가 의도를 유출할 경우
  • 릴리스 전에 @AppFunction 주석을 검토하세요. 공개 REST API를 검토하는 것과 같은 방식으로

Google I/O 2026은 매우 흥미로운 발표가 많았습니다. Compose First은 늦었지만 환영합니다. AI 개발 도구는 정말 유용합니다. Android Automotive는 탐색할 가치 있는 진정한 직업적 경로입니다.

하지만 AppFunctions는 "Android 앱"이라고 할 때 우리가 의미하는 것을 바꿀 것입니다. 그것은 숙제입니다. 그리고 다섯 년 뒤에는 I/O 2026을 변화가 시작된 순간으로 돌아보게 될 것입니다.

오늘 에이전트가 당신의 함수를 시작으로 호출할 경우 현재 서비스 레이어에서 idempotency를 어떻게 처리하시겠습니까? 아키텍처의 "악몽" 시나리오를 댓글에 남겨주세요 — 그 시나리오들을 들려주셨으면 좋겠습니다.


관련 세션: 개발자 키노트 — Google I/O 2026