-
ChatBot 만들기 - 3)frontend template 만들기 (textQuery, eventQuery Function + 리덕스로 메세지 저장하고 보여주기)NODE.JS 2021. 5. 15. 10:00
이제는 프론트엔드 부분을 만들어 보자.
App.js안에 대화창을 그대로 만들면 복잡해질 수 있으니,
src>Chatbot>Chatbot.js파일을 만들고 App.js안에 넣어주자. (컴포넌트로)
import React from "react"; import { Typography, Icon } from 'antd'; import Chatbot from './Chatbot/Chatbot'; const { Title } = Typography; function App() { return ( <div> <div style={{ display: 'flex', justifyContent: 'center', marginTop: '2rem' }}> <Title level={2} >CHAT BOT APP <Icon type="robot" /></Title> </div> <div style={{ display: 'flex', justifyContent: 'center' }}> <Chatbot /> </div> </div> ) } export default App
대화창 템플릿을 만들어보자.
import React, { useEffect } from 'react'; function Chatbot() { return ( <div style={{ height: 700, width: 700, border: '3px solid black', borderRadius: '7px' }}> <div style={{ height: 644, width: '100%', overflow: 'auto' }}> </div> <input style={{ margin: 0, width: '100%', height: 50, borderRadius: '4px', padding: '5px', fontSize: '1rem' }} placeholder="Send a message..." onKeyPress={keyPressHanlder} type="text" /> </div> ) } export default Chatbot;
입력창에 텍스트를 입력하고 엔터 누르면 그대로 대화창에 나타나도록 설정해주자.
//텍스트 입력하고 엔터 입력하면 대화창에 텍스트 나오도록 const keyPressHanlder = (e) => { if (e.key === "Enter") { if (!e.target.value) { return alert('you need to type somthing first') } //we will send request to text query route textQuery(e.target.value) e.target.value = ""; //한번 보내고 나면 비워준다. } } <input style={{ margin: 0, width: '100%', height: 50, borderRadius: '4px', padding: '5px', fontSize: '1rem' }} placeholder="Send a message..." onKeyPress={keyPressHanlder} type="text" />
이제 textQuery 함수를 만들어주자 .
import React, { useEffect } from 'react'; import Axios from 'axios'; function Chatbot() { const textQuery = async (text) => { // First Need to take care of the message I sent 내가 보낸 메세지를 보여준다. let conversation = { who: 'user', //누가 보냈는지 content: { //보낸 내용(텍스트 안에 텍스트 그다음 진짜 텍스트) text: { text: text } } } // We need to take care of the message Chatbot sent 내가 받은 메세지를 보여준다. const textQueryVariables = { text } try { //I will send request to the textQuery ROUTE 여기서 async 이기 때문에 await를 해준다. const response = await Axios.post('/api/dialogflow/textQuery', textQueryVariables) for (let content of response.data.fulfillmentMessages) {//결과로 들어오는 정보들이 많기 때문에 for문 돌린다. conversation = { who: 'bot', content: content } } catch (error) { conversation = { who: 'bot', content: { text: { text: " Error just occured, please check the problem" } } } } }
event query 기능도 만들자.
const eventQuery = async (event) => { // We need to take care of the message Chatbot sent const eventQueryVariables = { event } try { //I will send request to the eventQuery ROUTE const response = await Axios.post('/api/dialogflow/eventQuery', eventQueryVariables) for (let content of response.data.fulfillmentMessages) { let conversation = { who: 'bot', content: content } dispatch(saveMessage(conversation)) } } catch (error) { let conversation = { who: 'bot', content: { text: { text: " Error just occured, please check the problem" } } } } }
여기서 textquery는 우리가 엔터를 누를때 실행이 되는데,
eventQuery는 언제 실행되는지 아직 설정이 되어있지않다.
우리가 웹사이트에 들어갈때마다 실행이 되도록 설정해주자.
(event설정)
function Chatbot() { useEffect(() => {//웹이 시작될때 trigger할 것이다. 이벤트 이름을 넣어준다. eventQuery('welcomeToMyWebsite') }, [])
아직 화면창에 메세지를 보여주는 것이 없는데,
이제 메세지를 보여줄 때 리덕스를 사용헤서 메세지를 저장하고 보여주도록 설정하자.
(페이지를 리프레시 할 때마다 대화는 새로 시작되므로 db에 넣지는 않을 것이다)
(메세지를 db에 저장하지않고 저장하는 방법은 state에 저장하는 방법과 redux로 저장하는 방법 두가지가 있다)
client>index.js 파일안에 redux를 포함시켜준다.
import React from "react"; import ReactDOM from "react-dom"; import './index.css'; import "antd/dist/antd.css"; import App from "./App"; import Reducer from './_reducers'; import { Provider } from 'react-redux'; import { createStore, applyMiddleware } from 'redux'; import promiseMiddleware from 'redux-promise'; import ReduxThunk from 'redux-thunk'; import { BrowserRouter } from "react-router-dom"; import * as serviceWorker from "./serviceWorker"; const createStoreWithMiddleware = applyMiddleware(promiseMiddleware, ReduxThunk)(createStore); ReactDOM.render( <Provider store={createStoreWithMiddleware( Reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() )} > <BrowserRouter> <App /> </BrowserRouter> </Provider> , document.getElementById("root") ); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: https://bit.ly/CRA-PWA serviceWorker.unregister();
또 리덕스를 사용할때 쓰는 메소드를 actions>message_actions.js파일안에 넣어준다.
import { SAVE_MESSAGE, } from './types'; export function saveMessage(dataToSubmit) { return { type: SAVE_MESSAGE, payload: dataToSubmit } }
actions>types.js도 다음과 같이 변경해준다.
export const SAVE_MESSAGE = 'save_message';
reducers>message_reducer.js파일을 만들고 다음과 같이 추가해준다.
import { SAVE_MESSAGE, } from '../_actions/types'; //원래는 빈 배열이었던것 안에 text 내용들(action.payload)를 더해준다. export default function (state = {messages:[]}, action) { switch (action.type) { case SAVE_MESSAGE: return { ...state, messages: state.messages.concat(action.payload) } default: return state; } }
다시 chatbot.js는 이를 이용해서 리덕스를 사용한다. (dispatch)
내가 작성한 text뿐만아니라 챗봇이 입력한 내용도 text에 concat되도록 모두 입력해준다.
import { useDispatch, useSelector } from 'react-redux'; import { saveMessage } from '../_actions/message_actions'; unction Chatbot() { const dispatch = useDispatch(); const messagesFromRedux = useSelector(state => state.message.messages) const textQuery = async (text) => { // First Need to take care of the message I sent 내가 보낸 메세지를 보여준다. let conversation = { who: 'user', //누가 보냈는지 content: { //보낸 내용(텍스트 안에 텍스트 그다음 진짜 텍스트) text: { text: text } } } dispatch(saveMessage(conversation)) // console.log('text I sent', conversation) // We need to take care of the message Chatbot sent 내가 받은 메세지를 보여준다. const textQueryVariables = { text } try { //I will send request to the textQuery ROUTE 여기서 async 이기 때문에 await를 해준다. const response = await Axios.post('/api/dialogflow/textQuery', textQueryVariables) for (let content of response.data.fulfillmentMessages) {//결과로 들어오는 정보들이 많기 때문에 for문 돌린다. conversation = { who: 'bot', content: content } dispatch(saveMessage(conversation)) } } catch (error) { conversation = { who: 'bot', content: { text: { text: " Error just occured, please check the problem" } } } dispatch(saveMessage(conversation)) } } const eventQuery = async (event) => { // We need to take care of the message Chatbot sent const eventQueryVariables = { event } try { //I will send request to the eventtQuery ROUTE const response = await Axios.post('/api/dialogflow/eventQuery', eventQueryVariables) for (let content of response.data.fulfillmentMessages) { let conversation = { who: 'bot', content: content } dispatch(saveMessage(conversation)) } } catch (error) { let conversation = { who: 'bot', content: { text: { text: " Error just occured, please check the problem" } } } dispatch(saveMessage(conversation)) } }
이제는 저장된 내용들을 chat panel에 나오도록 지정해본다.
메세지를 보여주는 컴포넌트를 챗봇.js안에 넣으면 복잡해질 수 있어서
따로 빼서 작성해준다.
챗봇.js를 다음과 같이 수정해준다.
import React, { useEffect } from 'react'; import Axios from 'axios'; import { useDispatch, useSelector } from 'react-redux'; import { saveMessage } from '../_actions/message_actions'; import Message from './Sections/Message'; function Chatbot() { const dispatch = useDispatch(); //모든 메세지를 리덕스로부터 가져오기 위해서 useSelector를 사용한다. const messagesFromRedux = useSelector(state => state.message.messages) const renderMessage = (returnedMessages) => { //하나씩 다 드러내기 위해서 map 을 이용한다. if (returnedMessages) { return returnedMessages.map((message, i) => { return renderOneMessage(message, i); }) } else { return null; } } return ( {renderMessage(messagesFromRedux)} ) export default Chatbot;
여기서 renderOneMessage를 나타내주도록 하자.
디자인은 antd를 사용할 것이다.
import { List, Icon, Avatar } from 'antd'; const renderOneMessage = (message, i) => { console.log('message', message) return <div> <List.Item key = {i} style={{ padding: '1rem' }}> <List.Item.Meta avatar={<Avatar />} title={message.who} description={message.content.text.text} /> </List.Item> </div> } // template for card message }
여기서 채팅창에 드러나게 되는 메세지 구조는 계속 사용되므로
sections>message.js파일을 만들어서 옮겨주자.
import React from 'react' import { List, Icon, Avatar } from 'antd'; function Message(props) { //채팅창의 프로필을 나타내기 위해서 말하는 사람이 아바타냐 아니냐에 따라서 icon다르게 보이게 설정 const AvatarSrc = props.who ==='bot' ? <Icon type="robot" /> : <Icon type="smile" /> return ( <List.Item style={{ padding: '1rem' }}> <List.Item.Meta avatar={<Avatar icon={AvatarSrc} />} title={props.who} description={props.text} /> </List.Item> ) } export default Message
chatbot.js도 다음과 같이 변경해준다.
import Message from './Sections/Message'; return <Message key={i} who={message.who} text={message.content.text.text} />
'NODE.JS' 카테고리의 다른 글
ChatBot 만들기 - 5) actions추가하기 (0) 2021.05.15 ChatBot 만들기 - 4) card 메세지 나타내기 (0) 2021.05.15 ChatBot 만들기 - 2) textQuery route와 eventQuery route만들기 (0) 2021.05.15 ChatBot 만들기 - 1)dialogflow setting (0) 2021.05.15 Youtube 만들기 - 11) 좋아요, 싫어요 기능 만들기 (0) 2021.05.15