1. 시작하기 전에
스마트 홈 통합을 사용하면 Google 어시스턴트가 사용자 집에 있는 연결된 기기를 제어할 수 있습니다. Cloud-to-Cloud 통합을 빌드하려면 스마트 홈 인텐트를 처리할 수 있는 클라우드 웹훅 엔드포인트를 제공해야 합니다. 예를 들어 사용자가 "Hey Google, 조명 켜 줘"라고 말하면 어시스턴트가 클라우드 처리에 명령어를 전송하여 기기의 상태를 업데이트합니다.
Local Home SDK는 스마트 홈 인텐트를 Google Home 기기에 직접 라우팅하는 로컬 경로를 추가하여 스마트 홈 통합을 강화하므로, 신뢰성이 향상되고 사용자의 명령어 처리 지연 시간이 단축됩니다. Local Home SDK를 사용하면 타입스크립트 또는 자바스크립트로 로컬 처리 앱을 작성하고 배포할 수 있으며, 이 앱을 통해 기기를 식별하고 모든 Google Home 스마트 스피커나 Google Nest 스마트 디스플레이에서 명령을 실행할 수 있습니다. 그러면 앱이 기존의 표준 프로토콜을 사용하여 명령을 처리하며 근거리 통신망을 통해 사용자의 기존 스마트 기기와 직접 통신합니다.
클라우드 간 통합을 디버그하는 것은 프로덕션 품질로 통합을 빌드하는 데 중요한 단계이지만 유용한 정보와 사용하기 쉬운 문제 해결 및 테스트 도구가 없으면 어렵고 시간이 많이 걸립니다. 클라우드 간 통합을 디버그하는 데 도움이 되도록 Google Cloud Platform (GCP) 측정항목 및 로깅, 스마트 홈용 테스트 모음을 사용하여 통합 문제를 식별하고 해결할 수 있습니다.
기본 요건
- 클라우드 간 통합 만들기 개발자 가이드
- Cloud 간 통합을 위한 로컬 처리 사용 설정 Codelab 실행
빌드할 항목
이 Codelab에서는 Cloud-to-Cloud 통합을 위한 로컬 처리를 빌드하고 어시스턴트에 연결한 다음 스마트 홈 및 Google Cloud Platform (GCP) 측정항목 및 로깅을 위한 테스트 모음을 통해 로컬 Home 앱을 디버그합니다.
학습할 내용
- GCP 측정항목 및 로깅을 사용하여 프로덕션 문제를 식별하고 해결하는 방법
- 테스트 모음을 사용하여 기능 및 API 문제를 식별하는 방법
- 로컬 Home 앱을 개발하는 동안 Chrome DevTools를 사용하는 방법
필요한 항목
2. 세탁기 앱 실행
소스 코드 가져오기
다음 링크를 클릭하여 개발 머신에 이 Codelab의 샘플을 다운로드합니다.
명령줄에서 GitHub 저장소를 복제할 수도 있습니다.
$ git clone https://github.com/google-home/smarthome-debug-local.git
프로젝트 정보
시작 앱에는 Cloud 간 통합을 위한 로컬 처리 사용 설정 Codelab과 유사한 하위 디렉터리와 Cloud Functions가 포함되어 있습니다. 하지만 여기서는 app-start
대신 app-faulty
가 있습니다. 작동하지만 그다지 좋지 않은 로컬 Home 앱으로 시작합니다.
Firebase에 연결
Cloud 간 통합을 위한 로컬 처리 사용 설정 Codelab에서 만든 것과 동일한 프로젝트를 사용하지만 이 Codelab에서 다운로드한 파일을 배포합니다.
app-faulty
디렉터리로 이동한 후 Cloud-to-Cloud 통합을 위한 로컬 처리 사용 설정 Codelab에서 만든 통합 프로젝트를 사용하여 Firebase CLI를 설정합니다.
$ cd app-faulty $ firebase use <project-id>
Firebase에 배포하기
app-faulty/functions
폴더로 이동한 다음 npm
을 사용하여 필요한 모든 종속 항목을 설치합니다.
$ cd functions $ npm install
참고: 아래 메시지가 표시되면 무시하고 계속 진행할 수 있습니다. 이 경고는 일부 이전 종속 항목으로 인해 발생하며 자세한 내용은 여기를 참고하세요.
found 5 high severity vulnerabilities run `npm audit fix` to fix them, or `npm audit` for details
app-faulty/local/
디렉터리로 이동하고 다음 명령어를 실행하여 타입스크립트 컴파일러를 다운로드하고 앱을 컴파일합니다.
$ cd ../local $ npm install $ npm run build
그러면 index.ts
(타입스크립트) 소스가 컴파일되고 다음 콘텐츠가 app-faulty/public/local-home/
디렉터리에 배치됩니다.
bundle.js
: 로컬 앱과 종속 항목을 포함하는 컴파일된 자바스크립트 출력입니다.index.html
: 기기 내 테스트를 위한 앱을 제공하는 데 사용되는 로컬 호스팅 페이지입니다.
이제 종속 항목을 설치하고 프로젝트를 구성했으므로 앱을 처음으로 실행할 수 있습니다.
$ firebase deploy
콘솔에 다음과 같은 결과가 표시됩니다.
... ✔ Deploy complete! Project Console: https://console.firebase.google.com/project/<project-id>/overview Hosting URL: https://<projectcd -id>.web.app
이 명령어는 여러 Firebase용 Cloud Functions와 함께 웹 앱을 배포합니다.
HomeGraph 업데이트
브라우저 (https://<project-id>.web.app
)에서 호스팅 URL을 열어 웹 앱을 확인합니다. 웹 UI에서 새로고침 버튼을 클릭하여 동기화 요청을 통해 결함이 있는 세탁기 앱의 최신 기기 메타데이터로 HomeGraph를 업데이트합니다.
Google Home 앱을 열고 세탁기 기기가 새 이름인 'Faulty Washer'로 표시되는지 확인합니다. Nest 기기가 있는 방에 기기를 할당해야 합니다.
3. 스마트 세탁기 시작
Cloud 간 통합을 위한 로컬 처리 사용 설정 Codelab을 실행했다면 이미 가상 스마트 세탁기를 시작했을 것입니다. 중지된 경우 가상 기기를 다시 시작해야 합니다.
기기 시작하기
virtual-device/
디렉터리로 이동한 다음 기기 스크립트를 실행하여 구성 매개변수를 인수로 전달합니다.
$ cd ../../virtual-device $ npm install $ npm start -- \ --deviceId=deviceid123 --projectId=<project-id> \ --discoveryPortOut=3311 --discoveryPacket=HelloLocalHomeSDK
기기 스크립트가 예측한 매개변수를 통해 실행되는지 확인합니다.
(...): UDP Server listening on 3311 (...): Device listening on port 3388 (...): Report State successful
4. 로컬 Home 앱 테스트
다음과 같은 음성 명령을 통해 Google Home 기기에 전송하여 기기에 명령어를 전송합니다.
"Hey Google, 세탁기 켜 줘."
"Hey Google, 세탁기 시작해 줘."
"Hey Google, 로컬 강제."
"Hey Google, 세탁기 중지해 줘."
'로컬 강제' 후 세탁기를 제어하려고 하면 Google 어시스턴트가 '죄송합니다. 지금은 결함이 있는 세탁기를 사용할 수 없는 것 같습니다'라고 응답합니다.
즉, 로컬 경로를 통해 기기에 연결할 수 없습니다. 'Hey Google, force local'을 실행하기 전에는 작동했습니다. 로컬 경로를 통해 기기에 연결할 수 없는 경우 클라우드 경로를 사용하도록 대체되기 때문입니다. 그러나 '로컬 강제'를 실행하면 클라우드 경로로 대체하는 옵션이 사용 중지됩니다.
문제의 원인을 파악하려면 Google Cloud Platform (GCP) 측정항목, 로깅, Chrome 개발자 도구와 같은 도구를 활용해 보겠습니다.
5. 로컬 Home 앱 디버그
다음 섹션에서는 Google에서 제공하는 도구를 사용하여 로컬 경로를 통해 기기에 연결할 수 없는 이유를 알아봅니다. Chrome 개발자 도구를 사용하여 Google Home 기기에 연결하고 콘솔 로그를 보고 로컬 Home 앱을 디버그할 수 있습니다. 또한 Cloud Logging에 맞춤 로그를 전송하여 사용자가 로컬 Home 앱에서 발견하는 주요 오류를 파악할 수 있습니다.
Chrome 개발자 도구 연결
디버거를 로컬 처리 앱에 연결하려면 다음 단계를 따르세요.
- Google Home 기기를 Developer Console 프로젝트에 액세스할 수 있는 사용자에게 연결했는지 확인합니다.
- Google Home 기기를 재부팅합니다. 그러면 Google Home HTML URL 및 개발자가 Developer Console에 입력한 스캔 구성을 가져올 수 있습니다.
- 개발 머신에서 Chrome을 시작합니다.
- 새 Chrome 탭을 열고 주소 입력란에
chrome://inspect
를 입력하여 검사기를 실행합니다.
페이지에 기기 목록이 표시되고 앱 URL이 Google Home 기기 이름 아래에 표시되어야 합니다.
검사기 실행
앱 URL에서 검사를 클릭하여 Chrome 개발자 도구를 실행합니다. 콘솔 탭을 선택하고 TypeScript 앱에서 출력된 IDENTIFY
인텐트의 콘텐츠를 볼 수 있는지 확인합니다.
이 출력은 IDENTIFY 핸들러가 성공적으로 트리거되었지만 IdentifyResponse
에서 반환된 verificationId
가 HomeGraph의 기기와 일치하지 않음을 의미합니다. 이유를 알아보기 위해 맞춤 로그를 추가해 보겠습니다.
맞춤 로그 추가
로컬 홈 SDK에서 DEVICE_VERIFICATION_FAILED
오류가 출력되지만 근본 원인을 찾는 데는 큰 도움이 되지 않습니다. 스캔 데이터를 올바르게 읽고 처리하는지 확인하기 위해 맞춤 로그를 추가해 보겠습니다. 오류가 있는 약속을 거부하면 오류 메시지가 실제로 Cloud Logging에도 전송됩니다.
local/index.ts
identifyHandler(request: IntentFlow.IdentifyRequest):
Promise<IntentFlow.IdentifyResponse> {
console.log("IDENTIFY intent: " + JSON.stringify(request, null, 2));
const scanData = request.inputs[0].payload.device.udpScanData;
if (!scanData) {
const err = new IntentFlow.HandlerError(request.requestId,
'invalid_request', 'Invalid scan data');
return Promise.reject(err);
}
// In this codelab, the scan data contains only local device id.
// Is there something wrong here?
const localDeviceId = Buffer.from(scanData.data);
console.log(`IDENTIFY handler: received local device id
${localDeviceId}`);
// Add custom logs
if (!localDeviceId.toString().match(/^deviceid[0-9]{3}$/gi)) {
const err = new IntentFlow.HandlerError(request.requestId,
'invalid_device', 'Invalid device id from scan data ' +
localDeviceId);
return Promise.reject(err);
}
const response: IntentFlow.IdentifyResponse = {
intent: Intents.IDENTIFY,
requestId: request.requestId,
payload: {
device: {
id: 'washer',
verificationId: localDeviceId.toString(),
}
}
};
console.log("IDENTIFY response: " + JSON.stringify(response, null, 2));
return Promise.resolve(response);
}
또한 올바른 버전을 사용하고 있는지 확인할 수 있도록 로컬 Home 앱 버전을 변경합니다.
local/index.ts
const localHomeSdk = new App('1.0.1');
맞춤 로그를 추가한 후에는 앱을 다시 컴파일하고 Firebase에 다시 배포해야 합니다.
$ cd ../app-faulty/local $ npm run build $ firebase deploy --only hosting
이제 Google Home 기기를 재부팅하여 업데이트된 로컬 홈 앱을 로드할 수 있도록 합니다. Chrome 개발자 도구의 콘솔 로그를 확인하여 Google Home 기기가 예상 버전을 사용하고 있는지 확인할 수 있습니다.
Cloud Logging 액세스
Cloud Logging을 사용하여 오류를 찾는 방법을 살펴보겠습니다. 프로젝트의 Cloud Logging에 액세스하려면 다음 단계를 따르세요.
- Cloud Platform 콘솔에서 프로젝트 페이지로 이동합니다.
- 스마트 홈 프로젝트를 선택합니다.
- 작업에서 로깅 > 로그 탐색기를 선택합니다.
로깅 데이터에 대한 액세스는 통합 프로젝트 사용자의 Identity and Access Management (IAM)를 통해 관리됩니다. 로깅 데이터의 역할 및 권한에 관한 자세한 내용은 Cloud Logging 액세스 제어를 참고하세요.
고급 필터 사용
로컬 기기를 식별할 수 없어 로컬 경로가 작동하지 않으므로 IDENTIFY
인텐트에서 오류가 발생하는 것으로 확인됩니다. 하지만 정확한 문제를 파악해야 하므로 먼저 IDENTIFY
핸들러에서 발생하는 오류를 필터링해 보겠습니다.
쿼리 표시 전환을 클릭하면 쿼리 빌더 상자가 표시됩니다. 쿼리 빌더 상자에 jsonPayload.intent="IDENTIFY"
를 입력하고 쿼리 실행 버튼을 클릭합니다.
따라서 IDENTIFY
핸들러에서 발생하는 모든 오류 로그가 수신됩니다. 그런 다음 마지막 오류를 펼칩니다. IDENTIFY
핸들러에서 약속을 거부할 때 설정한 errorCode
및 debugString
를 확인할 수 있습니다.
debugString
에서 로컬 기기 ID가 예상 형식이 아님을 알 수 있습니다. 로컬 Home 앱은 로컬 기기 ID를 deviceid
로 시작하고 3자리 숫자가 뒤에 오는 문자열로 가져올 것으로 예상하지만 여기서 로컬 기기 ID는 16진수 문자열입니다.
오류 수정
스캔 데이터에서 로컬 기기 ID를 파싱하는 소스 코드로 돌아가 문자열을 바이트로 변환할 때 인코딩을 제공하지 않았음을 확인할 수 있습니다. 스캔 데이터는 16진수 문자열로 수신되므로 Buffer.from()
를 호출할 때 문자 인코딩으로 hex
를 전달합니다.
local/index.ts
identifyHandler(request: IntentFlow.IdentifyRequest):
Promise<IntentFlow.IdentifyResponse> {
console.log("IDENTIFY intent: " + JSON.stringify(request, null, 2));
const scanData = request.inputs[0].payload.device.udpScanData;
if (!scanData) {
const err = new IntentFlow.HandlerError(request.requestId,
'invalid_request', 'Invalid scan data');
return Promise.reject(err);
}
// In this codelab, the scan data contains only local device id.
const localDeviceId = Buffer.from(scanData.data, 'hex');
console.log(`IDENTIFY handler: received local device id
${localDeviceId}`);
if (!localDeviceId.toString().match(/^deviceid[0-9]{3}$/gi)) {
const err = new IntentFlow.HandlerError(request.requestId,
'invalid_device', 'Invalid device id from scan data ' +
localDeviceId);
return Promise.reject(err);
}
const response: IntentFlow.IdentifyResponse = {
intent: Intents.IDENTIFY,
requestId: request.requestId,
payload: {
device: {
id: 'washer',
verificationId: localDeviceId.toString(),
}
}
};
console.log("IDENTIFY response: " + JSON.stringify(response, null, 2));
return Promise.resolve(response);
}
또한 올바른 버전을 사용하고 있는지 확인할 수 있도록 로컬 Home 앱 버전을 변경합니다.
local/index.ts
const localHomeSdk = new App('1.0.2');
오류를 수정한 후 앱을 컴파일하고 Firebase에 다시 배포합니다. app-faulty/local
에서 다음을 실행합니다.
$ npm run build $ firebase deploy --only hosting
수정사항 테스트
배포 후 Google Home 기기를 재부팅하여 업데이트된 로컬 Home 앱을 로드할 수 있도록 합니다. 로컬 Home 앱 버전이 1.0.2인지 확인합니다. 이번에는 Chrome 개발자 도구 콘솔에 오류가 표시되지 않습니다.
이제 기기에 명령어를 다시 전송해 보세요.
"Hey Google, 로컬 강제."
"Hey Google, 세탁기 중지해 줘."
"Hey Google, 세탁기 켜 줘."
...
"Hey Google, 기본값으로 강제 적용해 줘."
6. 스마트 홈용 테스트 모음 실행
Google Home 앱의 터치 컨트롤을 사용하거나 음성 명령을 통해 기기를 확인한 후 자동화된 스마트 홈용 테스트 모음을 사용하여 통합과 연결된 기기 유형 및 트레잇을 기반으로 사용 사례를 검증할 수 있습니다. 테스트 모음은 일련의 테스트를 실행하여 통합의 문제를 감지하고, 이벤트 로그를 살펴보기 전에 디버깅을 신속하게 진행할 수 있도록 실패한 테스트 사례에 관한 유용한 메시지를 표시합니다.
스마트 홈용 테스트 모음 실행
다음 안내에 따라 테스트 모음으로 클라우드 간 통합을 테스트합니다.
- 웹브라우저에서 스마트 홈용 테스트 모음을 엽니다.
- 오른쪽 상단의 버튼을 사용하여 Google에 로그인합니다. 이렇게 하면 테스트 모음이 명령어를 Google 어시스턴트로 직접 전송할 수 있습니다.
- 프로젝트 ID 필드에 클라우드 간 통합의 프로젝트 ID를 입력합니다. 그런 다음 다음을 클릭하여 계속합니다.
- 테스트 설정 단계에서 기기 및 트레일 섹션에 결함이 있는 세탁기가 표시됩니다.
- 샘플 세탁기 앱에는 세탁기를 추가 / 삭제 / 이름을 바꾸는 UI가 없으므로 테스트 요청 동기화 옵션을 사용 중지합니다. 프로덕션 시스템에서는 사용자가 기기를 추가 / 삭제 / 이름을 바꿀 때마다 동기화 요청을 트리거해야 합니다.
- 로컬 및 클라우드 경로를 모두 테스트할 것이므로 로컬 홈 SDK 옵션을 사용 설정된 상태로 둡니다.
- 다음: 테스트 환경을 클릭하여 테스트 실행을 시작합니다.
테스트가 완료되면 로컬 경로의 일시중지/재개 테스트가 실패하고 클라우드 경로의 일시중지/재개 테스트가 통과하는 것을 확인할 수 있습니다.
오류 메시지 분석
실패한 테스트 사례의 오류 메시지를 자세히 살펴봅니다. 테스트의 예상 상태와 실제 상태를 알려줍니다. 이 경우 '세탁기 일시중지'의 예상 상태는 isPaused: true
이지만 실제 상태는 isPaused: false
입니다. 마찬가지로 '세탁기 일시중지'의 경우 예상 상태는 isPaused: true
이지만 실제 상태는 isPaused: false
입니다.
오류 메시지를 보면 로컬 경로에서 isPaused
상태를 반대로 설정하는 것 같습니다.
오류 식별 및 수정
Local Home 앱이 기기에 실행 명령어를 전송하는 소스 코드를 찾아보겠습니다. getDataCommand()
는 executeHandler()
에서 호출하여 기기에 전송된 실행 명령어에서 payload
를 설정하는 함수입니다.
local/index.ts
getDataForCommand(command: string, params: IWasherParams): unknown {
switch (command) {
case 'action.devices.commands.OnOff':
return {
on: params.on ? true : false
};
case 'action.devices.commands.StartStop':
return {
isRunning: params.start ? true : false
};
case 'action.devices.commands.PauseUnpause':
return {
// Is there something wrong here?
isPaused: params.pause ? false : true
};
default:
console.error('Unknown command', command);
return {};
}
}
실제로 isPause
를 역방향 상태로 설정하고 있습니다. params.pause
가 true
이면 true
로 설정하고 그렇지 않으면 false
로 설정해야 합니다. 문제를 해결해 보겠습니다.
local/index.ts
getDataForCommand(command: string, params: IWasherParams): unknown {
switch (command) {
case 'action.devices.commands.OnOff':
return {
on: params.on ? true : false
};
case 'action.devices.commands.StartStop':
return {
isRunning: params.start ? true : false
};
case 'action.devices.commands.PauseUnpause':
return {
isPaused: params.pause ? true : false
};
default:
console.error('Unknown command', command);
return {};
}
}
올바른 버전을 사용 중인지 확인할 수 있도록 로컬 Home 앱 버전을 변경합니다.
local/index.ts
const localHomeSdk = new App('1.0.3');
앱을 다시 컴파일하고 Firebase에 다시 배포해야 합니다. app-faulty/local
에서 다음을 실행합니다.
$ npm run build $ firebase deploy --only hosting
이제 Google Home 기기를 재부팅하여 업데이트된 로컬 홈 앱을 로드합니다. 로컬 홈 앱 버전이 1.0.3인지 확인합니다.
수정사항 테스트
이제 동일한 구성으로 스마트 홈 테스트 모음을 다시 실행하면 모든 테스트 사례가 통과한 것을 확인할 수 있습니다.
7. 축하합니다
축하합니다. 스마트 홈 및 Cloud Logging용 테스트 모음을 통해 Local Home 앱 문제를 해결하는 방법을 알아봤습니다.
자세히 알아보기
다음과 같은 작업을 해볼 수 있습니다.
- 기기에 지원되는 특성을 더 많이 추가하고 테스트 모음으로 테스트합니다.
- 각 인텐트 핸들러에 맞춤 로그를 추가하고 Cloud Logging에서 확인합니다.
- 대시보드를 만들고, 알림을 설정하고, 프로그래매틱 방식으로 측정항목 데이터에 액세스하여 통합에 관한 유용한 사용량 측정항목을 확인합니다.
또한 사용자에게 통합을 게시하기 위한 인증 프로세스 등 통합을 테스트 및 제출하는 과정도 자세히 알아볼 수 있습니다.