A2UI가 웹 중심 구성요소에서 네이티브 Android로 전환되고 있습니다. A2UI v0.9의 새로운 Jetpack Compose 렌더러를 사용하면 개발자가 LLM에서 생성한 동적 UI를 네이티브로 렌더링할 수 있습니다. 웹뷰 대신 코루틴 기반 액터 프레임워크, sealed 클래스 수명 주기, 네이티브 Material 3 통합을 활용합니다.
이 세션에서는 새로운 아키텍처를 살펴보고, 서버 기반 구성요소 트리가 반응형 Compose 노드로 변환되는 방식과 기본 제공 레이아웃 전환이 UI를 차단하지 않고 구조적 업데이트를 원활하게 처리하는 방식을 설명합니다.
스크립트
안녕하세요. 이번 세션에서는 새로운 A2UI 라이브러리를 사용하여 Android 앱에서 UI를 생성하는 방법을 알아보겠습니다.
저는 Jetpack Compose를 담당하는 제품 관리자 닉 부처입니다.
안녕하세요. 저는 Compose팀의 엔지니어인 안톤입니다.
저는 새로운 Jetpack Compose A2UI 렌더러를 개발하고 있으며, 오늘 이 렌더러를 소개해 드리게 되어 매우 기쁩니다.
예, AI 기능이 발전함에 따라 많은 앱에서 AI를 활용하여 사용자 경험을 개선하거나 새로운 경험을 구축하는 방법을 모색하고 있습니다.
일부 사용자가 주목하는 흥미로운 경험 중 하나는 생성형 UI입니다. 이는 런타임에 에이전트 백엔드가 사용자를 위해 완전히 새로운 UI를 생성하는 방식으로, 매우 흥미로운 기술이라고 생각합니다.
하지만 안타깝게도 현재 많은 에이전트 백엔드의 응답은 이와 같이 텍스트로 가득한 화면에 갇혀 있습니다.
여기서는 에이전트에게 여행 예약이라는 새로운 기능을 생성해 달라고 요청한 예시입니다.
프롬프트는 다음과 같습니다.
보시다시피 꽤 괜찮은 답변을 제공하지만, 쉽게 상호작용할 수 없는 텍스트 벽에 갇혀 있으며 애플리케이션의 나머지 부분과 전혀 다르게 보입니다.
생성형 UI를 사용하면 동적 입력에 대한 응답으로 즉시 새로운 UI를 생성할 수 있어 더 많은 맞춤설정이나 새로운 경험을 제공할 수 있습니다.
다음은 표준 네이티브 구성요소를 사용하여 만든 UI의 예입니다.
따라서 지금도 이러한 환경을 직접 구축할 수 있지만 백엔드와의 교환을 위한 자체 형식, 자체 이벤트 스트림, 함수 등을 정의해야 합니다.
A2UI(Agent-to-UI)는 이 모든 것을 정의하는 JSON 형식의 개방형 프로토콜입니다.
A2UI에서 정말 마음에 드는 점 중 하나는 선언적 형식이라는 것입니다.
에이전트는 렌더링할 UI의 JSON 표현을 반환할 뿐입니다.
클라이언트가 실행해야 하는 코드를 반환하지 않으므로 특정 보안 취약점에 노출될 수 있습니다.
선언적입니다.
그렇다면 A2UI에 대해 알아야 할 다른 개념은 무엇일까요?
카탈로그와 표시 경로라는 두 가지 주요 개념만 있습니다.
카탈로그에는 에이전트가 UI를 빌드하는 데 사용할 수 있는 텍스트, 버튼, 행, 열과 같은 구성요소가 있습니다.
카탈로그에는 에이전트가 텍스트 형식을 지정하거나, 사용자 입력을 검증하거나, URL을 여는 데 사용할 수 있는 함수도 있습니다.
카탈로그는 앱에서 지원하는 구성요소와 함수를 정의하는 계약이며, 에이전트는 이러한 구성요소와 함수만 사용하여 빌드할 수 있습니다.
즉, 닉이 언급한 것처럼 에이전트는 앱에서 임의의 코드를 실행하지 않습니다.
설계하고 신뢰하는 구성요소로 UI를 구성합니다.
또 다른 중요한 개념은 표시 경로입니다.
표면은 슬라이드의 여행 플래너 예시와 같이 에이전트가 제어하는 UI의 한 부분입니다.
에이전트는 A2UI 프로토콜 메시지(예: createSurface, updateComponents)를 사용하고 카탈로그에 정의된 구성요소와 함수를 사용하여 JSON으로 표시되는 UI를 설명합니다.
카탈로그는 에이전트가 인터페이스를 구성하는 데 사용할 수 있는 구성요소를 제어하므로 A2UI에서 매우 기본적인 개념입니다.
A2UI 사양은 대부분의 UI를 구성하는 데 사용할 수 있는 공통 구성요소 집합인 기본 카탈로그를 정의합니다.
Jetpack Compose A2UI 렌더러는 Material 3 구성요소를 사용하여 기본 카탈로그를 구현합니다.
대부분의 카탈로그 구성요소는 즉시 사용할 수 있습니다.
이미지, 동영상, 오디오 플레이어의 세 가지 미디어 구성요소가 있으며, 여기에 원하는 라이브러리를 연결해야 합니다.
예를 들어 이미지 로드에는 Coil을, 동영상 및 오디오 플레이어에는 Media3를 선택할 수 있습니다.
하지만 A2UI와 새로운 렌더러 라이브러리의 진정한 강점은 완전히 맞춤설정된 자체 구성요소와 카탈로그를 만들고 에이전트가 UI를 빌드하는 데 사용할 수 있는 요소를 완전히 제어할 수 있다는 점입니다.
카탈로그는 나중에 다시 살펴보겠습니다.
그럼 어떻게 보이는지 살펴보겠습니다.
데모 기기로 전환해 주세요.
이 앱은 우리가 사용할 데모 앱이며, A2UI 렌더러를 빌드하여 얼마나 쉬운지 보여드리겠습니다.
화면 중앙에 있는 작은 칩에서 볼 수 있듯이 에이전트에 보낼 수 있는 프롬프트 집합입니다.
따라서 프롬프트 중 하나를 클릭하면 해당 프롬프트가 실제 상담사 백엔드로 전송됩니다. 데모가 잘 작동하기를 바랍니다. 프롬프트 외에도 몇 가지 시스템 안내도 추가합니다.
따라서 백엔드에 A2UI 프로토콜과 구성요소 카탈로그(이 경우 여기에서 사용하는 기본 카탈로그)에 대해 알려주고, 이를 사용하여 UI를 빌드할 수 있도록 하며, 이를 빌드하는 방법에 대한 몇 가지 예시를 제공합니다.
데모 앱의 채팅 세션 구현은 이미 에이전트의 대답에서 원시 JSON A2UI 메시지를 추출하는 방법을 알고 있습니다.
이 경우 에이전트는 텍스트와 함께 A2UI 프로토콜 메시지(createSurface, updateComponents)로 대답했습니다.
하지만 현재 앱은 이 원시 JSON을 표시할 뿐 아직 UI로 렌더링하지는 않습니다.
코드를 구현하기 전에 잠시 슬라이드로 돌아가서 이 구현에 사용할 시스템 설계에 대해 알아보겠습니다.
아, 여기 있네요.
계획은 이렇습니다.
따라서 AI 에이전트는 ChatSession 클래스에서 수신하는 응답을 앱으로 스트리밍합니다.
ChatSession은 에이전트의 응답이 스트리밍될 때 응답에서 원시 A2UI JSON 메시지를 추출하여 A2UI 메시지 프로세서에 전달합니다.
메시지 프로세서는 원시 JSON 메시지를 시맨틱 표현으로 변환하는 파서로 구성됩니다.
그런 다음 프로세서는 카탈로그 정의를 기준으로 메시지를 검증하고 이를 표시 경로 모델 집합에 매핑합니다.
표면 모델은 UI의 모양과 에이전트가 제어하는 UI 요소를 정의합니다.
그런 다음 이러한 표시 경로 모델은 라이브러리에서 제공하는 A2UI 표시 경로 컴포저블을 사용하여 네이티브 Compose UI로 렌더링됩니다.
에이전트와 UI 간의 통신은 양방향이므로 사용자가 UI와 상호작용할 때 사용자 작업과 오류가 에이전트로 다시 전달됩니다.
요약하자면 카탈로그, 메시지 프로세서, 표시 영역이라는 세 가지 주요 구성요소가 있습니다.
코드를 작성해 보겠습니다.
네, 기계를 바꿀 수 있다면 만들어 보죠.
첫 번째 단계는 종속 항목을 추가하는 것입니다.
여기에서 Material 3 A2UI 종속 항목이 이미 포함되어 있고 프로젝트가 이미 동기화되어 있으므로 Gradle을 지켜볼 필요가 없습니다.
이는 Material 3 구성요소를 사용하여 기본 카탈로그의 구현을 정의하는 최상위 종속 항목이지만, 그 아래에는 더 세분화된 다른 종속 항목이 있습니다.
따라서 기본 카탈로그를 사용하지 않으려면(이유는 나중에 설명) 더 세분화된 카탈로그를 사용할 수 있습니다.
하지만 이렇게 하면 나머지가 전이적으로 가져와집니다.
좋습니다.
세 가지가 있다고 말씀드렸습니다.
첫 번째 단계는 카탈로그를 정의하는 것이었습니다.
따라서 애플리케이션에서 기본 카탈로그의 Material 3 구현 인스턴스를 빌드하는 데 도움이 되는 팩토리 함수를 만들겠습니다.
따라서 여기서는 버전이 지정된 라이브러리 함수인 Material A2UI 기본 카탈로그를 사용합니다.
앞서 말씀드렸듯이 이 라이브러리는 필요한 모든 공통 구성요소를 제공하지만, 특정 이미지 로드 라이브러리나 미디어 렌더링 라이브러리를 사용하도록 강제하지는 않으므로 직접 제공해야 합니다.
예를 들어 여기에서 안톤은 이미 Coil의 AsyncImage 컴포저블을 둘러싼 래퍼를 만들었습니다. 이 래퍼는 이미지를 로드하는 데 사용되며, 데모에서 위키백과의 이미지를 조회하는 데 사용되는 약간의 추가 로직도 포함되어 있습니다.
따라서 이를 카탈로그 구성요소에 매우 쉽게 연결할 수 있습니다.
마찬가지로 렌더링, 동영상, 오디오에도 유사한 구현이 있으며, Media3 라이브러리를 사용하여 ExoPlayer로 렌더링합니다.
이렇게 아주 간단하게 자체 구성요소를 연결할 수 있습니다.
카탈로그에 필요한 다음 요소는 표준 함수 집합입니다.
즉, 클라이언트가 제공할 수 있는 기능입니다.
기본 카탈로그와 함께 이 구현을 제공하지만, 사용자가 항목을 탭하면 URI를 여는 것과 같은 특정 작업을 수행하는 데 사용할 컨텍스트를 제공해야 합니다.
이렇게 첫 번째 단계가 완료되었습니다.
카탈로그를 만들었습니다.
다음 단계는 메시지 프로세서를 설정하는 것입니다.
이것이 실제로 응답 등을 파싱하는 것입니다.
애플리케이션의 뷰 모델로 전환하여 이전에 설정한 카탈로그를 인스턴스화합니다. 이 카탈로그가 필요하기 때문입니다.
그런 다음 이 메시지 파서, 메시지 프로세서 객체를 만듭니다. 이 객체는 사용하려는 구성요소에 대해 응답의 유효성을 검사하기 위해 카탈로그에 액세스해야 합니다.
만들어야 하는 또 다른 구성요소는 파서입니다. 파서는 실제로 JSON 메시지가 반환될 때 파싱을 수행하는 객체입니다.
여기에서 ChatSession 객체를 만듭니다.
앞서 언급했듯이 ChatSession은 이미 에이전트의 응답에서 원시 A2UI 메시지를 추출합니다.
그리고 새로운 A2UI 메시지가 준비되면 이 onA2uiMessage 콜백을 호출합니다.
따라서 각 메시지가 도착하면 프로세서에 전달하기만 하면 됩니다.
processInput 함수를 호출하고 파서와 메시지를 전달합니다.
따라서 processInput 함수는 각 메시지를 파싱한 다음 프로세서가 카탈로그와 비교하여 검증하고 표시 경로 모델을 업데이트할 수 있도록 작업을 큐에 추가합니다.
따라서 프로세서는 현재 활성 표면 모델 집합을 activeSurfaces StateFlow로 노출합니다.
ChatSession에는 렌더링할 에이전트 답장의 UI 상태에 표시 모델을 추가하는 로직이 있습니다.
따라서 이 activeSurfaces StateFlow를 ChatSession에 매개변수로 전달하면 됩니다.
또한 앞서 언급했듯이 processInput 함수는 프로세서의 작업을 큐에 추가합니다.
하지만 실제로 처리를 시작하려면 백그라운드 스레드에서 코루틴을 실행해야 합니다.
따라서 뷰 모델 범위를 사용하고 기본 디스패처를 설정합니다.
그리고 이 코루틴에서 processor.collectMessages를 호출하기만 하면 됩니다.
따라서 이 코루틴이 활성 상태인 동안 프로세서는 메시지 큐에서 메시지를 처리합니다.
따라서 사용자가 채팅 기록을 지울 때 현재 표시 영역도 지워야 합니다.
따라서 clearSurfaces를 호출합니다.
좋습니다.
처음 두 단계를 완료했습니다.
카탈로그를 만들고 메시지를 파싱하는 프로세서를 만들었습니다.
다음 단계는 UI에 이러한 응답을 실제로 렌더링하는 것입니다.
따라서 파서에서 파싱되는 표면을 수집합니다.
이제 UI에서 이 에이전트 응답 객체에 이를 전달하고 반복합니다.
그런 다음 이 A2UI Surface 컴포저블을 사용하고 각 모델을 반복하면서 모델을 전달하여 원하는 위치에 콘텐츠를 렌더링합니다.
여기서 한 가지 유의할 점은 에이전트가 많은 양의 콘텐츠를 표시하기로 선택한 경우 목록과 같은 것을 사용하도록 선택할 수 있다는 것입니다.
따라서 이 구성요소에 제한된 높이를 최대 높이로 설정하여 제대로 렌더링되도록 해야 합니다.
이렇게 해서 기본 A2UI 렌더러를 구현했습니다.
이제 앱으로 돌아가서 다시 실행하고 프롬프트를 다시 입력하면 에이전트가 이를 사용하여 실제 UI를 렌더링할 것입니다.
그래서 실시간으로 코딩합니다.
이것은 눈속임이 아닙니다.
하지만 실제 이미지를 사용합니다.
탭, 대화상자, 날짜 선택 도구, 슬라이더 등 앱을 빌드할 때 사용하는 모든 표준 Material 구성요소를 사용하여 텍스트 응답만 표시하는 대신 앱을 빌드합니다.
실제 Material 구성요소를 사용하는 실제 앱이므로 실제 구성요소가 하는 일을 합니다.
네이티브 성능을 제공합니다.
여기에서 어두운 모드에 응답하는 등의 작업을 수행합니다.
이것이 Material 구성요소입니다.
따라서 색상, 도형, 서체 등을 맞춤설정한 외부 Material 테마 컴포저블이 있는 경우 이러한 구성요소는 이를 선택하여 애플리케이션의 나머지 부분과 동일하게 표시됩니다.
닉이 언급했듯이 이러한 구성요소는 네이티브 Compose 구성요소이므로 네이티브 시맨틱스를 갖습니다.
즉, Compose의 모든 내장 접근성 기능이 지원됩니다.
따라서 TalkBack, 글꼴 크기 조정, 키보드 탐색과 같은 기능이 기본적으로 작동합니다.
런타임에 에이전트를 호출하여 새 UI를 생성하므로 지연 시간이 발생할 수 있습니다.
상담사가 응답할 때까지 기다려야 할 수도 있습니다.
라이브러리는 이 지연 시간을 최소화하기 위해 최선을 다합니다.
따라서 이러한 종류의 대기 시간을 삽입하여 조금 더 천천히 보여주고 이를 최소화하는 방법을 보여드리겠습니다.
예, 에이전트가 전체 응답을 스트리밍하는 데 시간이 걸릴 수 있으므로 점진적 렌더링이라는 것을 사용합니다.
따라서 루트 구성요소가 도착하는 즉시 노출 영역의 UI가 표시되기 시작합니다.
모든 컨테이너 구성요소는 하위 요소가 아직 도착하지 않은 경우 하위 요소의 로드 자리표시자를 표시합니다.
하위 구성요소가 도착하면 Material 3 모션 스키마를 사용하여 애니메이션이 적용됩니다.
좋습니다.
라이브러리의 다른 기능을 보여주기 위해 다음 프롬프트로 전환할 수 있습니다.
저녁 식사 테이블을 예약하는 또 다른 프롬프트입니다.
여기서 에이전트는 이 슬라이더 구성요소를 사용하기로 선택합니다. 네, 사용했습니다.
여기에서 에이전트가 즉석에서 만든 UI를 드래그하고 상호작용할 수 있습니다.
구성요소의 숫자만 변경하는 것이 아니라 아래에 있는 형식 문자열도 계산하고 있습니다.
그렇다면 업데이트를 위해 에이전트로 다시 돌아가는 왕복 작업이 아니라는 말씀이시죠?
이는 양방향 결합입니다.
따라서 슬라이더 구성요소는 이 표시 경로의 인메모리 데이터 모델에 바인딩됩니다.
값이 변경되면 데이터 모델의 동일한 경로에 바인딩된 모든 구성요소가 자동으로 업데이트됩니다.
따라서 슬라이더는 표시할 값을 읽고, 슬라이더를 드래그하면 모델에 다시 기록하여 업데이트합니다.
이 모든 작업은 에이전트로 왕복하지 않고 로컬에서 이루어집니다.
하지만 에이전트에게 데이터를 업데이트하도록 요청할 수도 있습니다.
6명으로 하겠습니다.
에이전트가 대답할 때 슬라이더에 주의를 기울입니다.
6으로 증가했습니다.
상담사로부터 받은 내용을 살펴보겠습니다.
에이전트는 UI에 대한 완전히 새로운 설명을 보내는 대신 슬라이더의 경로를 업데이트하는 작은 메시지 updateDataModel 하나만 보냈습니다.
슬라이더가 해당 경로에 바인딩되어 있으므로 제자리에서 업데이트되었습니다.
따라서 각 표시 경로의 데이터 모델은 Compose 스냅샷 상태를 기반으로 빌드되며 경로에 바인딩된 각 구성요소 속성은 파생 상태입니다.
따라서 사용자 또는 에이전트가 데이터 모델의 값을 업데이트할 때마다 동일한 값에 결합된 모든 구성요소가 자동으로 재구성됩니다.
좋습니다.
그럼 조치를 취하고 예약 버튼을 누르면 어떻게 되나요, 안톤?
현재는 아무 일도 일어나지 않습니다.
버튼이 작업을 전달하지만 아직 에이전트에 전송하지는 않습니다.
이 문제를 해결해 보겠습니다.
입력을 처리하기 위해 프로세서에 연결해야 했던 것처럼, 에이전트가 만든 UI에서 사용자가 수행하는 모든 작업, 즉 모든 출력에도 연결해야 합니다.
이를 위해 프로세서가 노출하는 outboundEvents 흐름을 사용하고 이를 수집합니다.
따라서 사용자가 작업을 탭하거나 UI로 무언가를 수행할 때 원하는 로직을 연결할 수 있습니다.
여기서는 에이전트에게 작업을 다시 전송하여 수행할 작업을 결정하지만, 이 시점에서 자체 백엔드를 호출할지 아니면 애플리케이션의 다른 화면으로 이동할지 결정할 수도 있습니다.
여기에서 안톤이 연결했으므로 이제 예약 확인 작업을 누르면 수행한 작업이 에이전트로 다시 전송됩니다.
이때 백엔드에서 일부 기능을 호출하도록 결정할 수도 있습니다.
에이전트에 실제로 전송되는 내용을 살펴보겠습니다.
이것이 A2UI 액션 메시지입니다.
컨텍스트가 있으며, 컨텍스트에는 이 작업이 수행될 때 에이전트가 전송을 요청한 속성 집합이 포함됩니다.
따라서 버튼을 클릭하면 데이터 모델의 현재 값을 기반으로 해당 속성이 확인되고 에이전트에 전송됩니다.
지금까지는 에이전트가 상당히 협조적이었지만, AI는 실수를 할 수 있습니다.
에이전트가 UI에서 실수를 저지르고 이를 다시 설명하는 경우 어떤 일이 발생하는지 살펴보겠습니다.
마지막 프롬프트에서는 시연을 위해 에이전트에게 오류를 발생시키도록 요청합니다.
어떻게 되는지 살펴보겠습니다.
네.
보시다시피 구성요소 중 하나에 오류가 표시되어 있습니다.
이제 문제가 해결되었습니다.
이것이 에이전트 자체 수정 루프입니다.
각 구성요소가 UI에 도달하기 전에 카탈로그 스키마를 기준으로 메시지를 검증합니다.
구성요소가 스키마와 일치하지 않으면 오류로 표시됩니다.
기본 카탈로그 구현의 렌더러는 해당 특정 구성요소에만 오류가 발생하고 나머지 UI는 계속 표시됨을 보여줍니다.
내부적으로 렌더러는 에이전트에 오류 메시지를 전송하여 무엇이 잘못되었는지 에이전트에 알립니다.
따라서 이 경우 존재하지 않는 알 수 없는 구성요소 유형이 카탈로그에 없다는 메시지가 전송되었습니다.
그런 다음 에이전트는 기존 UI를 업데이트하는 후속 응답을 보내고 UI가 제자리에 고정되었습니다.
네.
Material 기본 카탈로그의 현재 구현에서는 오류가 즉시 표시되지만, 커스텀 구성요소에서는 에이전트가 오류를 수정하는 동안 구성요소의 로딩 표시기를 계속 표시하도록 결정할 수 있습니다.
이렇게 하면 오류가 사용자에게 완전히 숨겨질 수 있습니다.
카탈로그와 커스텀 구성요소에 대해 말씀드리자면, 지금까지는 기본 카탈로그를 기본 제공되는 그대로 사용해 왔지만, 물론 필요에 맞게 맞춤설정할 수도 있습니다.
팩토리 함수를 사용하여 카탈로그를 만들면 모든 구성요소에 기본값이 적용되는 것을 확인할 수 있습니다.
따라서 원하는 경우 자체 구성요소를 쉽게 연결할 수 있습니다.
예를 들어 앱에 자체 디자인 시스템과 자체 버튼이 있는 경우 자체 버튼을 쉽게 연결할 수 있습니다.
따라서 카탈로그 인스턴스를 만들 때 이 매개변수를 재정의하고 이 구성요소 정의를 만듭니다.
이를 구현하기 위해 새 클래스를 만들면 여기에 A2UI 기본 카탈로그 유형 버튼 인터페이스가 표시됩니다.
따라서 이를 구현하면 자체 구성요소를 연결할 수 있습니다.
네.
기본 카탈로그의 구성요소를 재정의하므로 이 A2UI 기본 카탈로그 버튼 인터페이스를 구현해야 합니다.
이 인터페이스에는 이미 이름, 설명, 속성 스키마와 같은 구성요소에 관한 정보가 있습니다.
따라서 자체 버전을 구현하려면 이 컴포저블 함수를 구현하기만 하면 됩니다.
몇 가지 매개변수를 사용합니다.
따라서 이러한 파라미터는 검증 후 에이전트로부터 수신된 값입니다.
여기에는 버튼의 콘텐츠가 될 구성요소를 참조하는 하위 ID 등이 있습니다.
또한 버튼 유형, 작업 정의, 기타 매개변수를 설명하는 변형도 있습니다.
버튼을 프로덕션에 구현할 때는 이러한 매개변수를 모두 사용해야 하지만 데모에서는 하위 요소와 작업만 있으면 됩니다.
이 컴포저블을 구현해 보겠습니다.
이 컴포저블 함수에서 맞춤 좋아요 버튼 유형을 내보내겠습니다.
보시다시피 A2UI에서 제공하고 검증한 매개변수를 사용할 수 있습니다.
예를 들어 onClick 함수에서 이 작업을 전달할 수 있습니다.
이 라이브러리 유형 DispatchAction을 사용하여 A2UI가 정의한 작업을 전달할 수 있습니다.
따라서 자체 버튼을 사용하지만 하위 콘텐츠, 라벨 또는 에이전트가 사용하기로 결정한 모든 항목을 렌더링하려고 합니다.
이를 위해 observeA2uiComponentState 함수를 사용하고 childId를 전달하여 렌더링해야 하는 하위 콘텐츠에 대한 핸들을 가져옵니다.
그런 다음 이 A2uiComponent 컴포저블 함수를 사용하고 하위 구성요소 상태를 전달하여 렌더링합니다.
여기에서는 성공적으로 로드된 경우에만 하위 콘텐츠를 렌더링하지만, 로드 중이거나 오류 상태인 경우 표시할 내용을 결정하는 자체 로직을 여기에 포함할 수도 있습니다.
데모 앱의 예시 프롬프트 중 하나로 돌아가서 표준 Material 버튼을 사용하여 버튼을 교체해 보겠습니다.
아주 미묘한 차이입니다.
여기에서도 매우 맞춤설정된 버튼을 사용하고 있습니다.
좋습니다. 이렇게 하면 라이브러리 유형 중 일부를 대체할 수 있습니다. 하지만 자체 맞춤 유형으로 라이브러리를 확장하려면 어떻게 해야 할까요?
카탈로그 자체에 추가하는 방식으로 매우 쉽게 할 수 있습니다.
안톤은 별표 평점 구성요소를 구현하기 위해 새 클래스를 만들 예정입니다.
따라서 이 가상의 여행 중에 식당을 평가할 수 있도록 애플리케이션에 무언가를 추가할 것입니다.
기존 구성요소를 대체하는 것이 아니므로 이 더 일반적인 인터페이스, 즉 A2UI 구성요소 인터페이스를 사용합니다. 이 인터페이스에는 재정의해야 하는 몇 가지 매개변수가 있습니다.
처음 두 개는 이름과 설명입니다.
이것이 구성요소의 정의와 역할입니다.
이러한 설명은 매우 중요합니다. 카탈로그를 직렬화하고 백엔드로 전송하여 UI를 빌드하는 데 사용할 수 있는 항목을 알려줄 때, 에이전트가 응답 시 이 구성요소를 사용할지 여부를 결정하는 데 사용하기 때문입니다.
따라서 AI에게 이 코드가 무엇을 하는지 정확히 알고 있다고 설명해야 합니다.
재정의해야 하는 다음 속성은 properties 속성입니다. 이 속성은 기본적으로 이 구성요소가 처리하는 데이터를 정의합니다.
레스토랑을 평가할 수 있는 별표 평점 막대를 만들고 있으며, 필요한 속성은 두 가지입니다.
사용자가 선택한 별표 수와 같은 값이 될 것입니다. 이는 사용자가 상호작용하고 변경할 수 있는 변경 가능한 속성이며 최대 별표 수입니다.
여기에서 안톤은 이 동적 숫자 유형 속성을 값으로 사용하고 있습니다.
이것은 실제로 선택된 별의 개수입니다.
이 값의 용도를 에이전트에 알려주는 자체 설명이 있습니다.
그리고 정적 유형 속성이 있습니다. 이 구성요소에서 표시할 수 있는 최대 별표 수를 정의하기 위해 숫자 유형의 A2UI 속성을 사용합니다.
두 속성을 정의한 후에는 반환하기만 하면 됩니다.
따라서 이 properties 속성을 재정의하여 목록을 반환합니다.
이것이 바로 에이전트에게 이 구성요소가 처리하는 내용을 알려주는 방법입니다.
네.
닉이 언급했듯이 이 속성 정의는 에이전트가 이 구성요소를 사용하는 방법을 알 수 있도록 카탈로그 스키마의 일부로 전송됩니다.
하지만 이 속성, 즉 동일한 속성 정의는 런타임 시 유형이 지정된 값을 추출하는 데도 사용됩니다.
따라서 단일 정보 소스가 있습니다.
이를 구현하기 위해 무엇을 해야 하는지 살펴보겠습니다.
먼저 max 속성에 액세스하려면 max 속성이 정적 속성이므로 에이전트가 속성 객체에 인라인으로 값을 전송합니다.
따라서 이 속성에 액세스하려면 proper-max 속성 객체를 키로 사용하여 속성 객체에서 가져오면 됩니다.
이 속성은 숫자로 정의되므로 int로 변환하고 기본값을 설정해야 합니다.
다음은 값 속성입니다.
값 속성은 동적 속성이므로 데이터 모델의 무언가를 참조하고 있음을 의미합니다.
따라서 이 속성에 액세스하려면 properties.bind API를 사용하고 속성 객체를 키로 매개변수로 전달해야 합니다.
이 속성의 데이터 모델에 저장된 현재 값을 반환하고 향후 업데이트를 구독합니다.
따라서 데이터 모델이 업데이트될 때마다 이 구성요소가 자동으로 재구성됩니다.
마찬가지로 int로 변환하고 기본값을 설정했습니다.
평점을 변경할 수 있도록 양방향 결합을 설정해야 합니다.
이를 위해 bindUpdater API를 사용합니다.
속성을 정의하는 동일한 속성 객체를 전달하고, 그 결과 bindUpdater는 데이터 모델에서 이 속성의 값을 변경할 수 있는 람다를 반환합니다.
이제 별표 평점 구성요소를 내보낼 준비가 되었습니다.
좋습니다.
자체 커스텀 구성요소를 정의했으므로 이제 사용 중인 카탈로그에 추가해야 합니다.
이전에는 기본 카탈로그를 그대로 사용했습니다.
이제 모든 기본 카탈로그를 가져와 커스텀 구성요소를 추가하는 새 카탈로그를 만들어야 합니다.
따라서 카탈로그는 세 가지 요소로 구성됩니다.
ID, 구성요소 집합, 함수 집합입니다.
ID의 경우 URI를 사용하여 카탈로그와 작업 중인 버전을 고유하게 식별합니다.
그런 다음 구성요소의 경우 기본 카탈로그에서 모든 구성요소를 가져온 다음 추가 연산자를 사용하여 방금 만든 커스텀 구성요소를 추가합니다.
함수의 경우 기본 카탈로그의 함수를 사용합니다.
이렇게 해서 기본 카탈로그를 확장하여 새 맞춤 구성요소 유형을 추가했습니다.
맞춤 카탈로그를 만드는 것은 매우 간단합니다.
따라서 에이전트는 전송된 시스템 안내를 통해 카탈로그를 사용하는 방법을 학습합니다.
이제 커스텀 카탈로그를 만들었으니 시스템 요청 사항도 업데이트해 보겠습니다.
따라서 시스템 요청 사항을 빌드하는 함수에 카탈로그 객체를 전달합니다.
바로 이렇습니다.
지금까지는 이 표준 카탈로그를 사용하고 있었기 때문에 기본 카탈로그의 스키마에 대한 정적 정의를 전송했습니다.
이제 커스텀 카탈로그가 있으므로 toJsonSchemaString 함수를 사용하여 스키마를 동적으로 생성할 수 있습니다.
그러면 방금 추가한 별표 평점 구성요소를 포함하여 이 카탈로그의 완전한 JSON 표현이 생성됩니다.
시스템 요청 사항에는 에이전트의 응답 예시도 포함되어 있습니다.
예시가 올바른 카탈로그를 참조하도록 여기에서 카탈로그 ID도 변경합니다.
좋습니다.
이렇게 해서 기본 카탈로그를 확장하여 자체 커스텀 유형을 추가했습니다.
따라서 이를 다시 배포하고 이러한 프롬프트 중 하나를 시도하면 평가 UI가 표시됩니다.
그러면 에이전트가 대답합니다.
여기에서 이 별표 구성요소는 방금 구현하기 전까지는 에이전트가 본 적이 없는 새로운 구성요소입니다.
이것은 기본 카탈로그의 일부가 아니며, 방금 만들었습니다.
별표를 클릭하면 bindUpdater 람다가 호출되고 데이터 모델에서 값이 업데이트됩니다.
평점을 제출하면 작업을 살펴볼 수 있으며 작업의 컨텍스트에는 방금 설정한 값이 포함됩니다.
이제 슬라이드로 돌아가겠습니다.
지금까지 많은 내용을 빠르게 살펴보았지만, 에이전트가 텍스트로만 응답하는 방식에서 실제 네이티브 UI를 생성하는 방식으로 쉽게 전환하는 방법을 보여드렸습니다.
통합이 매우 쉽다는 것을 보여드렸기를 바랍니다.
구성요소 카탈로그를 만들고, 파싱 로직을 구현하고, UI에서 렌더링하는 세 가지 주요 구성요소를 수행해야 합니다.
또한 이 라이브러리가 점진적 렌더링, 스키마 검증, 자체 수정 루프, 오류 처리 등 다양한 작업을 기본적으로 수행한다는 점을 보여드렸습니다.
오늘 시연을 통해 맞춤설정이 매우 쉽다는 것을 보여드렸습니다.
Material을 사용하는 경우 기본 카탈로그가 Material Theming에 어떻게 반응하는지 보여드렸지만, 카탈로그 내에서 구성요소를 확장하거나 교체하는 것은 매우 간단합니다.
하지만 가장 강력한 기능은 자체 카탈로그를 만들 때 발휘된다고 생각합니다.
상담사가 실제로 사용할 수 있는 UI를 최대한 제어하려면 완전히 커스텀 구성요소를 만들면 됩니다.
완전히 자유롭게 맡기고 싶다면 텍스트 라벨이나 버튼과 같은 아주 기본적인 구성요소를 제공하고 'UI를 만들어 줘'라고 요청하면 됩니다.
더 세밀하게 제어하려면 더 정교한 구성요소를 만들고 '이것만 사용할 수 있습니다'라고 지정하면 됩니다.
따라서 에이전트가 수행할 수 있는 작업과 사용자에게 표시할 수 있는 내용을 제어할 수 있습니다.
이것이 바로 Google에서 방금 출시한 새로운 A2UI 렌더러 라이브러리와 Jetpack 라이브러리입니다.
implementation("androidx.compose.material3:material3-a2ui:1.0.0-alpha02")현재 알파 버전이며 알파 02가 최근에 출시되었습니다.
다음 링크를 통해 확인해 보세요.
developer.android.com에 문서와 몇 가지 샘플이 있습니다.
기본 카탈로그를 보여주는 기본 샘플과 더 정교한 샘플이 있으며, 방금 만든 이 JetPacker 샘플 앱은 오늘 ADS 기조연설에서 다시 보게 될 것입니다. 이 앱은 사용자가 보는 것을 제어하기 위해 더 정교한 구성요소를 만드는 방법을 보여줍니다.
말씀드린 것처럼 현재 알파 단계이므로 지금 확인해 보고 의견과 기능 요청을 보내주시면 좋습니다. 어떻게 사용하고 싶은지, 어떤 기능이 있으면 사용하고 싶은지 알려주세요.
라이브러리를 확인하고 의견을 보내주세요.
그렇게 해서 얻은 결과입니다.
여러분과 여러분의 에이전트가 이 도구를 사용하여 어떤 결과물을 만들어낼지 정말 기대됩니다.
정말 감사합니다.
감사합니다.