모에모에 코딩

카카오톡 아이 오픈 빌더를 이용한 번역 봇 제작기 본문

개발 일기/카카오톡 챗봇

카카오톡 아이 오픈 빌더를 이용한 번역 봇 제작기

hazimenya 2019. 2. 8. 14:02
반응형

본 게시물은 Kakao i open builder 를 이용하여 언어 번역 채팅 봇을 제작 하는 과정을 일기 형태로 작성하였습니다.


시작하기 앞서 불평불만


Kakao i open builder의 OBT 시작은 기존 스마트 API 형 채팅봇에게 시한부 선고를 내리는 말과 같았습니다. 지금 이 글을 작성하고 있는 필자도 OBT 이전부터 서비스를 이미 해왔고, 유지보수에만 신경쓰고 있었습니다.

당시 운영했던 서비스 Github 링크 https://github.com/stories2/KangnamShuttle

하지만 위와 같이 카카오톡 챗봇에 중대 업데이트가 올라 왔고, 2019년 말 까지 새로운 형태의 카카오톡 챗봇 서비스를 도입하지 않으면 서비스 중단이라는 공지까지 하면서 어쩔 수 없이 추가 개발을 해야 되는 상황이 되었습니다. 그래도 기존 API 형태랑 많이 다르지 않을 것이라 기대 했던 부분은 새로운 API 스펙을 보자 물거품이 되어버렸고, 다시 처음부터 프로젝트를 새로 만들는게 더 이득이라 생각될 정도였습니다.

다행이도 지금은 어느정도 지지고 볶은 덕분에 현재는 어느정도 서비스를 복구 할 수 있었습니다.

 https://github.com/stories2/KangnamShuttle-3.0


번역 채팅 봇 제작기


어찌되었든 우리는 다시 채팅 봇을 제작할 것입니다. 
주제는... 언어 번역을 해주는 봇이 괜찮겠군요.

들어가기 앞서

- OBT 승인된 계정이 준비되어야 합니다.
- 필자가 사용하던 챗봇 소스 템플릿에서 약간의 커스텀 마이징만 할 예정입니다.
- 모든 소스 내용은 https://github.com/stories2/Kakao-Translate 여기서 확인 할 수 있습니다.
- Kakao i open builder 의 무수히 많은 기능들 중 일부만 이용할 것 입니다.
- Kakao plus 계정이 준비되어 있어야 합니다.

사용할 서비스 / 기술

- Firebase functions, realtime-database https://firebase.google.com/
- NodeJS ver6.11.5
- NPM ver3.10.10

완성된 모습 미리보기



하단에 표시된 번역을 부탁해~! 버튼을 제네릭 메뉴라고 합니다. 제네릭 메뉴를 누르면 유저에게 어떤 언어로 번역을 할 것 인지 선택할 수 있는 퀵 메뉴를 제공할 것입니다.

또한 퀵 메뉴 (한국어, 영어, 일본어) 를 눌렀을 시 해당 언어로 번역을 시작한다는 문구를 보여주며, 이 문구 이후 부터 사용자가 입력하는 내용을 번역해줍니다.

번역을 마치고 싶을 땐 2가지 방법이 주어지는데 퀵 메뉴 (홈) 을 누르거나 제네릭 메뉴 (번역을 부탁해~!) 를 누를 시 초기 메뉴인 번역 될 언어 선택 상태로 돌아오게끔 합니다.


위 기능이 전부입니다.



TODO

어떤 내용을 만들지 목표가 정해졌으니 해야할 일을 정리하려 합니다.

 할일

내용

비고 

 Kakao plus 프로필 만들기

 사용자가 카카오에서 채팅을 할 프로필 생성

 

 Kakao i open builder 프로필 만들기

 챗봇을 만들기 위한 프로필 생성

 

 Firebase 프로젝트 만들기

 챗봇 어플리케이션을 호스팅할 프로젝트 생성

 

 언어 선택 스킬 응답 만들기 

 사용자가 번역할 언어 선택지를 스킬을 이용해 생성

 

 언어 번역 스킬 응답 만들기

 Google API를 이용하여 사용자가 입력한 내용 번역

 

 언어 번역 시나리오 및 블럭 연동

 스킬과 블럭을 연동시켜 서비스 구현

 

 베포 및 테스트

 사용자에게 직접 서비스

 



개발 시작!

본격적으로 개발을 시작하여 봅니다.



Kakao plus 프로필 만들기




카카오 plus 친구를 만들 수 있는 주소는 https://business.kakao.com 입니다.

시작하기 버튼을 눌러 프로필 관리 화면으로 이동할 수 있습니다. 



새 플러스 친구 만들기 버튼을 눌러 새 프로필을 만들게 됩니다.


필수 추가요소 및 프로필 정보를 입력한 후 확인을 누르면 플러스 친구 프로필이 만들어 집니다.



왼쪽 메뉴에서 관리 -> 상세설정 -> 플러스친구 홈 

에서 홈 공개, 검색 허용을 토글하면 카카오 검색시 프로필이 노출될지를 정할 수 있습니다.


Kakao i open builder 프로필 만들기



Kakao i open builder 에서 프로필을 만들기 위해 https://i.kakao.com/openbuilder 로 이동합니다.


우측상단의 + 버튼을 클릭하여 새 프로필을 만들 수 있습니다. 선택지에서는 카카오톡 챗봇을 선택합니다.

프로필 이름까지 정하면 위와 같은 화면이 보여지게 됩니다.


간단한 설정이 필요합니다. 위에서 만들었던 카카오 Plus 프로필과 방금 만든 Kakao i open builder 프로파일을 서로 연결시켜주어야 합니다.


설정 탭 -> 플러스친구 계정 연결에서 플러스 친구 계정 드롭다운을 눌러 우리가 만들었던 카카오 Plus 프로필을 선택한 후 우측 상단의 저장 버튼을 꼭! 눌러줍니다.

저장완료 후 다시 카카오 Plus 프로필로 돌아가보겠습니다.



카카오 Plus 친구 -> 스마트 채팅 -> 챗봇 매뉴에 들어가면 상단에 

챗봇

kakaobot ~~~과 연동됨 이라는 상태 표시 문구가 작성되어있습니다. 이때 연동 되었다는 문구가 보인다면 카카오Plus 프로필과 kakao i open builder 프로젝트가 서로 연결이 된 것입니다.


Firebase 프로젝트 만들기

https://console.firebase.google.com/ 로 접속하여 프로젝트 추가를 눌르면 프로젝트 생성 작업이 시작됩니다.




프로젝트 이름을 입력하면 자동으로 프로젝트 ID가 작성되며 위치 설정시 서비스할 대상이 위치한 가장 가까운 지역을 선택합니다.

위치 리스트는 https://firebase.google.com/docs/functions/locations?hl=ko 에서 확인할 수 있습니다.

이후 동의, 프로젝트 만들기를 눌러주면 됩니다.


Firebase에서는 많은 서비스를 제공하는데 이번에 사용할 서비스는 다음과 같습니다.

- Realtime database

- Cloud functions

- Hosting (option)


각각의 서비스의 시작 튜토리얼은 Firebase 에서 제공하고 있으므로 생략하겠습니다.

또한 외부 네트워크 연결이 필요하므로 Firebase 가격정책상 https://firebase.google.com/pricing/?hl=ko 종량제를 사용해야 합니다.


스킬응답을 위해 미리 개발한 프로젝트를 이용하도록 하겠습니다.


https://github.com/stories2/Kakao-Translate/tree/6df33334a13eae1e91ab91baf4a3cf4eb6abab2f 여기에서 프로젝트를 다운받을 수 있습니다.

해당 프로젝트는 아직 번역 기능이 추가되지 않은 프로젝트입니다.


스킬 개발 전 Kakao i open builder 응답 처리 형태



Kakao i open builder 에서는 이미 자세한 튜토리얼을 제공하고 있습니다. https://i.kakao.com/docs/getting-started-overview#%EC%98%A4%ED%94%88%EB%B9%8C%EB%8D%94-%EC%86%8C%EA%B0%9C 

모든 내용을 이해하면 좋지만 쉽게 보면

- 시나리오에서 블럭을 만들었을 때 하나의 블럭에는 크게 발화와 응답으로 나누어집니다.

- 발화는 사용자가 카톡을 날리는 행위

- 응답은 그 행위에 장단맞춰주기

라고 보면 될것 같습니다.


응답에서 더 자세히 보자면

- 파라미터는 스킬 실행

- 응답 형태는 텍스트, 이미지, 컴포넌트, 스킬응답


으로 구성되어있습니다.

스킬 개발 전 프로젝트 구조

위에서 간단하게 발화, 응답을 형태를 확인하였습니다. 이를 응용하여 번역봇을 만들어 볼텐데 문제가 생깁니다.
번역을 하려면 사용자가 입력하는 모든 내용을 받아들이고 변환해주어야 합니다. 이를 위해서 모든 발화를 쓴다? 말도 안되는 소리지요. 
Kakao i open builder 에서는 지정되지 않은 발화가 받아들여졌을 시 폴백 블록이라는 기본 블럭에 도달하게 만들어 줍니다. 이를 응용하면 됩니다.

사용자가 발화를 만들어내면 블럭에 따라 처리를 하며, 이때 스킬이 해당 유저의 상태를 저장을 합니다.

그 상태를 확인 하면서 다음 발화를 만들어 낼 시 이전 상태값을 참고하여 그 결과를 스킬로 만들어 응답하면 동적으로 응답을 구현 할 수 있게됩니다.


언어 선택 스킬 응답 만들기 

카카오톡에서는 많은 컴포넌트의 응답 형식을 지원하고 있습니다. 우리는 간단하게 구현할 것이기 때문에 simpleText와 quick replies만 사용할 것 입니다.

응답을 만들는 과정을 Firebase Realtime database에 기술하고 Firebase Cloud functions 에서는 DB에 정의된 응답을 반환하는 작업과 유저 관리, 번역 로직만을 기술할 것입니다.
{
  "actions": {
    "1": {
      "nextAction": [ "1000", "2000", "3000" ],
      "quickReplies": [
        {
          "action": "message",
          "label": "🇰🇷한국어",
          "messageText": "한국어로 번역을 시작해줘"
        },
        {
          "action": "message",
          "label": "🇺🇸영어",
          "messageText": "영어로 번역을 시작해줘"
        },
        {
          "action": "message",
          "label": "🇯🇵일본어",
          "messageText": "일본어로 번역을 시작해줘"
        }
      ],
      "response": [
        {
          "simpleText": {
            "text": "새로운 번역은 언제나 환영이야!"
          }
        }
      ]
    }
  }
}

Firebase Realtime database 에 다음의 내용을 작성하면 언어 선택 스킬응답은 완료입니다. 

위 내용을 해석하자면 다음과 같습니다.


- actions: 여러 스킬 응답 탬플릿 액션들을 가지는 object 입니다.

- 1: 임의로 정의한 action의 고유 번호입니다. 본 과정에선 언어 선택 부분의 스킬응답 고유 번호가 1이 됩니다.

- nextAction: 퀵 응답을 눌렀을 시 이동할 다음 action의 고유 번호 리스트입니다.

- quickReplies: 퀵 응답의 object 리스트입니다. 

- response: 스킬 응답 템플릿 object 리스트입니다.


언어 번역 스킬 응답 만들기

{
  "actions" : {
    "1" : {
      "nextAction" : [ "1000", "2000", "3000"],
      "quickReplies" : [ {
        "action" : "message",
        "label" : "🇰🇷한국어",
        "messageText" : "한국어로 번역을 시작해줘"
      }, {
        "action" : "message",
        "label" : "🇺🇸영어",
        "messageText" : "영어로 번역을 시작해줘"
      }, {
        "action" : "message",
        "label" : "🇯🇵일본어",
        "messageText" : "일본어로 번역을 시작해줘"
      } ],
      "response" : [ {
        "simpleText" : {
          "text" : "새로운 번역은 언제나 환영이야!"
        }
      } ]
    },
    "1000" : {
      "lang" : "ko",
      "nextAction" : [ "1" ],
      "quickReplies" : [ {
        "action" : "message",
        "label" : "🏠홈",
        "messageText" : "다시 메인으로 돌아가줘 "
      } ],
      "response" : [ {
        "simpleText" : {
          "text" : "%s"
        }
      }, "다시 홈으로 돌아가욧!" ]
    },
    "2000" : {
      "lang" : "en",
      "nextAction" : [ "1" ],
      "quickReplies" : [ {
        "action" : "message",
        "label" : "🏠홈",
        "messageText" : "다시 메인으로 돌아가줘 "
      } ],
      "response" : [ {
        "simpleText" : {
          "text" : "%s"
        }
      }, "다시 홈으로 돌아가욧!" ]
    },
    "3000" : {
      "lang" : "ja",
      "nextAction" : [ "1" ],
      "quickReplies" : [ {
        "action" : "message",
        "label" : "🏠홈",
        "messageText" : "다시 메인으로 돌아가줘 "
      } ],
      "response" : [ {
        "simpleText" : {
          "text" : "%s"
        }
      }, "다시 홈으로 돌아가욧!" ]
    }
}


위 내용을 Firebase realtime database에 덮어 씌워주세요. 

이번에는 새로운 action object 와 해당 action object 에 lang 키가 추가되었습니다. 

사용자가 영어 번역을 요청했을 시 해당 사용자의 상태값은 2000이 되어있을 것이며 이때 모든 발화를 번역할 타겟 언어는 lang 에 정의된 en 이다 라는 것을 구현하기 위함입니다.


본격적인 번역을 시작하겠습니다. 다운받은 소스의 functions 폴더에서 

npm i -save @vitalets/google-translate-api

를 작성하여 구글 번역 api npm 모듈을 설치합니다.


functions/Action/translateAction.js 에 다음 소스를 작성합니다.



exports.translateStringToOtherLang = function (request, response, callbackFunc) {
    const action = JSON.parse(JSON.stringify(request.action))
    const reqText = request.body["userRequest"]["utterance"]
    const translate = require('@vitalets/google-translate-api')
    global.log.debug("translateAction", "translateStringToOtherLang", "user data: " + JSON.stringify(request.user) + " action data: " + JSON.stringify(action))
    global.log.debug("translateAction", "translateStringToOtherLang", "req text: " + reqText)

    var makeResponseTemplate = function(responseText) {
        const responseManager = request.responseManager
        const util = require('util')
        var actionText = action["response"][global.define.DEFAULT_RESPONSE_TYPE_ZERO]["simpleText"]["text"]
        actionText = util.format(actionText, responseText)
        action["response"][global.define.DEFAULT_RESPONSE_TYPE_ZERO]["simpleText"]["text"] = actionText
        responseManager.pushTemplate(action["response"][global.define.DEFAULT_RESPONSE_TYPE_ZERO])
        for(var index in action["quickReplies"]) {
            responseManager.pushQuickReply(action["quickReplies"][index])
        }
    }

    translate(reqText, {to: action.lang})
        .then(result => {
            global.log.debug("translateAction", "translateStringToOtherLang", "translate result: " + JSON.stringify(result))
            makeResponseTemplate(result["text"])
            callbackFunc()
        })
        .catch(error => {
            global.log.error("translateAction", "translateStringToOtherLang", "cannot translate: " + JSON.stringify(error))
            makeResponseTemplate("이런, 번역 요정이 장난을 쳐버렸군요.")
            callbackFunc()
        })
}

request 로 부터 사용자 상태에 따른 현재 응답할 action 템플릿이 포함되서 들어오며, 사용자의 발화 내용은 reqest.body["userRequest"]["utterance"] 에 있습니다. 이후로는 action 템플릿에 사용자 발화 내용을 번역한 결과를 쓰고, 응답 관리자(responseManager)에게 템플릿을 전달한 다음 callbackFunc 을 호출함으로써 action 처리를 마무리 짓고 있습니다.

functions/Core/actionManager.js 에 다음 소스를 다음과 같이 수정합니다.

exports.executeOrder = function (request, response, actionIndex, callbackFunc) {
    const basicAction = require('../Action/basicAction')
    const translateAction = require('../Action/translateAction')

    const actionTable = {
        "1": basicAction.hello,
        "1000": translateAction.translateStringToOtherLang,
        "2000": translateAction.translateStringToOtherLang,
        "3000": translateAction.translateStringToOtherLang,
    }

    global.log.debug("actionManager", "executeOrder", "execute action #" + actionIndex)

    actionTable[actionIndex](request, response, callbackFunc) // <-- pass request, response, callbackFunc(flush response data)
}

여기서는 action 번호와 그 action 을 실행할 메소드를 매칭하는 작업을 합니다. 번역 action의 번호를 1000, 2000, 3000 으로 DB에 정의 했었기 때문에 1000, 2000, 3000 을 작성한 후 그 값을 번역 매소드인 translateAction.translateStringToOtherLang 으로 설정합니다.


이로써 스크립트 작업이 모두 끝났습니다.

스킬으로 사용하기 위해 firebase functions 에 배포를 합니다. 배포 한 후 자동으로 endpoint 가 생성됩니다.


언어 번역 시나리오 및 블럭 연동

Kakao i open builder 의 스킬 탭에 들어가 새로운 스킬을 생성합니다.

우린 크게 2가지 종류의 스킬을 만들것입니다.
- 각 블럭마다 적용할 action 스킬
- 폴백 블럭에 실행될 continue 스킬


스킬은 다음과 같이 작성하면 됩니다. 

url 은 action/ 스킬 번호를 쓰면 됩니다.


1: 번역 선택

1000: 한국어 번역

2000: 영어 번역

3000: 일본어 번역


continue 스킬은 

/v3KakaoApi/continue

형식으로 작성하면 됩니다.


그럼 다음과 갈은 스킬들이 생성됩니다.


폴백블럭 설정

초기화 후 continue 스킬과 밑에 스킬데이터 사용을 허용합니다.

사용자가 선택한 언어로 모든 발화가 번역되는 부분입니다.


번역/환영 블럭 설정


번역할 언어 선택 부분입니다.


환영 스킬을 적용 후 스킬데이터를 응답으로 설정합니다.


번역 블럭(영어) 설정


번역 스킬을 적용 후 응답은 번역 시작한다는 문구를 작성합니다. 

이 경우 스킬과 응답이 동시에 실행되어 사용자 상태는 영어 번역 모드로, 카카오톡 화면에서는 번역 시작하겠다는 피드백 문구가 보여지게 됩니다.


베포 및 테스트

배포 탭에서 베포를 눌러주면 됩니다.


최종 소스는 https://github.com/stories2/Kakao-Translate 에서 받으 실 수 있습니다.


반응형

'개발 일기 > 카카오톡 챗봇' 카테고리의 다른 글

강남대학교 달구지봇 개발기  (0) 2019.02.15