Android AppFunctions는 어떻게 앱을 에이전트에게 연결할까?

easyhooon·7일 전
post-thumbnail

부제: Android CLI로 시작해 AppFunctions로 여러 앱을 잇고, 여비에 여행 생성 기능을 붙여본 기록

서두

2026 Google I/O Extended의 Android AppFunctions 핸즈온에 참여했다.

발표 자료는 다음 한 문장으로 시작했다.

@AppFunction 하나로 내 앱의 기능을 AI Agent에게 제공할 수 있다.

Gemini 기반 Agent 앱에서 ChatApp의 Alice에게 메시지를 보내는 첫 시연까지는 생각보다 덜 신기했다. Agent가 한 앱의 함수를 대신 호출하는 모습은 어느 정도 예상할 수 있었다.

그다음 시연은 달랐다.

가계부 앱에서 소비 내역을 가져와 ChatApp의 Alice에게 보내줘.

Agent는 AppFunctions가 적용된 가계부 앱에서 소비 내역을 가져온 뒤, 그 결과를 ChatApp으로 넘겨 Alice에게 메시지를 보냈다.

가계부 앱과 ChatApp을 미리 연결하지 않았는데도 하나의 요청이 두 앱을 거쳤다. AppFunctions가 단일 앱의 기능을 호출하는 데서 끝나지 않고 여러 앱의 기능을 이어 붙일 수 있다는 점이 가장 신기했다.

시연을 마친 뒤에는 각자 프로젝트에서 “사용자가 한 문장으로 시킬 만한 행동” 하나를 AppFunction으로 열어봤다.

나는 여비의 여행 생성 기능을 골랐다.

여비에서 여행을 만들려면 앱을 열고 제목, 일정, 국가를 차례대로 입력해야 한다.

그런데 이 과정은 한 문장으로도 충분히 표현된다.

8월 3일부터 7일까지 일본으로 가는
"도쿄 여름휴가" 여행을 여비에 만들어줘.

그래서 기존 여행 생성 Repository를 AppFunction으로 연결해봤다.

@AppFunction(isDescribedByKDoc = true)
suspend fun createTrip(
    params: CreateTripParams,
): CreateTripResult

코드만 보면 어노테이션이 붙은 suspend 함수 하나다. 조금 생각해보면 이상하다.

에이전트는 여비의 코드를 모르는데, 이 함수가 있다는 사실과 파라미터의 의미를 어떻게 알아낼까?

함수는 앱 프로세스 안에 있는데 다른 앱인 에이전트는 어떻게 실행할까?

Manifest에는 exported="true"인 Service를 등록하는데, 아무 앱에서나 호출할 수 있는 것은 아닐까?

자연어로 들어온 요청이 실패했을 때 에이전트는 그 이유를 어떻게 구분할까?

AppFunctions가 무엇인지, Kotlin 함수가 어떻게 에이전트의 도구가 되는지부터 살펴보자.

I/O Extended 핸즈온에서 본 흐름과 여비의 여행 생성 기능을 실제 브랜치에 적용한 과정도 함께 정리해보려 한다.

본론

우선, 결론부터 말하면 AppFunctions는 앱의 함수를 LLM에 직접 넘기는 API가 아니다.

함수의 구조와 설명을 Android OS에 등록해두면 권한을 가진 에이전트가 이를 발견하고 실행한다. AppFunctions는 이 과정을 담당하는 Android 플랫폼 API다.

Android 공식 문서는 이를 앱이 온디바이스 MCP 서버처럼 동작하는 구조라고 설명한다.

앱의 Kotlin 함수
        ↓
KSP가 함수 Metadata와 호출 코드 생성
        ↓
Android OS가 설치된 AppFunction을 색인
        ↓
권한 있는 Agent가 함수 목록 조회
        ↓
사용자 요청과 함수 설명을 바탕으로 함수 선택
        ↓
앱 프로세스에서 기존 비즈니스 로직 실행

AppFunctions는 무엇인가?

기존 Android 앱은 대부분 UI로 기능을 제공했다. 사용자는 화면을 열고 버튼을 눌러 원하는 기능에 도달한다.

AppFunctions는 여기에 에이전트가 호출하는 두 번째 표면을 추가한다.

사람을 위한 표면
사용자 -> Screen -> ViewModel -> Repository

에이전트를 위한 표면
Agent -> AppFunctionService -> Repository

두 경로의 진입점은 다르지만 실제 기능까지 다시 만들 필요는 없다.

AppFunction에서도 기존 Repository와 Database, 로그인 상태를 그대로 사용할 수 있다.

AppFunctions의 호출 경로와 함수 실행은 기기 안에 있다.

AppFunction 내부에서 기존 Repository가 서버 API를 호출한다면 네트워크 통신은 여전히 발생한다.

별도의 원격 MCP 서버를 운영하지 않아도 된다고 해서 앱의 모든 작업이 오프라인이 되는 것은 아니다.

일반적인 MCP와 무엇이 다를까?

AppFunctions는 MCP의 tool과 비슷한 역할을 Android 앱 안에서 제공한다.

다만 실행 위치와 발견 방식이 다르다.

구분Android AppFunctions일반적인 원격 MCP
실행 위치설치된 Android 앱 프로세스별도 원격 서버
함수 발견Android OS의 AppFunction RegistryMCP Server가 제공하는 tool 목록
기존 앱 상태로그인, DB, Repository를 직접 사용앱 로컬 상태 대신 서버 측 상태·인증 사용
네트워크호출 자체는 기기 안에서 중계보통 Client와 Server 사이 통신 필요
접근 제어Android 권한과 AppFunction 활성화 상태MCP Server의 인증과 정책
현재 상태Android 16+ Experimental Preview플랫폼과 구현에 따라 다름

위 표는 별도 원격 서버로 운영하는 MCP와 비교한 일반적인 차이다. MCP의 실행 위치와 상태·인증 방식은 구성에 따라 달라질 수 있다.

AppFunctions와 원격 MCP는 서로를 완전히 대체하지 않는다. 에이전트는 요청에 따라 서버의 MCP tool과 기기에 설치된 AppFunction을 함께 고려할 수 있다.

Kotlin 함수는 어떻게 에이전트의 도구가 될까?

이 과정에는 Provider, Android Platform, Consumer가 등장한다.

[Provider App]
@AppFunction + KDoc + Serializable Type
               ↓
[Android Platform]
Function Registry + AppFunctionManager
               ↓
[Consumer]
권한을 가진 Agent 또는 System App

우리가 구현하는 것은 Provider 쪽이다.

@AppFunctionServiceEntryPoint가 붙은 추상 Service에 공개할 함수를 작성한다.

@RequiresApi(36)
@AppFunctionServiceEntryPoint(
    serviceName = "YeoBeeAppFunctionService",
    appFunctionXmlFileName = "yeobee_app_function_service",
)
abstract class BaseYeoBeeAppFunctionService : AppFunctionService()

이 클래스가 abstract인 이유는 KSP가 실제 Service 구현체를 생성하기 때문이다.

serviceName은 생성할 클래스 이름이고, appFunctionXmlFileName은 함수 Metadata XML의 이름이다.

KSP는 @AppFunction 함수의 이름과 파라미터, 반환 타입, 설명을 읽어 Metadata와 호출 Dispatcher를 만든다.

앱이 설치되면 Android OS가 Manifest와 생성된 XML을 읽어 사용 가능한 함수를 색인한다.

권한 있는 에이전트는 AppFunctionManager를 통해 Metadata를 조회한다.

에이전트는 이 Metadata를 보고 사용자 요청에 맞는 함수와 인자를 고른다. Android Platform은 실행 요청을 Provider 앱의 Service로 넘긴다.

KDoc이 단순한 주석이 아니게 됐다

여비의 createTrip에는 다음과 같은 KDoc을 작성했다.

/**
 * Creates a solo trip and attempts to issue
 * an invitation code and link for it.
 *
 * An existing trip with the same title, dates,
 * and countries is reused to make retries safer.
 *
 * @param params The trip title, travel dates,
 * and destination country codes.
 * @return The created trip ID and invitation details.
 */
@AppFunction(isDescribedByKDoc = true)
suspend fun createTrip(params: CreateTripParams): CreateTripResult

isDescribedByKDoc = true를 사용하면 KDoc 본문과 @param, @return 설명이 AppFunction Metadata에 포함된다.

이제 이 주석은 개발자만 읽는 문서가 아니다.

에이전트가 이 함수를 언제 선택하고 각 값에 무엇을 넣어야 하는지 판단하는 명세가 된다.

KDoc 본문       -> 함수 설명
@param          -> 파라미터 설명
@return         -> 반환값 설명
Property KDoc   -> 필드 설명

복구 방법이나 운영 규칙은 함수 설명이나 @AppFunctionInstruction에 명시하면 된다.

런타임에서는 원인에 맞는 AppFunctionException을 반환한다. 그래야 호출자가 실패 종류를 구분한다.

함수 하나의 설명만으로 부족한 규칙은 앱 수준의 app_metadata.xml에 적을 수 있다.

여비의 앱 Metadata에는 동일한 여행을 재사용한다는 규칙을 적었다. 초대 발급만 실패하면 여행을 다시 만들지 않는다는 점과 날짜·국가 코드 형식도 함께 기록했다.

Function KDoc
  -> 이 함수의 목적, 파라미터, 반환값

App Metadata
  -> 여러 함수에 공통인 앱의 제약과 운영 규칙

커스텀 입력과 결과는 어떻게 전달할까?

여행 생성에는 제목, 시작일, 종료일, 국가 목록이 필요하다.

결과에는 여행 ID와 초대 코드 등 여러 값이 들어간다.

이런 커스텀 타입에는 @AppFunctionSerializable을 사용한다.

@AppFunctionSerializable(isDescribedByKDoc = true)
data class CreateTripParams(
    /** A non-blank trip title of at most 15 characters. */
    val title: String,
    /** The first travel date in ISO-8601 yyyy-MM-dd format. */
    val startDate: String,
    /** The last travel date in ISO-8601 yyyy-MM-dd format. */
    val endDate: String,
    /** ISO 3166-1 alpha-3 country codes. */
    val countryCodes: List<String>,
)
@AppFunctionSerializable(isDescribedByKDoc = true)
data class CreateTripResult(
    val tripId: Long,
    val tripCreated: Boolean,
    val invitationCreated: Boolean,
    val invitationCode: String?,
    val invitationUrl: String?,
    val invitationExpiresAt: String?,
    val message: String,
)

KSP는 이 구조를 직렬화 가능한 Schema로 만든다. 덕분에 에이전트는 문자열 하나를 억지로 해석하지 않고, 이름과 타입, 설명이 정해진 구조화된 입력과 결과를 다룬다.

I/O Extended 핸즈온에서는 무엇을 해봤을까?

핸즈온은 세 단계로 진행됐다. Android CLI로 Android 16, API 36 에뮬레이터를 준비하고, 공식 AppFunctions 저장소의 ChatApp과 Testing Agent를 실행했다.

ChatApp
  -> AppFunction을 제공하는 Provider

Testing Agent
  -> 함수 목록을 조회하고 실행하는 Consumer

ChatApp에는 연락처를 찾는 searchContacts와 메시지를 보내는 sendMessage가 있다.

사람은 “Alice에게 안녕이라고 보내줘”라고 말하지만, 실제 전송 함수에는 내부 식별자인 endpointValue가 필요하다.

Testing Agent의 수동 모드에서는 각 함수와 파라미터를 직접 골라 호출한다.

LLM 모드에서는 에이전트가 필요한 경우 다음처럼 두 함수를 연결한다.

"Alice에게 안녕이라고 보내줘"
        ↓
searchContacts(query = "Alice")
        ↓
endpointValue 획득
        ↓
sendMessage(endpointValue, "안녕")

여기까지는 ChatApp 안의 두 함수를 연결한 흐름이다.

내가 더 흥미롭게 본 것은 다음 시연이었다. Agent에게 가계부 앱의 소비 내역을 가져와 Alice에게 보내달라고 요청했다.

"가계부 앱의 소비 내역을 가져와
Alice에게 메시지로 보내줘"
        ↓
가계부 앱의 소비 내역 조회 AppFunction
        ↓
소비 내역 반환
        ↓
ChatApp.searchContacts(query = "Alice")
        ↓
ChatApp.sendMessage(endpointValue, 소비 내역)

가계부 앱은 ChatApp을 알지 못하고, ChatApp도 가계부 앱을 알지 못한다.

Gemini 기반 Agent가 Android에 등록된 각 앱의 AppFunction Metadata를 보고 함수를 고른 뒤, 앞선 함수의 결과를 다음 함수의 입력으로 넘겼다.

AppFunctions가 앱끼리 직접 통신하게 만든 것은 아니다. Android의 Registry에 각 앱의 기능을 올려두고 Agent가 실행 시점에 조합하면서 앱 사이의 상호작용이 만들어졌다.

직접 해보니 어떤 기능을 AppFunction으로 열어야 할지도 조금 선명해졌다.

  • 사용자가 한 문장으로 표현할 수 있는 행동인가
  • 화면을 여러 번 누르는 것보다 문장으로 요청하는 편이 빠른가
  • 파라미터를 사람이 말로 설명할 수 있는가
  • 내부 ID가 필요하다면 이를 찾는 함수도 제공했는가
  • 결과를 에이전트가 사용자에게 설명할 수 있는가

마지막에는 각자의 앱에서 이 기준에 맞는 함수 하나를 열어봤다. 여비에서는 여행 생성이 가장 잘 맞았다.

여비에는 무엇을 추가했을까?

2026년 7월과 1.0.0-alpha10 기준으로 AppFunctions를 사용하려면 compileSdk 36 이상과 KSP가 필요하다.

dependencies {
    implementation("androidx.appfunctions:appfunctions:1.0.0-alpha10")
    ksp("androidx.appfunctions:appfunctions-compiler:1.0.0-alpha10")
}

여비 브랜치에서는 AppFunctions를 붙이는 과정에서 AGP와 Gradle을 올리고 compileSdk도 37로 조정했다.

공식 요구사항은 compileSdk 36 이상이므로 다른 프로젝트도 무조건 37까지 올려야 하는 것은 아니다.

AppFunction Service에서는 Metro의 기존 AppGraph를 가져와 네 개의 Repository를 그대로 사용했다.

TokenRepository
  -> 로그인 여부 확인

CountryRepository
  -> 지원하는 국가 코드 확인

TripRepository
  -> 기존 여행 조회, 기간 중복 검사, 여행 생성

InvitationRepository
  -> 초대 코드와 링크 생성

여행 생성 API Client나 AppFunctions 전용 Domain 계층은 따로 만들지 않았다.

기존 Repository API를 재사용하고, AppFunction Service에는 에이전트 호출에 필요한 흐름만 새로 작성했다. 입력 검증부터 중복 여행 조회, 기간 충돌 확인, 초대 발급과 부분 성공 처리까지 이 Service가 순서대로 조율한다.

Agent
  ↓
YeoBeeAppFunctionService
  ↓
AppGraph
  ↓
기존 Repository
  ↓
여비 Server API

여행 생성은 어떤 순서로 실행될까?

createTrip은 Repository 함수 하나만 호출하고 끝나지 않는다. 자연어 요청에서 만든 값이 실제 앱의 제약을 만족하는지부터 확인한다.

로그인 확인
  ↓
제목, 날짜, 국가 코드 형식 검증
  ↓
여비가 지원하는 국가인지 확인
  ↓
동일한 여행이 이미 있는지 조회
  ↓
기간이 겹치는 다른 여행 확인
  ↓
동행자 없는 여행 생성
  ↓
초대 코드와 링크 발급
  ↓
구조화된 결과 반환

네트워크와 저장소 작업이 있어 함수는 suspend로 선언하고 Dispatchers.IO에서 실행했다. AppFunction은 기본적으로 Main Thread에서 시작되므로 blocking 작업을 그대로 두면 안 된다.

입력값도 앱 화면과 같은 수준으로 검증했다.

입력여비에서 적용한 규칙
제목공백 불가, 최대 15자
시작일·종료일ISO-8601 yyyy-MM-dd
날짜 순서종료일은 시작일보다 빠를 수 없음
국가 코드ISO 3166-1 alpha-3, 1~20개
국가 지원 여부여비 서버의 국가 목록과 다시 비교

에이전트가 Schema에 맞는 타입을 보냈다고 해서 비즈니스 규칙까지 만족하는 것은 아니다. UI 경로에서 하던 검증은 AppFunction 경로에서도 Provider 앱이 책임져야 한다.

에이전트 호출에서는 재시도가 더 중요했다

에이전트가 응답을 받지 못하거나 결과를 잘못 해석하면 같은 요청을 다시 보낼 수 있다. 여행 생성처럼 상태를 바꾸는 함수에서는 중복 생성 가능성을 먼저 생각해야 했다.

여비에서는 제목, 날짜, 국가 집합이 모두 같은 여행이 이미 있으면 새로 만들지 않고 기존 여행을 재사용했다.

같은 제목 + 같은 날짜 + 같은 국가
  -> 기존 여행 ID 반환

다른 여행과 날짜가 겹침
  -> 자동 생성 중단
  -> 여비 앱에서 확인하도록 안내

물론 완전한 멱등성을 보장하지는 않는다. 현재 Mutex는 같은 Service 인스턴스 안의 동시 실행만 직렬화하고, 기존 여행을 재사용한 뒤에도 초대 발급은 다시 시도한다.

이번 구현은 “중복 생성을 막았다”보다 “중복 생성 가능성을 줄였다”가 정확하다.

초대 발급이 실패했을 때는 여행 생성까지 실패로 돌리지 않았다.

여행 생성 성공
  ↓
초대 발급 실패
  ↓
tripId 반환
invitationCreated = false
앱에서 초대를 만들도록 안내

하나의 AppFunction 안에서도 작업이 일부만 성공하는 경우가 있다. 그래서 Boolean 하나가 아니라 에이전트가 다음 행동을 정하는 데 필요한 구조화된 결과가 필요했다.

실패도 에이전트가 이해할 수 있어야 한다

잘못된 입력과 로그인 문제, 서버 장애를 같은 Exception으로 반환하면 호출자는 무엇을 다시 물어봐야 할지 알기 어렵다.

여비에서는 실패 원인에 따라 AppFunctions의 예외 타입을 나눴다.

상황반환하는 예외 또는 결과
로그인하지 않음현재 브랜치는 AppFunctionPermissionRequiredException 사용
제목·날짜·국가가 잘못됨AppFunctionInvalidArgumentException
다른 여행과 기간이 겹침AppFunctionElementAlreadyExistsException
국가·여행 조회 또는 생성 실패AppFunctionAppUnknownException
여행 생성 후 초대만 실패예외가 아닌 부분 성공 결과

예외 타입을 보고 에이전트는 다음 행동을 선택할 수 있다. 인자가 잘못됐다면 값을 다시 물어보고, 로그인이 필요하다면 앱을 열도록 안내하는 식이다.

다만 현재 브랜치에서 미로그인에 사용한 예외는 다시 살펴봐야 한다.

공식 문서에서 AppFunctionPermissionRequiredException은 Calendar처럼 사용자 승인이 필요한 Android 권한을 Provider 앱이 받지 못했을 때 쓰는 예외다. 계정 로그인 상태와는 의미가 다르다.

공식 가이드대로라면 로그인 전에는 함수를 기본 비활성화하고, 로그인 상태가 확인된 뒤 setAppFunctionEnabled()로 노출하는 방식이 더 정확하다.

exported="true"인 Service는 안전할까?

KSP가 생성한 Service는 Manifest에 등록해야 Android Platform이 발견한다.

<service
    android:name=".appfunctions.YeoBeeAppFunctionService"
    android:exported="true"
    android:permission="android.permission.BIND_APP_FUNCTION_SERVICE"
    tools:targetApi="36">

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

    <property
        android:name="android.app.appfunctions.schema"
        android:value="app_functions_schema.xsd" />

    <property
        android:name="android.app.appfunctions.v2"
        android:value="yeobee_app_function_service.xml" />
</service>

exported="true"만 보면 외부 앱이 자유롭게 접근할 수 있는 Service처럼 보이지만, 이 Service는 BIND_APP_FUNCTION_SERVICE 권한을 요구한다.

다른 앱의 AppFunction을 발견하고 실행하는 호출자도 EXECUTE_APP_FUNCTIONS 권한이 필요하다.

이 권한은 일반 앱에 자유롭게 주어지지 않는다. 시스템과 허용된 에이전트를 위한 관문이다.

그렇다고 Provider가 모든 기능을 안심하고 열어도 되는 것은 아니다. 공식 문서는 필요한 데이터와 행동만 좁게 공개하고, 민감한 정보는 피하라고 권장한다. 삭제나 결제처럼 중요한 행동에는 앱 자체의 명확한 확인 단계를 두라고도 권한다.

로그인이나 구독 상태에 따라 사용할 수 없는 함수라면 기본값을 비활성화한 뒤 런타임에 활성화하는 방법도 있다.

직접 호출과 자연어 호출은 무엇이 다를까?

AppFunction의 등록 상태와 실행은 ADB로 확인할 수 있다.

adb shell cmd app_function list-app-functions
adb shell "cmd app_function execute-app-function \
  --package com.yeobee.dev \
  --function 'com.yeobee.appfunctions.BaseYeoBeeAppFunctionService#createTrip' \
  --parameters '{
    \"params\": {
      \"title\": \"도쿄 여름휴가\",
      \"startDate\": \"2026-08-03\",
      \"endDate\": \"2026-08-07\",
      \"countryCodes\": [\"JPN\"]
    }
  }'"

위 예시는 debug variant의 Application ID다. release 앱을 설치했다면 --packagecom.yeobee로 바꿔야 한다.

이 방식은 LLM의 함수 선택을 빼고 Service 등록과 직렬화, 함수 호출 경로를 직접 확인한다. 함수 ID와 파라미터를 이미 알고 직접 채워 넣기 때문에 LLM이 자연어에서 적절한 함수를 골랐는지는 검증하지 못한다.

ADB 직접 호출
  -> 등록과 실행 검증

Testing Agent의 수동 모드
  -> 권한 있는 조회와 결정적 실행 검증

Testing Agent의 LLM 모드
  -> 자연어에서 함수와 인자를 고르는 과정 검증

Gemini 전체 연동
  -> 실제 사용자 자연어 경험 검증

Testing Agent에는 수동 입력 모드와 LLM 기반 Agent 모드가 모두 있다. Testing Agent로 실행했다는 사실만으로 자연어 함수 선택까지 검증했다고 보기는 어렵다. 어떤 모드를 사용했는지 구분해야 한다.

2026년 7월 현재 AppFunctions는 Experimental Preview다.

AppFunctions와 Gemini의 전체 연동도 제한된 앱과 시스템 에이전트를 대상으로 한 Private Preview 단계다.

지금 구현하고 ADB와 Testing Agent로 준비할 수는 있지만, 모든 배포 앱이 곧바로 Gemini에서 호출되는 것은 아니다.

결론

처음 질문으로 돌아가보자.

에이전트는 여비의 코드를 모르는데 어떻게 createTrip을 발견하고 실행할까?

KSP가 @AppFunction과 타입, KDoc을 읽어 함수 Metadata와 호출 코드를 만든다. Android OS는 앱이 설치될 때 이 정보를 Registry에 색인한다.

권한을 가진 에이전트는 Metadata를 조회하고 사용자 요청과 함수 설명을 비교해 함수를 선택한다.

플랫폼이 호출을 여비의 Service로 넘기면 함수는 앱 프로세스 안에서 기존 Repository를 사용해 여행을 만든다.

사용자의 자연어
  ↓
권한 있는 Agent
  ↓
Android AppFunction Registry
  ↓
YeoBeeAppFunctionService
  ↓
기존 Repository와 Server API
  ↓
여행 ID, 초대 코드, 결과 메시지

핸즈온 전에는 AppFunctions를 “함수에 어노테이션을 붙여 AI가 호출하게 만드는 기능” 정도로 생각했다.

ChatApp에서 Alice에게 메시지를 보내는 시연만 봤을 때는 앱을 조작하는 새로운 입력 방식처럼 느껴졌다.

가계부 앱의 소비 내역을 가져와 Alice에게 보내는 흐름을 보고 나서 생각이 달라졌다. AppFunction 하나는 단일 앱의 기능이지만, Agent는 여러 앱의 함수를 이어 하나의 작업으로 만들 수 있었다.

AppFunctions가 직접 앱들을 연결하는 것은 아니다. 각 앱이 기능과 설명을 Android Registry에 공개하고, Agent가 사용자의 요청에 맞춰 실행 순서를 조합한다.

여비에 직접 적용해보니 어노테이션보다 어떤 기능을 열 것인지, 에이전트가 오해하지 않도록 어떻게 설명할 것인지 정하는 일이 더 중요했다.

KDoc은 Schema의 일부가 됐고, Exception은 복구 전략이 됐다.

UI에서는 사용자가 화면을 보며 잘못된 값을 바로 고친다. AppFunction에서는 입력 검증과 구조화된 실패가 그 역할을 대신한다.

재시도 가능한 환경에서는 중복 생성과 부분 성공도 함수 계약에 포함해야 했다.

아직 AppFunctions는 Experimental Preview이고 Gemini와의 전체 연동도 제한적이다.

그래도 기존 앱의 아키텍처를 갈아엎지 않고 Repository 앞에 에이전트용 진입점을 하나 더 만들 수 있다는 점은 분명했다.

앱이 더 이상 화면으로만 기능을 제공하지 않는다면, 앞으로의 API 설계에는 사람뿐 아니라 에이전트가 읽을 함수 설명도 함께 들어가게 될 것 같다.

Reference

profile
실력은 고통의 총합이다. Android Developer

0개의 댓글