-
Golang: gin으로 카카오 챗봇 서버 만들기컴퓨터/Go language 2021. 1. 1. 22:57728x90반응형
gin
카카오 챗봇 Python으로 만들기 링크
카카오 챗봇 Rust언어로 만들기 링크
카카오 챗봇 예제 서버 Python FastAPI 소스 링크
카카오 챗봇 풀 예제 Github 링크
1. gin
gin은 Python의 Flask, FastAPI처럼 웹 서버를 만들 수 있는 웹 프레임워크이다.
웹 프레임워크 벤치마크에 따르면, gin은 39위, FastAPI는 130위이다.
Go 프레임워크로 젤 빠른 fiber나 echo, router, fasthttp 등이 있지만,
자료가 제일 많은 gin으로 만들어보았다.
2. Go 패키지 및 기본 설정
go get -u github.com/gin-gonic/gin
gin을 설치하고
아래 예제 코드처럼 만들면 GET /ping이 있는 서버가 열린다.
package main import "github.com/gin-gonic/gin" func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080") }
미들웨어 설정
JSON 형식만 request 되기 때문에 그냥 항상 application/json으로 바꿔준다.
(PureJSON, JSON...등을 보니 그 안에서도 content-type을 작성해주기 때문에 안해도 된다.)
// JSONMiddleware is to set all types of requests are JSON. func JSONMiddleware() gin.HandlerFunc { return func(c *gin.Context) { c.Writer.Header().Set("Content-Type", "application/json") c.Next() } } func main() { router := gin.Default() router.Use(JSONMiddleware()) ... }
3. 카카오 챗봇 JSON 파싱
nested 된 json을 어떻게 파싱 할지 고민하다가, FastAPI BaseModel처럼 형식을 짰다.
구조
KakaoJSON (베이스 모델) └---- Action (json:action) └---- Bot (json:bot) └---- Contexts (json:contexts) └---- Intent (json:intent) └---- UserRequest (json:userRequest) contexts는 input/output 없으면 왠만하면 [] 빈 배열이다.
형식
// KakaoJSON request main type KakaoJSON struct { Action struct { ID string `json:"id"` ClientExtra struct { } `json:"clientExtra"` DetailParams map[string]interface{} `json:"detailParams"` Name string `json:"name"` Params map[string]interface{} `json:"params"` } `json:"action"` Bot struct { ID string `json:"id"` Name string `json:"name"` } `json:"bot"` Contexts []interface{} `json:"contexts"` Intent struct { ID string `json:"id"` Extra struct { Reason struct { Code int64 `json:"code"` Message string `json:"message"` } `json:"reason"` } `json:"extra"` Name string `json:"name"` } `json:"intent"` UserRequest struct { Block struct { ID string `json:"id"` Name string `json:"name"` } `json:"block"` Lang string `json:"lang"` Params struct { IgnoreMe bool `json:"ignoreMe,string"` Surface string `json:"surface"` } `json:"params"` Timezone string `json:"timezone"` User struct { ID string `json:"id"` Properties struct { BotUserKey string `json:"botUserKey"` BotUserKey2 string `json:"bot_user_key"` } `json:"properties"` Type string `json:"type"` } `json:"user"` Utterance string `json:"utterance"` } `json:"userRequest"` }
4. 서버 테스트
route GET=("/") : 환영 메시지만 보여준다.
route POST=("/json") : 카카오 챗봇 API json 형식이 잘 되는지 테스트하는 route
아래 코드를 실행하면 localhost:8000에 서버가 열린다.
# 파이썬에선 쉽게 바로 접근할 수가 있다. request["intent"]["extra"]["reason"]["code"] request["action"]["params"]["sys_text"] request["userRequest"]["utterance"] request["userRequest"]["user"]["id"]
data.json에는 예제 카카오 챗봇 json이 담겨 있고, curl을 해서 post가 잘 되는지 확인한다.
WIN10@DESKTOP:~$ curl http://localhost:8000/json -d "@data.json" {"message":"Reason:1 | Params['sys_text']:2021 | Utterance:2021 검색\n | UserID:idUser"}
이후 AWS나 구름IDE를 이용해서 서버를 열고
원하는 발화문, 블록 등을 만들어서 POST route를 만들면 끝이다.
package main import ( "fmt" "net/http" "github.com/gin-gonic/gin" ) // KakaoJSON request main type KakaoJSON struct { Action struct { ID string `json:"id"` ClientExtra struct { } `json:"clientExtra"` DetailParams map[string]interface{} `json:"detailParams"` Name string `json:"name"` Params map[string]interface{} `json:"params"` } `json:"action"` Bot struct { ID string `json:"id"` Name string `json:"name"` } `json:"bot"` Contexts []interface{} `json:"contexts"` Intent struct { ID string `json:"id"` Extra struct { Reason struct { Code int64 `json:"code"` Message string `json:"message"` } `json:"reason"` } `json:"extra"` Name string `json:"name"` } `json:"intent"` UserRequest struct { Block struct { ID string `json:"id"` Name string `json:"name"` } `json:"block"` Lang string `json:"lang"` Params struct { IgnoreMe bool `json:"ignoreMe,string"` Surface string `json:"surface"` } `json:"params"` Timezone string `json:"timezone"` User struct { ID string `json:"id"` Properties struct { BotUserKey string `json:"botUserKey"` BotUserKey2 string `json:"bot_user_key"` } `json:"properties"` Type string `json:"type"` } `json:"user"` Utterance string `json:"utterance"` } `json:"userRequest"` } // JSONMiddleware is to set all types of requests are JSON. func JSONMiddleware() gin.HandlerFunc { return func(c *gin.Context) { c.Writer.Header().Set("Content-Type", "application/json") c.Next() } } func main() { router := gin.Default() router.Use(JSONMiddleware()) router.GET("/", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "Server is running well.", }) }) router.POST("/json", func(c *gin.Context) { var json KakaoJSON if err := c.BindJSON(&json); err != nil { c.AbortWithStatusJSON(http.StatusOK, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"json": json, "user entered": json.UserRequest.Utterance, "params": json.Action.Params}) }) // SimpleText 만들고 보내는 과정 예제 router.POST("/simple", func(c *gin.Context) { var json KakaoJSON if err := c.BindJSON(&json); err != nil { c.AbortWithStatusJSON(http.StatusOK, gin.H{"error": err.Error()}) return } u := SimpleText{Version: "2.0"} u.Template.Outputs.SimpleText.Text = fmt.Sprintf("Entered: %v", json.UserRequest.Utterance) c.JSON(http.StatusOK, gin.H{"message": u}) }) router.Run(":8000") }
ex) simpleText 메시지 보내기
ListCard나 다른 부분들은 꽤나 복잡해진다.
파이썬 FastAPI 버전이 정신 건강에 좋다.아니면 맨 아래 참고에 만든 모듈 참고
모듈로 카톡 챗봇 JSON 만들기 링크
// SimpleText for Kakao Response type SimpleText struct { Template struct { Outputs []struct { SimpleText Text `json:"simpleText"` } `json:"outputs"` } `json:"template"` Version string `json:"version"` } // BuildSimpleText ... func BuildSimpleText(msg string) *SimpleText { stext := &SimpleText{Version: "2.0"} var temp []struct { SimpleText Text `json:"simpleText"` } simpleText := Text{Text: msg} text := struct { SimpleText Text `json:"simpleText"` }{SimpleText: simpleText} temp = append(temp, text) stext.Template.Outputs = temp return stext } c.JSON(http.StatusOK, gin.H{"message": BuildSimpleText(fmt.Sprintf("Entered: %v", json.UserRequest.Utterance))}) // {"template":{"outputs":{"simpleText":{"text":"Entered: 2021 검색\n"}}},"version":"2.0"}
ex) ListCard 메시지 보내기
gin.H = map[string]interface{}
아래는 2개의 카드와, 1개의 QuickReply를 추가했다.
(아니면 맨 아래 참고에 만든 모듈 참고)
// ListCard 만들고 보내는 과정 예제 router.POST("/card", func(c *gin.Context) { var kjson KakaoJSON if err := c.BindJSON(&kjson); err != nil { errorMsg := SimpleText{Version: "2.0"} errorMsg.Template.Outputs.SimpleText.Text = err.Error() c.JSON(http.StatusBadRequest, errorMsg) return } // Card items := []gin.H{} header := gin.H{"title": "header title"} // Card items item := gin.H{"title": "card", "description": "desc", "imageUrl": "img", "link": gin.H{"web": "webhref"}} item2 := gin.H{"title": "card2", "description": "desc2"} // Add two cards items = append(items, item) items = append(items, item2) // QuickReplies [Optional] quickReplies := []gin.H{} // Add one quick reply quickReply := gin.H{"messageText": "안녕하세요", "action": "message", "label": "안녕"} quickReplies = append(quickReplies, quickReply) // Make a template template := gin.H{"outputs": []gin.H{gin.H{"listCard": gin.H{"header": header, "items": items}}}} template["quickReplies"] = quickReplies // Optional listCard := gin.H{"version": "2.0", "template": template} c.JSON(http.StatusOK, listCard) }) // 결과 // {"template":{"outputs":[{"listCard:":{"header":{"title":"header title"},"items":[{"description":"desc","imageUrl":"img","link":{"web":"webhref"},"title":"card"},{"description":"desc2","title":"card2"}]}}],"quickReplies":[{"action":"message","label":"안녕","messageText":"안녕하세요"}]},"version":"2.0"}
ex) ListCard JSON marshal 테스트
nested struct을 이용하고, anonymous struct 모음이라 파이썬을 쓰는게 더 나을 것 같다.
listCard json tag를 만들기 위해 Outputs을 따로 anonymous 처리했음.
package main import ( "fmt" "github.com/clarketm/json" ) type Template struct { Outputs []struct { ListCard ListCardJSON `json:"listCard"` } `json:"outputs"` QuickReplies []QuickReply `json:"quickReplies,omitempty"` } type QuickReply struct { Action string `json:"action"` Label string `json:"label"` MessageText string `json:"messageText"` } type ListCardJSON struct { Buttons []Button `json:"buttons"` Header Header `json:"header,omitempty"` Items []Item `json:"items"` } type Button struct { Label string `json:"label"` Action string `json:"action"` } type Item struct { Description string `json:"description"` ImageUrl string `json:"imageUrl,omitempty"` Link Link `json:"link,omitempty"` Title string `json:"title"` } type Link struct { Web string `json:"web"` } type Header struct { Title string `json:"title"` } // KakaoListCard is main type KakaoListCard struct { Template Template `json:"template"` Version string `json:"version"` // 2.0 } func main() { data := KakaoListCard{Version: "2.0"} // Card Buttons buttons := []Button{{Label: "hey1", Action: "share"}, {Label: "hey2", Action: "share"}} // Card Contents items := []Item{ {Description: "desc1", ImageUrl: "img", Link: Link{Web: "web"}, Title: "title1"}, {Description: "desc2", Title: "title2"}, } listCard := ListCardJSON{ Buttons: buttons, Header: Header{Title: "hi"}, Items: items, } // Add outputs to template var temp []struct { ListCard ListCardJSON `json:"listCard"` } lc := struct { ListCard ListCardJSON `json:"listCard"` }{ListCard: listCard} temp = append(temp, lc) data.Template.Outputs = temp // To make a tag [{"listCard"}] // Make QuickReplies quickreplies := []QuickReply{{ MessageText: "어제 보여줘", Action: "message", Label: "어제", }} data.Template.QuickReplies = quickreplies b, _ := json.MarshalIndent(data, "", " ") fmt.Println(string(b)) }
참고
카카오톡 챗봇 API 응답 JSON 빌더 모듈 @Github 개인
아래 모듈을 사용하시면 더 빠르고 간편하게 챗봇을 만들 수 있습니다.
카카오톡 챗봇 서버 @Github 개인
카카오톡 챗봇 서버 Python FastAPI 버전 @Github
728x90'컴퓨터 > Go language' 카테고리의 다른 글
Golang: soup를 이용한 네이버 날씨 정보 가져오기 (0) 2021.02.14 Golang: JSON <, >, & HTML 기호 escape 하기 (0) 2021.02.09 Golang: Nested structs to JSON (중첩 struct Json만들기) (0) 2021.01.03