About Dr.diary
Home
👍🏻

카카오톡으로 혈당 관리를 할 수 있다고?

축! 닥터다이어리 챗봇 탄생!
목차
카카오톡 챗봇 개발 스토리 – 카카오톡 챗봇 개발은 어떻게 시작하게 되었는가? – 카카오톡 챗봇은 어떤거죠?
카카오톡 오픈빌더와 서버 연결하기 – 스킬에 대해서 알아보자! – 서버에서 스킬요청을 처리하는 예시
유기적 챗봇 설계 Tip– 블록간의 상호 작용을 위한 컨텍스트 활용방법– 조건별 응답을 위한 응답 JSON 처리 방법– 테스트 채널과 라이브 채널 운용을 통한 배포 과정– 엑스트라 활용하기!– Event API로 특정 조건 응답 전송하기– 자연스럽게 순환할 수 있는 구조 설계
챗봇 개발 후기 및 차후 계획

카카오톡 챗봇 개발 스토리

[카카오톡 챗봇 개발은 어떻게 시작하게 되었는가?]

카카오톡 챗봇은 “어떻게하면 보다 혈당 기록을 쉽게 남길 수 있을까?”라는 생각에서 시작되었습니다. 혈당관리에서 가장 중요한 것은 주기적으로 혈당을 기록하는 것인데… 지속적으로 혈당 기록을 하는 것은 참으로 어려운 일이죠. 그래서 개발팀과 기획팀은 생각했습니다. 유저가 앱을 떠나서도 일상 생활을 하면서 혈당 기록에 대한 자극을 받고 보다 쉽게 기록을 남길 수 있는 방법은 없을까?
카카오 i 오픈빌더로 제작된 카카오톡 챗봇 서비스 예시
카카오톡 챗봇에서 발화는 요청(request), 블록은 컨트롤러(controller), 메시지은 응답(response)의 역할을 하게 됩니다. 그래서 챗봇을 개발하면서 유저가 특정 발화를 하도록 유도하여 올바른 요청이 블록으로 갈 수 있도록 만들고, 블록은 들어온 요청을 잘 분석하여 필요한 데이터를 가공하고, 메시지 응답으로 유저가 원하는 내용을 보여주는 것이 핵심이라 할 수 있습니다.
우리가 개발을 하면서 특정 API를 사용하면 올바른 요청을 보내야하는 것이 중요한 만큼, 챗봇을 설계할 때도 유저가 블록을 실행할 수 있도록 특정 발화를 하도록 유도해야합니다. 그래서 엔티티를 설정할 때는 유저 입장에서 자연스럽게 입력할 수 있으면서도 핵심적으로 요청의 속성을 구분할 수 있는 단어와 패턴을 고려해서 설정하는 것이 중요합니다. ex) “공복{혈당기록 시점} 120{수치값} 기록해줘” 자세한 내용은 오픈빌더 도움말에도 있으니 참고하시길 바랍니다.
사용자 발화 기반으로 블록의 작동 원리
블록은 마치 API를 호출했을 시 실행되는 함수와 같은 역할이며 발화문에서 필요한 파라미터를 추출할 수 있습니다. 위 이미지를 예시로 들면 “아이유 노래 틀어줘”에서 가수의 이름과 음악재생의 액션을 뽑은 것처럼 블록이 어떠한 엔티티를 파라미터로 인식하고, 어떠한 액션을 취할지를 설정해야합니다. 여기서 ‘스킬’은 외부 API를 직접 연결하여 카카오 챗봇 내부가 아닌 외부에서 데이터 처리를 한 다음에 응답값을 받을 수 있도록 하는 장치입니다. 스킬이야 말로 챗봇 활용의 꽃이라 할 수 있기 때문에 자세한 부분은 오픈빌더 연결파트와 까다로웠던 핸들링 부분에서 더 말씀드리도록 하겠습니다.
그리고 메시지 응답 같은 경우 카카오톡 플랫폼에 맞게 텍스트 메시지, 이미지, 카드, 커머스 등 평소 카카오톡을 사용하면서 많이 봐왔던 익숙한 형태의 메시지 답장형태로 보낼 수 있습니다. 다만 아쉬운 점은 스킬을 사용하지 않는다면 블록은 발화문의 종류와 상관없이 1가지 타입의 응답만을 보낼 수 있기 때문에  스킬을 적재적소에 맞게 사용하여 챗봇의 응답을 더욱 풍성하게 만드는 것이 좋습니다.

카카오톡 오픈빌더와 서버 연결하기

[스킬에 대해서 알아보자!]

지금부터 챗봇 스킬에 대해서 이야기 해보자고!
스킬은 무조건 HTTP POST 요청으로만 서버에 전송할 수 있기 때문에 query나 params가 아닌 body로만 블록 수집한 데이터를 전송할 수 있습니다. 스킬로 서버에 요청을 보냈을 때, 해당 요청에 대해서 구별할 수 있는 데이터 값들은 주로 userRequest에 있습니다. req.body.userRequest에는 아래와 같은 값들이 전달되게 됩니다.
먼저 ‘block’부터 본다면 각 블록들은 고유한 id값을 가지게 되기 때문에 서버에 스킬요청이 들어왔을 때, 블록에 따라서도 분기를 다르게 갈 수 있습니다. 그리고 ‘user’부분에는 챗봇을 사용한 유저를 구별할 수 있는 값들이 있습니다. 챗봇채널이 있는 개인 카카오앱에 다른 서비스가 연결되어 있다면 appUserId를 통해서 연결된 다른 서비스에서도 동일한 유저를 식별할 수 있기 때문에 유기적인 데이터 처리가 가능하게 됩니다. 예를 들면 카카오톡 로그인을 한 회원이 자사 앱을 사용하면서 챗봇을 통해서 앱에 있는 데이터 값을 추가하고 싶다면 appUserId를 컨트롤러에서 유저식별값을 사용해서 해당 회원의 앱 계정에 혈당 기록을 추가하는 형태로도 사용할 수 있습니다. (정말 awesome 하죠?)
실행된 블록이 발화문에서 추출한 파라미터 같은 경우 action의 params에 있고, 혹시라도 유저가 타이핑한 전체 문장을 확인하고 싶다면 발화된 시점의 유저 메시지를 ‘utterance’를 통해서도 확인할 수 있으니 스킬을 활용하는 방법은 개발자의 몫에 맡겨져 있습니다.
– 서버에서 스킬요청을 처리하는 예시
스킬을 이용해서 혈당 기록 데이터를 생성하는 예시
스킬요청에서 데이터 처리에 필요한 정보들은 ‘action’에 주로 있습니다. 위의 이미지처럼 “공복 100″을 유저가 발화를 했을 때, 기대되는 행동은 유저가 사용하는 앱 계정에 공복혈당 기록으로 100mg/dL이 들어가는 것이기 때문에 우선 블록이 발화문에서 기록 생성에 필요한 시점에 대한 데이터와 수치에 대한 데이터를 캐치하도록 만듭니다.
유저가 어떠한 행동을 할지 모르기 때문에 되묻기 질문을 최대한 활용하세요!
유저는 어떠한 값들이 있어야 스킬 전송이 가능한지 모르기 때문에 스킬 요청에 필요한 파라미터값들은 꼭 필수파라미터로 바꿔서 되묻기 질문을 추가해야합니다. 추가로 혈당값 같은 integer value는 유저가 숫자 대신 ‘백이십’과 같이 문자로 쓰거나 숫자 사이에 오타가 들어갈 수 있기 때문에 시스템 엔티티를 통해서 최대한 필터링을 할 수 있도록 만드는 것이 중요합니다.
요청이 정상적으로 전송이 되었다면 이제 요청을 받은 API의 컨트롤러에서 데이터를 처리할 단계입니다. 저 같은 경우 스킬의 이름으로 어떠한 로직을 진행할지 결정하여 데이터 생성 쿼리를 DB에 보내도록 설계했습니다. 이때 각 상황에 따라서 유저에게 보낼 메시지가 다양하기 때문에 응답용 JSON 탬플릿을 만드는 함수를 만들어서 응답에 들어가는 카드 이미지나 버튼, 텍스트 내용들을 수정하는 것이 수월합니다.
그리고 유저의 잘못된 데이터 전송이나 네트워크 오류로 데이터 처리가 불가능한 상황에서는 요청으로 들어온 블록 id를 저장했다가 유저가 재시도할 수 있도록 메시지를 보낼 수 도 있으니 참고하시길 바랍니다.

유기적 챗봇 설계 Tip

[ 1. 블록간의 상호 작용을 위한 컨텍스트 활용방법 ]

블록 간의 상호작용은 컨텍스트 활용에서 부터 시작합니다.
스킬을 사용하지 않고 챗봇 내에서 유저의 발화문에 따라서 다른 응답을 사용하고 싶다면 ‘컨텍스트’를 활용하는 것도 좋은 방법이 될 수 있습니다. 예를 들어  블록A로 새로운 공복 혈당 기록을 만든 유저가 갑자기 수치를 바꾸고 싶어할 때, ‘공복’을 컨텍스트로 만들어서 다음 블록에게 전달하고, ‘수정하기’ 버튼을 통해서 블록B가 활성화되면 컨텍스트로 이미 ‘공복’이란 값이 넘어왔기 때문에 ‘공복혈당 값 수정하기’를 실행할 수 있게 됩니다.
컨텍스트는 블록 설정 메뉴에서 찾아볼 수 있습니다.
컨텍스트를 사용하기 위해서는 꼭 input으로 받는 컨텍스트도 설정하고 output으로 내보내는 컨텍스트를 설정해줘야 사용이 가능합니다. 컨텍스트를 내보냈다고 해서 모든 블록들이 해당 컨텍스트에 접근할 수 있는 것은 아닙니다. 그리고 컨텍스트를 사용할때는 반드시 파라미터쪽에 값을 #{컨텍스트 이름}.{컨텍스트에 따라오는 파라미터 이름}을 넣어주셔야 해당 컨텍스트가 가지고 있는 값을 이용할 수 있습니다. 하지만 컨텍스트의 단점은 유효한 시간과 횟수가 있기 때문에 유저가 다른 기능을 사용하다 보면 컨텍스트로 넘기고 싶은 값을 나중에 못 넘기게 되는 불상사가 일어날 수 있습니다. 이때는 Extra를 사용해야하는데 이는 다른 팁을 먼저 소개 드리고 말씀드리겠습니다.

[ 2. 조건별 응답을 위한 JSON 처리 방법 ]

응답에 사용될 이미지, 버튼 값들을 프로토타입 객체에 준비해뒀습니다!
당연한 이야기일 수 있지만 기존 챗봇의 1블록 1타입 응답에서 벗어나기 위해서는 스킬을 사용하고 각 스킬이 분기에 따라 다양한 응답을 보낼 수 있도록 공통 함수를 선언해서 사용하는 것이 좋습니다. 예를 들어 basicCard 타입을 케로셀형태로 2가지 이상의 카드를 보내고 싶다면, 다음과 같이 탬플릿 만드는 함수를 만들어볼 수 있습니다.
위와 같이 케로셀 타입을 응답을 선언하는 함수를 만들고 그 안에 item 형식으로 들어갈 카드를 배열 형태로 넣는다면 코드를 처음 보는 개발자도 복잡하게 embedded 된 응답형태를 보다 쉽게 이해할 수 있기 때문에 유지보수도 쉽고, 새로운 블록이 추가된다고 하더라도 탬플릿 함수에 원하는 이미지와 텍스트, 버튼만 넣어서 재활용이 가능하기 때문에 개발에 들어가는 리소스도 아낄 수 있습니다.

[ 3. 테스트 채널과 라이브 채널 운용을 통한 배포 과정 ]

초기 챗봇 테스트할 때 당신이 직면하게 될 모습
챗봇을 테스트하는 환경을 만드는 것도 중요합니다. 오픈빌더에 있는 봇테스트만 믿고 테스트를 진행하다간 실제 비즈니스 채널에서 예상치 못한 버그에 당황할 수 있습니다. Event API와 같이 유저가 아닌 챗봇이 먼저 메시지를 보내는 행동에 대한 테스트는 직접 테스트 환경을 구축해야만 확인이 가능하기 때문에 설정탭에 들어가서 운영 채널과 개발 채널을 연결하는 것이 중요합니다.
스킬마다 운용채널, 개발채널 요청을 보낼 API 주소를 설정할 수 있습니다.
개발 채널을 설정했다면, 스킬들을 개발용 서버에 연결해서 테스트를 진행하는 것이 심신안정에 좋습니다. 그래서 개발 채널에서 스킬을 테스트한 다음에 운영 채널 서버에 해당 코드를 merge하는 형태로 배포를 준비하는 것이 바람직합니다. 이와 같은 스탭을 준비해둬야지 CI/CD가 따로 없는 챗봇 개발 환경에서 실제 사용하고 있는 유저가 갑자기 버그를 보는 불상사를 막을 수 있습니다.

[ 4. 엑스트라 활용하기! ]

컨텍스트 말고 유저가 언제 와도 특정 값을 수정할 수 있게 할 수 없을까?
위에 컨텍스트는 챗봇이 스킬을 따로 사용하지 않고 블록에서 블록으로 이어질 때 사용하기 좋은 팁입니다. 하지만 유저가 어제 혈당 기록을 해놓고 생각해보니 그 값이 잘못 되어서 값을 바꾸고 싶다면 어떻게 해야할까요? 혈당 기록들은 각자 고유의 id값을 가지고 있지만 컨텍스트를 활용하기에는 시간적 제약이 있습니다. 이럴 때는 버튼에 Extra를 심어서 유저가 해당 버튼을 클릭하여 스킬을 실행할 때, 필요한 값이 clientExtra로 확인할 수 있도록 만드는 것이 좋습니다. 이 부분에 대해서는 공식문서에도 예시가 부족하기 때문에 여러번 테스트 해보시는 것을 추천드립니다.

[ 5. Event API로 특정 조건 응답 전송하기 ]

공복 혈당에 대한 알람을 맞췄다면 해당 시간에 공복 혈당 알림전송!
유저가 먼저 말을 걸지 않아도 챗봇이 메시지를 보내게 하려면 Event API를 사용하셔야 합니다. 다만 주의해야할 점은 Event API가 호출될 때, 호출된 이벤트가 등록된 블록이 실행되기 때문에 개별 유저에게 특정 메시지를 보내고 싶다면 엑스트라와 같이 ‘properties’를 설정해주셔야 합니다. 그래서 블록에서 스킬이 실행될 때, 해당 properties를 서버에 전송하게 되어서 해당 유저에게 필요한 메시지가 무엇인지 식별을 할 수 있게 됩니다.
Event API를 통한 메시지 전송은 위 4가지 스탭을 거치게 됩니다.
현재 닥터다이어리 봇 같은 경우는 Lamda 함수가 서버에 콜을 날리면 위의 config 데이터를  가지고 Event API을 호출합니다. 그럼 챗봇에서 ‘alarm’ 이벤트가 설정된 블록을 실행하게 되고 스킬 요청을 받은 서버는 req.body.userRequest.user.properties에 있는 값을 확인하여 알람 응답을 전송하게 됩니다.

[ 6. 자연스럽게 순환할 수 있는 구조 설계 ]

유저가 언제든 기능을 반복할 수 있도록 메인화면 카드를 추가!
잘 만들어진 챗봇이라도 유저의 사용성이 떨어진다면 슬프겠죠… 개발 외적으로도 주기적으로 사용할 유저 입장을 고려하여 유저가 굳이 위로 스크롤을 하지 않더라도 기능들을 잘 사용할 수 있도록 적당한 수의 메뉴버튼을 준비하고, 하나의 기능에 대한 응답을 보내더라도 후속 행동이 있을 수 있도록 다른 기능을 추천하거나, 도움말과 같이 언제든 기능에 대한 설명을 들을 수 있는 블록을 만들어서 우리 서비스만의 랜딩 시나리오를 봇 설계에 추가하는 것도 하나의 Tip이라고 생각합니다.
챗봇 개발 후기 및 차후 계획
여기요! 좋은 레퍼런스가 있어요!
챗봇 개발을 하면서 가장 힘들었던 점은 생각보다 참고할 레퍼런스 서비스가 적다는 것 이였습니다. 카카오톡 챗봇과 서비스 어플리케이션이 서로 상호작용하면서 이용되는 것을 고려하시는 분이라면 아마 이 글이 조금이나마 도움이 되지 않았을까? 합니다. (하하하) 저로서는 베타버전의 오픈빌더를 사용해서 원했던 서비스 그림을 만들 수 있어서 재미있었던 프로젝트였습니다. 차후 업로드 이미지에 대한 접근이 보다 자유롭게 풀리고, 시스템 엔티티들이 더 추가가 된다면 자사 앱에서 사용하고 있는 많은 기능들을 카카오톡 챗봇에 추가해서 우리 유저분들이 보다 편하게 건강관리를 하실 수 있도록 만들어 드리고 싶습니다. (사랑합니다 닥다앱 여러분 우리 같이 열심히 당뇨관리 해봅시다!
Editor. 임채영
@Dr.diary