Skip to content

리액트 애플리케이션과 다이얼로그플로 에이전트 연결하기

소규모 프로젝트를 기획하거나 기업에서 채팅 도우미(chat assistant)를 도입하려 할 때, 검색 리스트에 표시되는 가장 위에 오는 옵션 중 하나가 다이얼로그플로(Dialogflow)일 것입니다. 다이얼로그플로는 텍스트나 음성 입력을 처리하는 기술, 웹훅(webhook)을 이용해 동적인 대답을 제공하는 기술, 구글 어시스턴트를 이용해 100만 개의 구글 지원 장치에 연결하는 기술 같은 여러 기능을 제공합니다. 하지만 에이전트를 디자인하고 관리하기 위해 제공되는 다이얼로그플로의 콘솔과 별개로 웹 애플리케이션에서는 어떻게 가상 채팅 도우미를 사용할 수 있을까요?

다이얼로그플로Dialogflow는 음성이나 텍스트 형식의 자연어 입력을 처리하는 대화형 채팅 도우미의 디자인과 제작 과정을 간단히 할 수 있는 플랫폼입니다. 다이얼로그플로를 이용한 자연어 처리 채팅 도우미는 자체 콘솔에서도 이용할 수 있고, 웹 애플리케이션과 함께 이용할 수도 있습니다. 물론 이 글에서는 다이얼로그플로 에이전트에 대해 간단하게 설명하겠지만 Node.js와 다이얼로그플로에 대해서 어느 정도 이해하고 있는 독자를 대상으로 했습니다. 만일 다이얼로그플로를 처음 배운다면 이 기사를 통해 먼저 다이얼로그플로의 정의와 개념을 이해하면 됩니다.

이 글은 React.js 웹 애플리케이션과 다이얼로그플로 에이전트를 연결하는 Express.js 백엔드 애플리케이션을 사용해 어떻게 다이얼로그플로를 웹 애플리케이션에서 운영할 수 있는지에 대한 가이드입니다. 이 기사를 다 읽고 나면 여러분은 다이얼로그플로 에이전트를 각자의 웹 애플리케이션에 연결할 수 있게 될 것입니다.

밑에 있는 링크들 중 가장 관심이 가는 부분으로 건너뛰어도 괜찮고, 차례대로 읽어도 좋습니다.

 

  1. 다이얼로그플로 에이전트 세팅하기
  2. 다이얼로그플로 에이전트 통합하기
  3. Node Express 애플리케이션 세팅하기
  1. 웹 애플리케이션에 에이전트 통합하기

 

1.다이얼로그플로 에이전트 세팅하기

에이전트다이얼로그플로의 채팅 도우미를 일컫는 말로 인텐트intents, 풀필먼트fulfillment, 지식 기반knowledge base 같은 여러 요소들로 구성되어 있습니다. 다이얼로그플로에서 제공하는 콘솔로 우리는 에이전트의 대화 흐름을 만들고, 훈련시키고, 디자인할 수 있습니다. 우리는 내보내기 및 가져오기Export and import 기능을 사용해 이미 훈련된 에이전트를 ZIP 폴더 형식으로 내보낸 뒤 다른 곳에 저장할 것입니다.

다른 곳에 저장하기에 앞서 우리는 훈련된 에이전트와 병합될 새 에이전트를 만들어야 합니다. 콘솔에서 새 에이전트를 만들기 위해서는 고유한 이름, 에이전트와 연결할 구글 클라우드 프로젝트가 필요합니다. 구글 클라우드에 연결할 프로젝트가 아무것도 없다면 여기에서 새 프로젝트를 만들어 사용하면 됩니다.

사용자의 예산에 맞는 와인 제품을 추천하도록 훈련된 에이전트를 내보낸 ZIP 파일을 여기에서 다운로드합니다. 그후 Settings 페이지에 있는 내보내기 및 가져오기 탭을 눌러 새로 만든 에이전트에 방금 다운로드한 폴더를 저장합니다.

Restoring a previously exported agent from a ZIP folder
ZIP 폴더 형식으로 내보내진 에이전트를 다시 저장합니다.

 

추가된 에이전트는 사람들의 예산에 맞는 와인을 추천하도록 미리 훈련된 에이전트입니다.

Intents 페이지에 쓰여 있듯 이 에이전트에는 세 개의 인텐트가 생성돼 있습니다. 에이전트가 사용자의 입력을 인식하지 못할 때 사용되는 fallback 인텐트, 대화가 시작한 뒤 사용되는 Welcome 인텐트, 가격 매개변수amount parameter를 바탕으로 와인을 추천할 때 사용되는 get-wine-recommendation 인텐트가 그것입니다. 그중 get-wine-recommendation인텐트를 좀더 살펴봅시다.

get-wine-recommendation인텐트는 Default Welcome intent에서 오는 wine-recommendation 컨텍스트context를 가지고 있습니다.

 

컨텍스트는 인텐트들 간 흐름을 제어하는 데 사용되는 에이전트의 한 시스템입니다.

 

contexts 탭 아래에는 사용자가 쓸 법한 문장들을 모아놓은 Training phrases 탭이 있으며 에이전트를 훈련할 때 사용합니다. 에이전트는 인텐트와 관련된 다양한 문장들을 학습해 사용자의 문장을 인식하고 그 문장과 관련된 인텐트를 식별할 수 있게 됩니다.

get-wine-recommendation 인텐트에 속한 훈련용 문장들은 와인 선택과 가격 카테고리를 담고 있습니다.

List of available training phrases with get-wine-recommendation intent.
훈련용 문장을 보여주는 get-wine-recommendation 인텐트 페이지

 

위 이미지에서 우리는 사용 가능한 훈련용 문장들이 나열된 것을 볼 수 있으며, 통화 수치가 각각 노란색으로 강조 표시되어 있는 것도 확인할 수 있습니다. 이 강조 표시는 사용자가 입력한 문장들 중 엔터티entity를 뽑아내기 위해 다이얼로그플로가 자동으로 표시하는 주석annotation입니다.

에이전트와 대화할 때 인텐트가 매칭되면, Intents 탭 아래 있는 Fulfillment 탭에 있는 활성화된 웹훅webhook으로 HTTP 요청이 만들어집니다. 이 HTTP 요청은 사용자의 문장에서 제시된 가격 매개변수와 맞는 와인을 가져오기 위한 것입니다.

다이얼로그플로 콘솔 오른쪽에 있는 다이얼로그플로 에뮬레이터emulator를 통해 우리는 에이전트를 테스트해볼 수 있습니다. “Hi”라는 메시지를 보내 대화를 시작하고, 원하는 와인 가격을 말해봅시다. 웹훅이 즉시 호출돼 아래와 비슷한 답변들이 보일 것입니다.

Testing the imported agent agent webhook.
콘솔에 있는 에이전트 에뮬레이터로 가져온 에이전트의 풀필먼트fulfillment 웹훅을 테스팅하고 있습니다.

 

위 이미지에서는 Ngrok을 통해 웹훅 URL이 생성되는 것과 사용자가 입력한 가격인 20달러에 맞는 와인을 추천해주는 답변을 확인할 수 있습니다.

이제 다이얼로그플로 에이전트는 완벽하게 세팅되었습니다. 우리는 이 에이전트를 웹 애플리케이션에 통합해 다른 사용자들이 다이얼로그플로 콘솔을 사용하지 않고도 에이전트에 액세스해 상호작용할 수 있도록 만들 것입니다.

 

2.다이얼로그플로 에이전트 통합하기

HTTP 요청을 REST 엔드포인트에 보내는 등 다이얼로그플로 에이전트에 연결할 방법은 많지만, 그중 몇몇 언어에서 제공되는 공식 클라이언트 라이브러리를 사용하는 방법을 제일 먼저 추천합니다. 자바스크립트의 경우 NPM에서 @google-cloud/dialogflow 패키지를 설치할 수 있습니다.

내부적으로 @google-cloud/dialogflow 패키지는 gRPC로 네트워크에 연결하기 때문에 webpack이 패치된 경우를 제외하고는 브라우저 환경에서 지원되지 않습니다. 따라서 Node 환경에서 이 패키지 사용을 추천합니다. 이제부터 Express.js 백엔드 애플리케이션을 설치한 뒤 API 엔드포인트를 통해 웹 애플리케이션에 데이터를 제공하는 방법을 살펴봅시다.

 

3.Node Express 애플리케이션 세팅하기

express 애플리케이션을 세팅하기 위해서는 새 프로젝트 디렉터리를 만들고, 커맨드라인 터미널에서 yarn을 사용해 필요한 dependencies를 추가해야 합니다.

See the Pen <a href=’https://codepen.io/books-webactually/pen/OJbRWzr’>OJbRWzr</a> by webactually (<a href=’https://codepen.io/books-webactually’>@books-webactually</a>) on <a href=’https://codepen.io’>CodePen</a>.

필요한 dependencies들이 설치되면 웹 앱에 대해 CORS 지원을 활성화하고, 지정된 포트의 연결을 처리하는 매우 간단한 Express.js 서버를 세팅합니다.

See the Pen <a href=’https://codepen.io/books-webactually/pen/yLVagjY’>yLVagjY</a> by webactually (<a href=’https://codepen.io/books-webactually’>@books-webactually</a>) on <a href=’https://codepen.io’>CodePen</a>.

위에 있는 코드는 특정한 포트 연결을 기다리는 HTTP 서버를 구동하기 시작합니다. 이 HTTP 서버는 Express미들웨어cors 패키지를 사용함으로써 모든 요청에 대해 Cross-origin resource Sharing(CORS)을 지원합니다. 현재 이 서버는 라우트가 없기 때문에 요청을 받아들이기만 할 뿐 요청에 응답하지는 않습니다. 이제 라우트를 만들어봅시다.

두 개의 라우트를 추가해봅시다. 하나는 텍스트 데이터를 전송하는 용도이고, 하나는 녹음된 음성 데이터를 전송하는 용으로 사용될 것입니다. 이 두 라우트 모두 POST 요청을 받는데 나중에 요청 보디request body에 있는 데이터를 다이얼로그플로 에이전트에 전달할 때에도 사용됩니다.

See the Pen <a href=’https://codepen.io/books-webactually/pen/LYbRxJe’>LYbRxJe</a> by webactually (<a href=’https://codepen.io/books-webactually’>@books-webactually</a>) on <a href=’https://codepen.io’>CodePen</a>.

위에서 POST를 위한 두 개의 라우트 인스턴스를 만들었습니다. 지금 이 라우트들은 상태 코드 200과 하드 코딩으로 직접 쓴 말을 반환합니다. 다이얼로그플로 인증이 완료되면, 우리는 이 엔드포인트에서 다이얼로그플로와의 실질적 연결을 구현할 것입니다.

마지막으로 app.use와 라우트의 기본 경로path를 설정해 방금 만든 라우트 인스턴스를 Express 애플리케이션 파일 시스템에 편입해야 합니다.

See the Pen <a href=’https://codepen.io/books-webactually/pen/KKNgaew’>KKNgaew</a> by webactually (<a href=’https://codepen.io/books-webactually’>@books-webactually</a>) on <a href=’https://codepen.io’>CodePen</a>.

위에서 우리는 두 라우트를 위한 기본 경로를 추가했습니다. 커맨드라인에서 curl 커맨드를 통해 빈 요청을 보내 테스트해봅시다.

See the Pen <a href=’https://codepen.io/books-webactually/pen/dyOpNKd’>dyOpNKd</a> by webactually (<a href=’https://codepen.io/books-webactually’>@books-webactually</a>) on <a href=’https://codepen.io’>CodePen</a>.

위의 요청이 성공적으로 완료되면, 객체 데이터가 포함된 응답을 콘솔에서 볼 수 있습니다.

이제 에이전트 인증을 처리하고 @google-cloud/dialogflow 패키지를 이용해 데이터를 송신 및 수신할 수 있도록 다이얼로그플로와 실제로 연결해보겠습니다.

 

3-1.다이얼로그플로로 인증하기

모든 다이얼로그플로 에이전트는 구글 클라우드에 있는 프로젝트와 연결되어 있습니다. 따라서 다이얼로그플로 에이전트를 외부에서 연결하려면 구글 클라우드의 프로젝트를 인증하고 다이얼로그플로를 프로젝트 리소스 중 하나로 사용해야 합니다. 구글 클라우드 프로젝트에 연결할 수 있는 여섯 가지 방법 중 클라이언트 라이브러리를 통해 구글 클라우드의 특정 서비스에 연결할 때는 서비스 계정을 사용하는 방법이 가장 편리합니다.

참고: 상품화할 애플리케이션의 경우에는 서비스 계정의 키key가 다른 곳으로 잘못 전달될 수도 있기 때문에 서비스 계정 키를 사용하는 것보다는 수명이 짧은 API 키를 사용하는 것이 더 좋습니다.

 

3-2.서비스 계정이란?

서비스 계정은 외부 API를 통한 상호작용처럼 사람이 아닌 것과의 상호작용을 위해 생성된 구글 클라우드의 특수한 계정 유형입니다. 애플리케이션에서 서비스 계정은 구글 클라우드 인증을 위해 다이얼로그플로 클라이언트 라이브러리에서 생성된 키를 통해 액세스됩니다.

서비스 계정 생성 및 관리에 대한 클라우드 문서는 서비스 계정 생성에 대한 훌륭한 가이드를 제공하고 있습니다. 서비스 계정을 만드는 마지막 과정에서 서비스 계정에 Dialogflow API Admin 역할을 할당해야 합니다. 이 역할을 통해 서비스 계정은 연결된 다이얼로그플로에 대한 관리 및 제어 권한을 가지게 됩니다.

서비스 계정을 사용하기 위해서는 서비스 계정 키Service Accout Key가 필요합니다. 아래와 같은 과정을 통해 서비스 계정 키를 JSON 형식으로 만들 수 있습니다.

 

  1. Service Account 페이지를 살펴보기 위해 새로 만든 서비스 계정을 클릭합니다.
  2. Keys 섹션으로 스크롤을 내린 뒤 Add Key 드롭다운을 클릭하고, Create new key 옵션을 클릭합니다.
  3. JSON 파일 형식을 선택하고 오른쪽 하단에 Create 버튼을 클릭합니다.

 

참고: 서비스 계정 키는 구글 클라우드 프로젝트에 대한 아주 민감한 데이터를 가지고 있기 때문에 비공개로 유지하고 어떤 버전 컨트롤 시스템에도 저장하지 않는 것이 좋습니다. 버전 컨트롤 시스템에 저장하지 않으려면 .gitignore 파일에 추가하면 됩니다.

서비스 계정이 만들어졌고, 프로젝트 디렉터리 내에 서비스 계정 키도 있다면 이제 다이얼로그플로 클라이언트 라이브러리를 이용해 다이얼로그플로 에이전트와 데이터를 주고받을 수 있습니다.

See the Pen <a href=’https://codepen.io/books-webactually/pen/dyOpNQe’>dyOpNQe</a> by webactually (<a href=’https://codepen.io/books-webactually’>@books-webactually</a>) on <a href=’https://codepen.io’>CodePen</a>.

위에 있는 모든 라우트는 다음과 같은 과정을 통해 다이얼로그플로 에이전트와 데이터를 주고받습니다.

  1. 구글 클라우드를 인증한 다음, 다이얼로그플로 에이전트와 연결된 구글 클라우드 프로젝트의 프로젝트 ID와 세션을 식별하기 위한 random ID 를 활용해 다이얼로그플로로 세션을 만듭니다. 애플리케이션에서는 자바스크립트 UUID 패키지를 활용해 만들어진 각 섹션에 UUID 식별자를 생성합니다. 이는 다이얼로그플로 에이전트가 처리하는 모든 대화를 기록하거나 추적할 때 매우 유용합니다.
  2. 다이얼로그플로 도큐먼테이션에 따라 요청 객체request object 데이터를 만듭니다. 이 요청 객체는 앞에서 생성된 세션과 다이얼로그플로 에이전트로 보낼 요청 보디에 있는 메시지를 포함합니다.
  3. 다이얼로그플로 세션에 있는 detectIntent 메서드를 사용하여 요청 객체를 비동기적으로 보냅니다. 그리고 try-catch 블록에서 ES6의 async/await 구문을 사용해 에이전트의 응답을 기다립니다. 만일 detectIntent가 예외를 반환한다면 우리는 전체 응용 프로그램을 중단하지 않지만 대신 이 에러를 catch하고 리턴합니다. 에이전트의 응답 객체 샘플은 다이얼로그플로 도큐먼테이션에서 볼 수 있고, 이를 통해 객체에서 데이터를 추출하는 방법을 더 자세히 살펴볼 수 있습니다.

 

우리는 포스트맨Postman으로 위에서 구현한 dialogflow-response 라우트의 다이얼로그플로 커넥션을 테스트해볼 수 있습니다. 포스트맨은 개발 또는 프로덕션 단계에서 만들어진 API를 테스트할 수 있는 API 개발용 협업 플랫폼입니다.

참고: 지금은 포스트맨 데스크톱 애플리케이션을 따로 설치하지 않아도 됩니다. 2020년 9월부터 포스트맨의 웹 클라이언트가 배포되어 브라우저에서 직접 사용할 수 있습니다.

포스트맨 웹 클라이언트Web Client를 사용하여 새 작업 공간을 만들거나 기존 작업 공간을 사용해 우리가 만든 API 엔드포인트 http://localhost:5000/api/agent/text-inputPOST 요청을 만들고 messagekey와 “Hi There” 같은 값value을 쿼리의 파라미터로 추가합니다. Send 버튼을 클릭하면 Express 서버에 POST 요청을 보내 아래 이미지와 같은 응답이 표시됩니다.

Testing the text-input API endpoint using Postman.
포스트맨을 사용하여 텍스트 입력 API 엔드포인트를 테스트합니다.

 

위 이미지에서 Express 서버를 통해 다이얼로그플로 에이전트의 응답 데이터를 볼 수 있습니다. 반환된 데이터의 형식은 다이얼로그플로 웹훅 문서에 제공된 샘플 응답 정의에 따라 지정됩니다.

 

3-3.음성 입력 처리

기본적으로 모든 다이얼로그플로 에이전트들은 텍스트와 오디오 데이터를 처리할 수 있고 텍스트와 오디오 형식으로 응답을 반환할 수 있습니다. 그러나 오디오 입출력 데이터를 작업하는 것은 텍스트 입출력 데이터를 작업하는 것보다 약간 더 복잡할 수 있습니다.

음성 입력을 받고 다이얼로그플로로 보내 에이전트로부터 응답을 받기 위해 이전에 생성한 /voice-input 엔드포인트에 대한 구현부터 시작해봅시다.

See the Pen <a href=’https://codepen.io/books-webactually/pen/dyOpNwe’>dyOpNwe</a> by webactually (<a href=’https://codepen.io/books-webactually’>@books-webactually</a>) on <a href=’https://codepen.io’>CodePen</a>.

 

개략적으로 살펴보면 위에 나온 /voice-input 라우트는 사용자가 채팅 도우미에게 하는 말이 담긴 음성 입력을 파일로 받은 뒤 다이얼로그플로 에이전트에 보냅니다. 이 과정들을 다음과 같이 더 잘게 나눠 꼼꼼하게 이해해봅시다.

  • 먼저 웹 애플리케이션이 전송한 요청 속 폼form 데이터를 파싱parsing하기 위해 connect-busboyExpress 미들웨어로 추가합니다. 서비스 키를 사용하여 다이얼로그플로를 인증하고 이전에 했던 것과 동일한 방식으로 세션을 만듭니다. 그후 기본으로 제공되는 Node.js util 모듈의 promisify 메서드를 사용하여 Stream pipeline 메서드와 똑같이 나중에 여러 개의 스트림을 연결해 처리하고pipe 스트림이 완료된 후 정리를 수행하는 데 필요한 promise를 가져와 저장합니다.
  • 다음으로 다이얼로그플로 인증 세션과 다이얼로그플로로 보낼 오디오 파일로 요청 객체를 만듭니다. 이때 중첩된 오디오 객체를 활용해 다이얼로그플로 에이전트가 전송된 오디오 파일에 대해 Speech-To-Text 변환을 수행할 수 있도록 하였습니다.
  • 이제 다이얼로그플로 에이전트에서 백엔드 애플리케이션으로 새 데이터 스트림을 여는 detectStreamingIntent 메서드를 사용합니다. 이 메서드로 생성된 세션과 요청 객체를 활용하여 오디오 파일 속 사용자의 인텐트를 감지합니다. 데이터는 새로 열린 스트림을 통해 작은 비트로 다시 전송되며, 스트림 데이터 이벤트를 통해 나중에 다시 사용할 데이터를 streamData 변수에 저장합니다. 스트림이 닫히면 “end” 이벤트가 시작되고, 다이얼로그플로 에이전트에서는 streamData 변수에 저장된 응답을 웹 애플리케이션으로 보냅니다.
  • 마지막으로 connect-busboy의 파일 스트림 이벤트를 사용해 전송된 요청 보디에 오디오 파일 스트림을 받고, 이전에 생성한 promise에 전달합니다. 이 과정은 요청에서 들어오는 오디오 파일 스트림을 다이얼로그플로 스트림으로 연결해 처리하고, 오디오 파일 스트림을 위의 detectStreamingIntent 메소드를 통해 열린 스트림으로 연결해 처리하는 것입니다.

 

위의 단계가 잘 작동하는지 테스트하고 확인하기 위해 포스트맨을 사용하여 /voice-input 엔드포인트로 오디오 파일이 보디에 포함된 요청을 보내봅시다.

Testing the voice-input API endpoint using Postman.
포스트맨으로 녹음된 음성 파일을 이용해 voice-input API 엔드포인트를 테스트합니다.

 

위의 포스트맨 결과는 요청 보디에 “Hi”라는 메시지가 담긴 녹음 파일을 포함한 POST 요청에 대한 응답입니다.

이제 다이얼로그플로로 데이터를 송수신하는 Express.js 애플리케이션이 생겼습니다. 이제 React.js 애플리케이션에서 생성된 API를 사용하여 에이전트를 웹 애플리케이션에 통합해봅시다.

 

4.웹 애플리케이션 에이전트 통합하기

이미 만들어진 REST API를 사용하기 위해 우리는API에서 가져온 와인 목록을 홈페이지에 보여주고, babel proposal decorators plugin을 사용한 데코레이터를 지원하도록 만들어진 React.js 애플리케이션을 확장할 것입니다. 상태 관리를 위해 MobX를 도입하고, Express.js 애플리케이션에 추가된 REST API 엔드포인트를 사용하여 챗 컴포넌트component에서 와인을 추천하는 새로운 기능을 도입해 웹 애플리케이션을 조금 개선할 것입니다.

우선 몇몇 observable 값과 actions 같은 일부 메서드로 Mob 스토어store를 만들어 MobX로 애플리케이션의 상태 관리를 시작해봅시다.

See the Pen <a href=’https://codepen.io/books-webactually/pen/xxREgBP’>xxREgBP</a> by webactually (<a href=’https://codepen.io/books-webactually’>@books-webactually</a>) on <a href=’https://codepen.io’>CodePen</a>.

 

위에서 우리는 다음 값을 가진 응용 프로그램의 챗 컴포넌트 피처feature를 담는 스토어를 만들었습니다.

 

isChatWindowOpen

이곳에 저장된 값들은 다이얼로그플로의 메시지가 표시되는 챗 컴포넌트의 가시성을 제어합니다.

isLoadingChatMessages

다이얼로그플로 에이전트에서 만든 응답을 가져올 때 나타나는 로딩 표시기를 보여주는 데 사용됩니다.

agentMessages

이 배열은 다이얼로그플로 에이전트에서 응답받기 위한 모든 요청들에 대한 응답을 저장합니다. 이 배열의 데이터는 나중에 컴포넌트에 표시됩니다.

handleConversation

액션으로 데코레이트된 이 메서드는 agentMessages 배열에 데이터를 추가합니다. 우선 매개변수로 넘어온 사용자의 메시지를 추가하고, 다이얼로그플로의 응답을 받기 위해 Axios로 백엔드 애플리케이션에 보낼 요청을 만듭니다. 요청이 처리된 이후에 요청의 응답을 agentMessages 배열에 추가합니다.

참고: 애플리케이션에서 데코레이터 지원이 없는 경우를 위해 MobX는 타깃 스토어 클래스의 생성자에서 사용할 수 있는 makeObservable을 제공합니다. 여기에서 예시를 참조하세요.

스토어 설정을 통해 index.js 파일의 루트 컴포넌트부터 전체 애플리케이션까지를 MobX Provider higher-order component 로 감싸야 합니다.

See the Pen <a href=’https://codepen.io/books-webactually/pen/mdOrRga’>mdOrRga</a> by webactually (<a href=’https://codepen.io/books-webactually’>@books-webactually</a>) on <a href=’https://codepen.io’>CodePen</a>.

 

위에서 우리는 MobX Provider로 루트 앱 컴포넌트를 감싸고, 이전에 만든 스토어를 Provider 값 중 하나로 전달했습니다. 이제 스토어에 연결된 컴포넌트를 통해 계속해서 스토어를 사용할 수 있습니다.

 

4-1.채팅 인터페이스 만들기

API 요청을 통해 보내거나 받은 메시지를 표시하려면 나열된 메시지를 표시하는 채팅 인터페이스가 있는 새 컴포넌트가 필요합니다. 이를 위해 우리는 먼저 하드 코딩된 메시지를 표시하는 컴포넌트를 만든 다음 그걸 이용해 메시지를 표시할 것입니다.

See the Pen <a href=’https://codepen.io/books-webactually/pen/QWGKdRb’>QWGKdRb</a> by webactually (<a href=’https://codepen.io/books-webactually’>@books-webactually</a>) on <a href=’https://codepen.io’>CodePen</a>.

 

위에 있는 컴포넌트에는 챗 애플리케이션을 위한 기본적인 HTML 마크업이 있습니다. 에이전트 이름을 보여주는 헤더가 있고 채팅 창을 닫기 위한 닫기 아이콘과 하드 코딩된 텍스트를 담고 있는 메시지 말풍선, 리액트의 useState를 사용해 컴포넌트의 로컬 상태에 입력한 텍스트를 저장하기 위해 onChange 이벤트 핸들러가 연결된 입력 필드가 있습니다.

A preview of the Chat Component with a hard coded message from the chat Agent
하드 코딩된 메시지가 포함된 챗 에이전트의 챗 컴포넌트 미리 보기

 

챗 컴포넌트가 제대로 작동하여 하나의 채팅 메시지와 입력 필드가 있는 챗 창이 표시됩니다. 이제 하드 코딩된 텍스트가 아닌 API 요청에서 받은 실제 응답으로 메시지가 표시되도록 해봅시다.

이번에는 컴포넌트의 MobX 스토어에 저장된 값을 이용해 챗 컴포넌트를 개선합니다.

See the Pen <a href=’https://codepen.io/books-webactually/pen/LYbRxod’>LYbRxod</a> by webactually (<a href=’https://codepen.io/books-webactually’>@books-webactually</a>) on <a href=’https://codepen.io’>CodePen</a>.

 

위 코드에서 강조 표시된 부분들을 보면 이제 모든 챗 컴포넌트가 아래와 같은 새로운 작업을 수행할 수 있도록 수정되었음을 알 수 있습니다.

 

  • ApplicationStore에 값을 삽입한 뒤 MobX 스토어에 저장된 값들을 액세스할 수 있습니다. 스토어에 저장된 값들이 변경되면 다시 렌더링하기 위해 이 스토어 값들을 감시하는 컴포넌트도 만들었습니다.
  • 챗 컴포넌트가 열리면 useEffect 후크 내에서 handleConversation 메서드를 호출해 컴포넌트가 사용자 화면에 보여지는 즉시 요청을 만들기 때문에 바로 에이전트와 대화를 시작할 수 있습니다.
  • 챗 컴포넌트 헤더에 있는 isLoadingMessages 값들을 사용합니다. 에이전트에 응답을 받기 위한 요청을 보내며, isLodaingMessages 값을 true로 설정하고 헤더를 ‘Zara is typing…’으로 업데이트합니다.
  • 값들이 설정되면 스토어에 있는 agentMessages 배열이 MobX를 통해 프록시로 업데이트됩니다. 이 컴포넌트에서 MobX의 toJS 유틸리티를 사용해 프록시를 다시 배열로 변환하고 컴포넌트 내의 변숫값을 저장합니다. 이 배열은 map 함수를 사용해서 채팅 말풍선들을 채우기 위해 나중에 추가로 반복됩니다.

 

이제 우리는 챗 컴포넌트를 사용해 문장을 작성하고 에이전트에 답변이 보일 때까지 기다리면 됩니다.

Chat component showing a list data returned from the HTTP request to the express application.
Express 애플리케이션에 HTTP 요청으로부터 반환된 리스트 데이터를 보여주는 챗 컴포넌트

 

4-2.사용자 음성 입력 녹음하기

기본적으로 모든 다이얼로그플로 에이전트들은 모든 언어의 음성이나 텍스트 기반 인풋을 사용자로부터 받습니다. 그러나 사용자의 마이크에 액세스하고 음성 입력을 녹음하려면 웹 애플리케이션에서 몇 가지를 조정해야 합니다.

HTML MediaStream Recording API를 사용해 MobX 스토어에 있는 두 가지 새로운 방법으로 사용자의 음성을 녹음하도록 MobX의 스토어를 수정했습니다.

See the Pen <a href=’https://codepen.io/books-webactually/pen/eYBdgqv’>eYBdgqv</a> by webactually (<a href=’https://codepen.io/books-webactually’>@books-webactually</a>) on <a href=’https://codepen.io’>CodePen</a>.

챗 컴포넌트에서 녹음 아이콘을 클릭하면 MobX 스토어에 있는 StartAudioConversation 메서드가 호출되어 챗 컴포넌트가 시각적인 피드백을 제공하도록 isRecording을 true로 설정합니다.

미디어 디바이스Media Device 객체는 브라우저의 내비게이터navigator 인터페이스를 사용하여 사용자의 마이크를 요청합니다. getUserMedia 요청에 권한이 부여된 후, 사용자의 마이크에서 반환된 스트림의 media tracks를 사용해 recorder를 만들기 위해 MediaRecorder 생성자에게 전달할 미디어 스트림 데이터로 프로미스promise를 이행합니다. 그런 다음 MediaRecorder 객체를 스토어의 recorder에 저장합니다. recorder 는 나중에 다른 메서드들에서도 접근할 것입니다.

그런 다음 recorder 객체에서 start 메서드를 호출하고, 리코딩 세션이 끝나면 recordedBits 배열 프로퍼티에 저장될 Blob에서 스트림이 포함된 이벤트 인수와 함께 ondataavailable 함수가 시작됩니다.

ondataavailable 이벤트로 넘어간 이벤트 매개변수의 데이터를 로깅하면 Blob과 Blob의 프로퍼티들을 브라우저 콘솔에서 볼 수 있습니다.

Browser Devtools console showing logged out Blob created by the Media Recorder after a recording is ended.Pull Quotes
기록이 끝난 후 recorder에서 생성된 Blob의 로그를 브라우저 개발자 도구 콘솔이 보여줍니다.

 

이제 우리는 MediaRecoder 스트림을 시작할 수 있습니다. 사용자가 녹음을 끝내면 MediaRecorder 스트림을 끝내고 생성된 음성 파일을 Express.js 애플리케이션에 보내야 합니다.

아래 스토어에 추가된 새 메서드는 스트림을 중지하고 녹음된 음성 입력을 포함하는 POST 요청을 만듭니다.

See the Pen <a href=’https://codepen.io/books-webactually/pen/KKNgWPz’>KKNgWPz</a> by webactually (<a href=’https://codepen.io/books-webactually’>@books-webactually</a>) on <a href=’https://codepen.io’>CodePen</a>.

 

위의 메서드는 MediaRecorder의 stop 메서드를 실행하여 활성화된 스트림을 중지합니다. MediaRecorder가 중지된 후 시작된 onstop 이벤트 내에서 음악 유형으로 새 Blob을 만들고, 이 Blob을 FormData에 추가합니다.

마지막 단계로 Blob을 보디에 포함한 POST 요청을 만들고 요청 헤더에는 Content-Type: multipart/formdata를 추가해 파일이 백엔드 서비스 애플리케이션의connect-busboy 미들웨어로 파싱parse될 수 있도록 합니다.

MobX 스토어에서 녹음을 수행할 때 챗 컴포넌트에 추가해야 할 것은 MobX 작업을 실행하여 사용자의 음성 녹음을 실행하고 중지하는 버튼과 녹음 세션이 활성화될 때 표시할 텍스트뿐입니다.

See the Pen <a href=’https://codepen.io/books-webactually/pen/dyOpvbj’>dyOpvbj</a> by webactually (<a href=’https://codepen.io/books-webactually’>@books-webactually</a>) on <a href=’https://codepen.io’>CodePen</a>.

 

위의 챗 컴포넌트 헤더에서 강조 표시된 부분에 ES6 삼항 연산자를 사용하여 음성 입력이 녹음되고, 백엔드 애플리케이션으로 전송될 때마다 텍스트를 “Zara is listening …”으로 전환합니다. 이것은 사용자에게 수행 중인 작업에 대한 피드백을 제공합니다.

또한 텍스트 입력 외에도 채팅 도우미 사용 시 텍스트 및 음성 입력 옵션을 사용할 수 있음을 사용자에게 알리기 위해 마이크 아이콘을 추가했습니다. 만일 사용자가 텍스트 입력을 사용하기로 결정한다면 저장된 텍스트의 길이를 세고 삼항 연산자로 마이크 버튼을 Send 버튼으로 전환합니다.

음성 및 텍스트 입력을 모두 사용하여 새로 연결된 채팅 도우미를 몇 번 테스팅해보면 다이얼로그플로 콘솔을 사용할 때와 똑같은 답변을 얻는 것을 확인할 수 있습니다!

 

5.결론

앞으로 몇 년 동안은 공공 서비스에서 채팅 도우미가 주요하게 사용될 것입니다. 이 문서에서는 다이얼로그플로로 만든 채팅 도우미 중 하나를 백엔드 애플리케이션을 사용해 자체 웹 애플리케이션에 통합하는 방법에 대한 기본 가이드를 제공했습니다. 이렇게 만들어진 애플리케이션은 네틀리파이Netlify를 사용해 배포했으며 이곳에서 확인하실 수 있습니다. 프로젝트에 대한 상세한 README 파일이 포함되어 있으니 백엔드 express application과 React.js 웹 애플리케이션을 깃허브에서 살펴보는 것도 도움이 될 것입니다.

 

6.참고 자료

 

도서 소개

 

 

 

 

 

제레미 키스의 《서비스 워커로 만드는 오프라인 웹사이트

웹은 열린 공간이고 어디에서나 접근할 수 있다지만 반드시 네트워크에 연결돼야 하는 치명적인 약점을 갖고 있다. 5G시대에 우리 대부분은 너무나 빠르고 놀랍도록 안정적인 환경을 이용하고 있기 때문에 네트워크로 인해 비롯되는 문제를 간과하기 쉽다. 고품질의 네트워크 연결을 당연시하고 전 세계 모든 사람이 웹에서 같은 경험을 누리고 있다고 생각하는 것이다.
하지만 네트워크 연결이 불안정한 조건에서는 웹의 존재가 흐려지고, 페이지가 망가지고, 기능이 실행되지 않고, 이미지가 사라지고, 이로 인해 상처 입고, 좌절하기에 이른다. 웹의 이런 네트워크 의존성을 극복하기 위해 오래전부터 많은 사람이 노력해왔고, 마침내 그 해결책으로 ‘서비스 워커(SERVICE WORKER)’가 등장했다.

저작권 정보이 글은 Smashing Magazine 기사를 번역한 것입니다. 저작권자의 정당한 허락을 받은 저작물로 한국어판 저작권은 웹액츄얼리에 있습니다. 웹액츄얼리의 서면 동의 없이 무단 전재, 복제를 금합니다. 원본은 Integrating A Dialogflow Agent Into A React Application에서 확인할 수 있습니다.
참여를 기다립니다!웹액츄얼리에서 웹디자인 관련 영문 번역자를 찾습니다. 웹 콘텐츠 번역에 관심 있는 분은 메일로 간략한 본인 소개와 번역 이력을 보내주시면 연락드리겠습니다.
books@webactually.com

 

 

Smashing Magazine 기사 모아보기



맨위로