어서와, 개발은 처음이지?

카카오 오픈빌더와 외부 API 연동(feat.Nodejs) 본문

Node

카카오 오픈빌더와 외부 API 연동(feat.Nodejs)

오지고지리고알파고포켓몬고 2020. 3. 16. 05:33

이전에 플러스 친구와 외부 API 연동에 관한 글을 작성한 적 있습니다.

하지만 지난 2년동안 플러스 친구에 많은 변화가 생겼는데요.


카카오 플러스 친구의 명칭이 카카오 채널로 바뀌고, 챗봇 세팅 방식이 기존 [카카오 플러스 친구 - 외부 API 연동] 구조에서 오픈빌더가 추가되어 [카카오 채널(구 플러스 친구) - 카카오 i 오픈빌더 - 외부 API 연동] 구조로 바뀌었습니다.


이번 글에서는 오픈빌더의 챗봇 시나리오 관리 기능을 간단히 소개하고 외부 API를 연동하는 예제를 다뤄보겠습니다.

(연동파트는 5번 항목부터 보시면 됩니다.)



1. 블록


블록은 오픈빌더에서 질의/응답을 관리하는 최소 단위로, 사용자의 발화와 챗봇의 대답을 입력할 수 있습니다.


예를들어 인사라는 블록을 만들고 인사에 해당하는 사용자 발화 패턴들을 입력해두면, 실제 채널 톡방에서 그에 해당하는 발화가 들어왔을때 입력해둔 응답이 나오는 형식입니다.


예전에는 패턴과 발화 키워드가 1:1 매칭, 즉 입력해둔 패턴과 사용자 발화의 string이 정확히 일치할때만 블록이 실행됐었는데, 발화 패턴을 20개 이상 등록하면 머신러닝 기능을 이용할 수 있도록 기능이 생겼습니다.


아마 유사도 분석 개념이 기본으로 들어가있을 것이기 때문에 블록의 주제와 벗어나는 너무 뜬금없는 발화패턴들을 많이 넣지 않도록 하는걸 권장하겠습니다.



2. 시나리오


시나리오는 '블록'들을 묶어서 관리할 수 있는 단위로, 일종의 폴더 구조라고 생각하면 쉽습니다.

오픈빌더에서 좌측 상단에 파란 버튼을 클릭하여 시나리오를 생성할 수 있습니다.


하나의 시나리오에서 모든 블록을 관리하면 챗봇 도메인이 커질수록 관리가 어려워지니 아래 같은식으로 시나리오를 사용하여 블록을 구조화하면 운영 측면에서 수월해집니다. 



3. 컨텍스트


컨텍스트는 맥락이라는 뜻 입니다.


오픈빌더에 존재하는 컨텍스트는 자연어 분석을 통해서 맥락을 유추하는 것이 아닌 약간 다른 기법으로,

특정 블록이 실행될때 컨텍스트를 활성화 할 수 있고, 이 컨텍스트가 활성화 되어있을때만 블록이 실행될 수 있게 설정하는 방식으로 '맥락'을 만들어갑니다.


컨텍스트는 블록 수정화면 우측 상단메뉴에서 설정할 수 있습니다.



컨텍스트 설정 메뉴를 누르면 아래와 같은 화면이 나옵니다.



output 컨텍스트는 해당 블록이 실행됐을때 세팅되는 컨텍스트입니다.

input 컨텍스트는 블록에 해당하는 발화가 들어왔을때, input 컨텍스트에 입력한 값이 활성화 되어있어야 블록이 실행될 수 있도록 하는 일종의 조건 분기같은 개념입니다.


간단하게 로그인과 회원정보 변경으로 예를 들어보겠습니다.


우선 회원정보 변경이라는 블록을 만들고 아래와 같이 패턴 발화와 응답을 설정했습니다.

그리고 input 컨텍스트에 login이라는 컨텍스트를 입력했습니다.



다음으로 로그인이라는 블록을 만들고 아래와 같이 패턴 발화와 응답을 설정했습니다.

그리고 output 컨텍스트에 login이라는 정보를 입력합니다.



이제 기본설정이 완료되었으니 테스트 해보겠습니다.



아무 맥락(context) 없는 상태에서 회원정보 변경 발화가 들어오면 블록이 실행되지 않습니다.

input 컨텍스트에 'login'이라는 조건을 걸어놨기 때문입니다.



이번에는 로그인 블록을 통해 login 컨텍스트를 세팅하고 회원정보 변경을 요청하니 블록이 실행되는 모습을 볼 수 있습니다.


좀 더 논리적으로 구현하면, login 컨텍스트가 없을땐 회원정보 변경을 입력했을때 로그인을 유도하도록 하거나, 회원정보 변경에서도 output 컨텍스트에 login을 달아두어서 세션이 계속 유지되듯이 관리할 수 있겠습니다.


다만 이런 컨텍스트들을 한번에 관리할 수 있는 기능이 존재하지 않기 때문에, 각 블록별 어떤 컨텍스트들이 존재하는지 문서화 해둘 필요가 있습니다.


4. 파라미터


파라미터는 컨텍스트와 비슷하지만 발화 내용을 분석하여 특정 정보를 담는 역할을 합니다. 오픈빌더 내에서 사용되기보다는 외부 API에 정보를 전달하는 역할로 사용됩니다.


파라미터는 블록 수정화면에서 쉽게 찾을 수 있습니다.

일반 파라미터와 필수 파라미터가 있는데, 통상적으로 외부 API에서 필요한 파라미터들은 필수 파라미터가 대부분일것이라 감히 예상합니다.


파라미터는 아래와 같은 화면에서 등록하고, 필수 파라미터의 경우 되묻기 질문또한 설정할 수 있습니다.



내부 로직을 통해 사용자 발화를 분석해서 특정 엔티티를 찾아내어 파라미터에 등록하는데, 기본 제공 엔티티, 사용자 커스텀 엔티티 모두 사용가능합니다.

(엔티티에 대한 개념은 따로 다루지 않겠습니다.)


간단한 예로 생일을 변경하는 블록을 만들고 파라미터의 모습을 확인 해보겠습니다.




생성한 블록의 필수 파라미터에 출생년, 생일 엔티티와 매칭되는 필수 파라미터를 설정하고 되묻기 질문을 세팅했습니다.


이 블록을 테스트하면 다음과 같은 스탭으로 파라미터가 세팅되는 모습을 볼 수 있습니다.




5. 스킬 (외부 API연동)


오픈빌더에서는 스킬이라는 기능을 사용해야 외부 API와 연동할 수 있습니다.

스킬은 상단 메뉴에서 생성할 수 있으며, 플러스 친구에서 API와 연동했던것처럼 사용할 수 있습니다.



특별히 어려운 것은 없고 모든 스킬은 post 메서드로 요청되는 점을 유의 하시면 됩니다.


다만 오픈빌더 규격에 맞는 답변의 형태를 쉽게 볼수있도록 되어있지 않기 때문에 간단한 테스트는 아래 샘플코드를 사용하여 연동해보시길 바랍니다.

응답에 관한 포맷 정보는 공식문서에서 보실 수 있습니다.

app.post("/test", function(req, res) {
  return res.send({
    version: "2.0",
    template: {
      outputs: [
        {
          basicCard: {
            title: "간단한 텍스트와 버튼요소",
            description: "간단한 버튼과 텍스트입니다",
            buttons: [
              {
                action: "message",
                label: "열어보기"
              }
            ]
          }
        }
      ]
    }
  });
});


이제 원하는 블록에서 봇 응답으로 해당 스킬을 연동해주면 됩니다.






6. 간단한 데모 만들어보기


API 연동을 통해서 사용자의 관심사를 등록하는 간단한 로직을 만들어보겠습니다.


6-1. 블록 ID 확인하기


우선 오픈빌더의 각 블록들은 ID가 존재하는데, 오픈빌더에서는 확인할 수 없습니다.

버튼 요소에 이 블록 ID를 세팅하면 특정 블록을 실행할 수 있기 때문에 블록 ID를 볼 수 있는 스킬을 구현합니다.

app.post("/blockId", function(req, res) {
  const userRequest = req.body.userRequest;
  const blockId = userRequest.block.id;

  return res.send({
    version: "2.0",
    template: {
      outputs: [
        {
          basicCard: {
            title: "블록ID 입니다",
            description: blockId
          }
        }
      ]
    }
  });
});

다음으로 스킬을 세팅하고 블록에서 스킬을 사용하면 해당 블록의 ID를 볼 수 있습니다.




이제 블록 ID들을 엮어서 관심사 등록 로직을 구현해보겠습니다.


6-2. 관심사 등록 로직 


우선 관심사를 등록하는 블록을 생성합니다.

그리고 6-1에서 생성한 스킬에 연결하여 블록 ID를 확인해줍니다.



'관심사 등록 API' 블록의 ID는 5e6e7b4827fd9f0001350f12 입니다.


이번에는 API 로직을 구현해줍니다.

var favoriteTemplate = {
  운동: false,
  요리: false,
  여행: false,
  게임: false
};

var userDB = {};

app.post("/add", function(req, res) {
  const userRequest = req.body.userRequest;
  const userId = userRequest.user.id; // 카카오 식별자
  const userFavorite = userRequest.utterance; // 입력 발화

  // 관심사 정보가 없을경우 데이터 초기화
  if (!userDB[userId]) {
    userDB[userId] = { ...favoriteTemplate };
  }

  // 관심사 검증 후 등록
  if (userFavorite in favoriteTemplate) {
    userDB[userId][userFavorite] = true;
  }

  return res.send({
    version: "2.0",
    template: {
      outputs: [
        {
          basicCard: {
            description: "추가 되었습니다",
            buttons: [
              {
                action: "message",
                label: "내 관심사 목록",
                messageText: "내 관심사 목록"
              },
              {
                action: "message",
                label: "관심사 추가",
                messageText: "관심사 추가"
              }
            ]
          }
        }
      ]
    }
  });
});

userRequest의 utterance 속성은 해당 블록에서 입력된 발화를 뜻합니다.


관심사 추가 form을 제공하는 6-3.블록에서 특정 관심사 버튼을 선택하면 블록 ID를 지정하여 이 블록에 발화를 전달하게 할 것인데, utterance에 이 발화(관심사)가 들어있게 됩니다.


이제 이 API를 스킬에 등록하고, 관심사 등록 API 블록에 추가합니다.

6-3.블록을 통해서 발화를 전달받을것이기 때문에 패턴 발화는 제거하도록 합니다.




6-3. 관심사 추가 form


이 블록에서는 사용자가 등록 가능한 관심사 리스트를 보여주는 API가 필요합니다.


6-2에서 정의한 관심사 목록을 기반으로 사용자가 관심사로 등록(true)하지 않은 목록들을 버튼으로 보여주는 form 응답을 작성합니다.

app.post("/form", function(req, res) {
  const userRequest = req.body.userRequest;
  const userId = userRequest.user.id;
  let favoriteList = [];

  if (!userDB[userId]) {
    favoriteList = Object.keys(favoriteTemplate).map(f => {
      return {
        action: "block",
        label: f,
        blockId: "5e6e7b4827fd9f0001350f12" // 관심사 추가 API 블록
      };
    });
  } else {
    favoriteList = Object.keys(userDB[userId])
      .filter(f => {
        return userDB[userId][f] === false;
      })
      .map(f => {
        return {
          action: "block",
          label: f,
          blockId: "5e6e7b4827fd9f0001350f12" // 관심사 추가 API 블록
        };
      });
  }

  return res.send({
    version: "2.0",
    template: {
      outputs: [
        {
          simpleText: {
            text: "추가할 관심사를 선택해주세요"
          }
        }
      ],
      quickReplies: favoriteList
    }
  });
});

(quick reply 항목은 공식 문서에 response 위치가 명확하게 나와있지 않아서 추가해봤습니다.)


그리고 관심사 추가 form이라는 새 블록을 만들고 이 스킬을 연동시켜줍니다.(스킬 등록은 6-2처럼 해주시면 되겠습니다.)



이제 테스트를 해봅니다.



운동 관심사를 추가한 뒤, 다시 관심사를 추가하려할 때 운동 버튼을 보여주지 않기 때문에 방금 구현한 API와 챗봇 시나리오가 잘 동작함을 알 수 있습니다.


6-4. 내 관심사 목록


이제 마지막으로 내가 추가한 관심사를 볼 수 있도록 구현하겠습니다.

userDB 객체에 있는 관심사 중 true로 설정된 내용을 불러오는 로직으로 특별히 어려운 내용은 없으니 자세히 설명하진 않겠습니다.

app.post("/list", function(req, res) {
  const userRequest = req.body.userRequest;
  const userId = userRequest.user.id;
  let myFavoriteList = [];

  if (userDB[userId]) {
    myFavoriteList = Object.keys(userDB[userId]).filter(f => {
      return userDB[userId][f] === true;
    });
  }

  if (myFavoriteList.length <= 0) {
    return res.send({
      version: "2.0",
      template: {
        outputs: [
          {
            basicCard: {
              description: "등록된 관심사가 없습니다",
              buttons: [
                {
                  action: "message",
                  label: "관심사 추가",
                  messageText: "관심사 추가"
                }
              ]
            }
          }
        ]
      }
    });
  } else {
    return res.send({
      version: "2.0",
      template: {
        outputs: [
          {
            basicCard: {
              description: `현재 나의 관심사는 ${myFavoriteList
                .map(f => `[${f}]`)
                .join(", ")} 입니다`,
              buttons: [
                {
                  action: "message",
                  label: "관심사 추가",
                  messageText: "관심사 추가"
                }
              ]
            }
          }
        ]
      }
    });
  }
});

이제 이 API를 스킬에 등록한 뒤 내 관심사 목록이라는 블록을 생성하고 스킬을 연결해줍니다.



이제 테스트를 해봅니다.



역시 잘 동작하는 모습을 볼 수 있습니다.

여기까지 간단한(?) 카카오 오픈빌더 외부 API 연동 데모 구현을 마치겠습니다.


최대한 간단하게 흐름을 설명하기 위해 블록 간 연결을 통하여 로직을 구현했지만 위에서 언급한 컨텍스트나 파라미터를 사용하면 더욱 다채롭게 구현할 수 있습니다.



7. 채널 연결


카카오 채널과 오픈빌더 챗봇 연결은 설정 페이지에서 '운영 채널 연결'을 통해 할 수 있습니다.


또한 오픈빌더 내 봇 테스트에서는 버튼 클릭이라던지 몇가지 동작 안하는 컨텐츠가 존재하는데 '개발 채널 연결'을 통해 오픈빌더에서 설정한 내용들을 실시간으로 테스트해볼 수 있습니다.



아울러 챗봇 개발이 완료되었다면 배포 작업을 거쳐야 운영 채널에 반영됩니다. (실제 반영까지 약간의 시간이 소요될 수 있습니다)




8. 마무리


이번 글에서는 카카오 오픈빌더에서 외부 API를 연동하는 방법에 대해 알아봤습니다.

데모에서 구현하지 않은 컨텍스트 및 파라미터도 API의 response에 사용할 수 있으니 공식문서를 찬찬히 읽어보시기 바랍니다.


또한 카카오 채널에 푸시 메시지를 보낼 수 있는 '알림톡', 비교적 최근에 생겨난 회원가입 로직인 '카카오 싱크' 등 다양한 연계 서비스들도 있으니 카카오 플랫폼을 통해서 원하는 비즈니스 로직을 구축하시길 바라겠습니다.

4 Comments
댓글쓰기 폼