Line, 카카오톡, Slack, facebook 등 기존의 메신저 서비스와 연동하지 않고, 팀 프로젝트에서 ChatBot API를 사용하기로 하였다.
후보는 크게 세 가지였음.
- AWS: Amazon Lex
- Google Dialogflow
- NCP Chatbot
이 중 NCP Chatbot을 선택한 이유는 다음과 같다.
- Amazon Lex의 경우 레퍼런스가 많이 없다.
- Google Dialogflow의 경우 Node.js에서는 Google Credentials 인증이 편리한데, 스프링 부트의 경우 인증 절차가 너무 복잡하였다.
- 따라서 NCP Chatbot을 선택함.
NCP Chatbot
1. NCP 계정을 생성한다
네이버 계정과 연동하였음.
2. Console에서, NCP Chatbot 사용 시작하기를 누른다.
이때, 결제 카드 등록을 해야함.
3. 도메인 생성
생성 이후 오른쪽에 "빌더 실행하기" 클릭
4. 테스트 대화 생성
질문 등록 및, 답변 등록
5. 대화 빌드
빠른 빌드 선택
수동 테스트에서, 빌드한 대화 모델을 테스트해볼 수 있다.
6. Custom 연동
- 연동에 앞서서, API GateWay 사용 신청이 필요하다.
- 사용신청 이후, "연동" 버튼을 클릭 후, APIGW 자동 설정을 사용한다.
- API Invoke URI와 Secret Key를 application.yml 등에 저장한다.
Spring Boot 코드 작성
코드는 참고 링크 1번의 공식 문서를 참고하였다.
// build.gradle에 json-simple 추가
implementation group: 'com.googlecode.json-simple', name: 'json-simple', version: '1.1.1'
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.codec.binary.Base64;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;
@Slf4j
@RestController
public class ChatController {
@Value("${ncp.secret_key}")
private String secretKey;
@Value("${ncp.api_url}")
private String apiUrl;
@PostMapping("/chat")
public String sendMessage(@RequestBody String chatMessage) {
try {
URL url = new URL(apiUrl);
String message = getReqMessage(chatMessage);
String encodeBase64String = makeSignature(message, secretKey);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "application/json;UTF-8");
con.setRequestProperty("X-NCP-CHATBOT_SIGNATURE", encodeBase64String);
// post request
con.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.write(message.getBytes("UTF-8"));
wr.flush();
wr.close();
int responseCode = con.getResponseCode();
if (responseCode == 200) { // Normal call
System.out.println(con.getResponseMessage());
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String decodedString;
String jsonString = "";
while ((decodedString = in.readLine()) != null) {
jsonString = decodedString;
}
in.close();
JSONParser jsonparser = new JSONParser();
try {
JSONObject json = (JSONObject) jsonparser.parse(jsonString);
JSONArray bubblesArray = (JSONArray) json.get("bubbles");
JSONObject bubbles = (JSONObject) bubblesArray.get(0);
JSONObject data = (JSONObject) bubbles.get("data");
String description = "";
description = (String) data.get("description");
chatMessage = description;
} catch (ParseException e) {
log.error(">> [ChatController] cannot parse the jsonObject");
}
} else { // Error occurred
chatMessage = con.getResponseMessage();
}
} catch (MalformedURLException e) {
log.error(">> [ChatController] used wrong API URL");
} catch (IOException e) {
log.error(">> [ChatController] error", e);
}
return chatMessage;
}
public String makeSignature(String message, String secretKey) {
String encodeBase64String = "";
try {
byte[] secrete_key_bytes = secretKey.getBytes("UTF-8");
SecretKeySpec signingKey = new SecretKeySpec(secrete_key_bytes, "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(message.getBytes("UTF-8"));
encodeBase64String = Base64.encodeBase64String(rawHmac);
return encodeBase64String;
} catch (Exception e) {
System.out.println(e);
}
return encodeBase64String;
}
public String getReqMessage(String voiceMessage) {
String requestBody = "";
try {
JSONObject obj = new JSONObject();
long timestamp = new Date().getTime();
obj.put("version", "v2");
// userId is a unique code for each chat user, not a fixed value, recommend use UUID.
// use different id for each user could help you to split chat history for users.
obj.put("userId", "U47b00b58c90f8e47428af8b7bddc1231heo2");
obj.put("timestamp", timestamp);
JSONObject bubbles_obj = new JSONObject();
bubbles_obj.put("type", "text");
JSONObject data_obj = new JSONObject();
data_obj.put("description", voiceMessage);
bubbles_obj.put("type", "text");
bubbles_obj.put("data", data_obj);
JSONArray bubbles_array = new JSONArray();
bubbles_array.add(bubbles_obj);
obj.put("bubbles", bubbles_array);
obj.put("event", "send");
requestBody = obj.toString();
} catch (Exception e) {
System.out.println("## Exception : " + e);
}
return requestBody;
}
}
Postman 으로 테스트 결과
- NCP Chatbot에 답변으로 등록한 "그래요" 메시지가 정상적으로 출력되는 것을 볼 수 있다.
다음 게시글에서는 대화 내용을 좀 더 추가해볼 예정이다.
참고 링크
1. Clova chatbot custom api: https://api-fin.ncloud-docs.com/docs/ai-application-service-chatbot-chabot#%EC%9D%91%EB%8B%B5-%EA%B2%B0%EA%B3%BC-status-%EC%84%B1%EA%B3%B5-%EB%B0%8F-%EC%8B%A4%ED%8C%A8
2. [Spring Boot + WebSocket+ 네이버 Chatbot] 챗봇 만들기 - 2 https://milenote.tistory.com/45
3. Spring Boot로 나만의 Chatbot 구축하기 : https://medium.com/@dynamic_maize_coyote_676/spring-boot%EB%A1%9C-%EB%82%98%EB%A7%8C%EC%9D%98-chatbot-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-868b4a379209
'Spring Boot' 카테고리의 다른 글
Spring Boot] org.springframework.orm.jpa.JpaSystemException: ids for this class must be manually assigned before calling save() (0) | 2023.07.15 |
---|---|
Spring Boot] 채팅 시스템 설계 (0) | 2023.07.11 |
Spring Boot] java.lang.IllegalArgumentException: Key argument cannot be null (0) | 2023.07.08 |
Spring Boot] JWT 토큰을 사용한 사용자 인증/인가 (0) | 2023.07.03 |
Spring Boot] OncePerRequestFilter 에서 json으로 응답 결과 보내기 (0) | 2023.07.02 |