Chapter01/Open API

[ CLOVER Chatbot ] 전송 응답 테스트 코드 / UI 제공 여부/ 응답 컴포넌트 비교

EmmaDev_v 2025. 11. 10. 20:00

 

외부 API 연동 중 제일 힘들었던 것 같다

왜냐면 공식문서 대로 따라했는데

안됏다ㅠ........

 

 

https://api.ncloud-docs.com/docs/ai-application-service-chatbot#SecretKey%EB%B0%9C%EA%B8%89%EB%B0%8F%ED%99%95%EC%9D%B8%EB%B0%A9%EB%B2%95

 

 

 

 

 

 

 

 

 

 

 

위 참고해서  apiURL 랑   secretKey 넣기.

 

public Map<String, String> sendMessage(String voiceMessage) {
        Map<String, String> parsed = new HashMap<>();
        
        try {
            String apiURL = "APIGW INvoke URL";
            String secretKey = "시크릿키";
            URL url = new URL(apiURL);
    
            String message = getReqMessage(voiceMessage);
            log.info("[Chatbot] 요청 메시지 JSON: {}", message);
    
            String encodeBase64String = makeSignature(message, secretKey);
            log.info("[Chatbot] 생성된 Signature(Base64): {}", encodeBase64String);
    
            HttpURLConnection con = (HttpURLConnection) url.openConnection();
            con.setRequestMethod("POST");
            con.setRequestProperty("Content-Type", "application/json;UTF-8");
            con.setRequestProperty("X-NCP-CHATBOT_SIGNATURE", encodeBase64String);
            con.setDoOutput(true);
    
            try (DataOutputStream wr = new DataOutputStream(con.getOutputStream())) {
                wr.write(message.getBytes(StandardCharsets.UTF_8));
                wr.flush();
            }
    
            int responseCode = con.getResponseCode();
            StringBuilder sb = new StringBuilder();
    
            if (responseCode == 200) {
                try (BufferedReader in = new BufferedReader(
                        new InputStreamReader(con.getInputStream(), StandardCharsets.UTF_8))) {
                    String line;
                    while ((line = in.readLine()) != null) {
                        sb.append(line);
                    }
                }
            } else {
                log.error("[Chatbot] 오류 응답 코드: {} - {}", responseCode, con.getResponseMessage());
                    try (BufferedReader err = new BufferedReader(
                            new InputStreamReader(con.getErrorStream(), StandardCharsets.UTF_8))) {
                        String line;
                        while ((line = err.readLine()) != null) {
                            sb.append(line);
                        }
                    }
                }
        
                String rawResponse = sb.toString();
                log.info("[Chatbot] 원본 응답: {}", rawResponse);
        
                parsed = extractChatResponse(rawResponse); 
        
        } catch (Exception e) {
                log.error("[Chatbot] 예외 발생", e);
        }
        
        log.info("[Chatbot] 최종 응답 data: {}", parsed);
        return parsed; 
    }
        


    public Map<String, String> extractChatResponse(String jsonResponse) {
        Map<String, String> result = new HashMap<>();
        ObjectMapper mapper = new ObjectMapper();

        try {
            JsonNode root = mapper.readTree(jsonResponse);

            JsonNode dataNode = root.has("data") 
                    ? (root.get("data").isTextual() 
                        ? mapper.readTree(root.get("data").asText()) 
                        : root.get("data"))
                    : root;

            String description = dataNode.path("bubbles")
                    .path(0)
                    .path("data")
                    .path("description")
                    .asText(null);

            long timestamp = dataNode.path("timestamp").asLong(0);

            if (description != null && timestamp > 0) {
                Instant instant = Instant.ofEpochMilli(timestamp);
                ZonedDateTime koreaTime = instant.atZone(ZoneId.of("Asia/Seoul"));
                String formattedTime = koreaTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

                result.put("description", description);
                result.put("time", formattedTime);
            } else {
                log.warn("[Chatbot] 응답 파싱 실패 - 필수 데이터 누락");
            }

        } catch (Exception e) {
            log.error("[Chatbot] 응답 파싱 실패", e);
        }

        return result;
    }


    private String makeSignature(String requestBody, String secretKey) throws Exception {
        SecretKeySpec signingKey = new SecretKeySpec(secretKey.getBytes("UTF-8"), "HmacSHA256");
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(signingKey);
        byte[] rawHmac = mac.doFinal(requestBody.getBytes("UTF-8"));
        return Base64.getEncoder().encodeToString(rawHmac);
    }


    public static String getReqMessage(String voiceMessage) {
        try {
            ObjectMapper mapper = new ObjectMapper();
    
            long timestamp = new Date().getTime();
    
            ObjectNode obj = mapper.createObjectNode();
            obj.put("version", "v2");
            obj.put("userId", "U458c90f8e7bddc8131heo281231heo287bddc123");
            obj.put("timestamp", timestamp);
    
            ObjectNode dataObj = mapper.createObjectNode();
            dataObj.put("description", voiceMessage);
    
            ObjectNode bubblesObj = mapper.createObjectNode();
            bubblesObj.put("type", "text");
            bubblesObj.set("data", dataObj);
    
            ArrayNode bubblesArray = mapper.createArrayNode();
            bubblesArray.add(bubblesObj);
    
            obj.set("bubbles", bubblesArray);
            obj.put("event", "send");
    
            return mapper.writeValueAsString(obj);
    
        } catch (Exception e) {
            log.error("[Chatbot] 요청 JSON 생성 실패", e);
            return "";
        }
    }

 

GPT와 구글링으로 완성,,

 

 

 

대화 시나리오 구성하기

빌드하기

배포하기

 

 

다 잊지말기!!

 

대화 시나리오는 

 

 

예시로 있는거랑 내가 좀 추가한거

 

변수로 요일, 지역, 단어 다 지정할 수 있다

좀 만지작만지작 해봐야 알 수 있을 것 같다

 

 

그리고 찾아본 내용

 

 

 

UI는 제공하는가?

 

1) 클로바 톡톡, LINE 등 공식 채널 사용 시 : 응답 JSON만 반환하면, 플랫폼에서 알아서 UI로 렌더링해줌.2) 자체 웹 챗봇 사용 시 : JSON 응답만 제공됨, 해당 JSON을 받아 출력할 화면 개발 필요함.

 

 

 

응답 컴포넌트별 지원 내용

 

1) Basic
텍스트 응답만 필요한 기본 질의. (text, image, link 지원)
ex)주택 신청기간은 언제인가요? → 2025년 3월 예정입니다.

 

2) Composite
버튼·링크 가능. (text, image, button 지원)
ex)주택 신청기간은 언제인가요? → 단계별 버튼(공고 확인, 신청하러가기)

 

3) Flex
정보가 카드형 리스트로 제공. (리스트, 카드, 슬라이드 등 커스터마이징 가능)
메신저 연동 등 고급형 서비스용

반응형