ReactJS
Real Time
리액트 실시간 채팅 앱 개발 및 Parse React 훅 활용
11 분
리액트 채팅 앱 실시간 소개 지난 가이드에서는 @parse/react @parse/react 헬퍼 라이브러리에 대해 더 알게 되었습니다 이 라이브러리는 리액트 애플리케이션에서 parse live query 지원을 빠르게 활성화합니다 이 라이브러리는 전적으로 typescript로 작성되었으며, parse javascript sdk , 현재 알파 버전입니다 이제 이 가이드에서는 parse react 훅을 사용하여 간단한 실시간 채팅 애플리케이션을 만드는 현실적인 상황을 다룰 것입니다 이 앱은 parse의 live query의 유용성을 강조하는 두 개의 리액트 컴포넌트로 구성되어 있으며, 완전한 실시간 앱을 만드는 데 필요한 모든 것을 보여줍니다 parse react native는 현재 알파 버전입니다 이 라이브러리는 테스트 중이므로 주의해서 진행하는 것이 좋습니다 여러분의 피드백은 매우 감사하므로, 이 라이브러리를 사용하고 질문이나 첫 인상을 community\@back4app com으로 이메일로 보내주시면 됩니다 전제 조건 이 튜토리얼을 완료하려면 다음이 필요합니다 생성된 리액트 앱과 back4app에 연결된 이전 가이드를 완료하여 parse react 훅과 live query에 대한 더 나은 이해 이 가이드에서 제공하는 화면 레이아웃을 테스트/사용하려면 ant design ant design 라이브러리를 설정해야 합니다 목표 리액트에서 @parse/react @parse/react 훅을 사용하여 실시간 채팅 애플리케이션을 구축하는 것입니다 1 데이터베이스 클래스 이해 및 생성 채팅 애플리케이션은 두 개의 작은 데이터베이스 클래스로 구성됩니다 닉네임 닉네임 및 메시지 메시지 닉네임 닉네임 은 이름 이름 이라는 텍스트 필드만 가지고 있으며, 애플리케이션의 사용자들을 나타냅니다 메시지 메시지 는 두 사용자 간에 전송된 모든 텍스트 메시지를 보유하므로, 텍스트 텍스트 이라는 텍스트 필드와 보낸 사람 보낸 사람 및 받는 사람 받는 사람 , 두 개의 객체 포인터 필드가 필요합니다 이들은 모두 닉네임 닉네임 클래스와 관련이 있습니다 다음 스니펫을 back4app 대시보드의 javascript 콘솔에서 실행하여 이러한 클래스를 생성하고 샘플로 채워주세요 1 // create nicknames 2 let nicknamea = new parse object('nickname'); 3 nicknamea set('name', 'smartboy22'); 4 nicknamea = await nicknamea save(); 5 let nicknameb = new parse object('nickname'); 6 nicknameb set('name', 'clevergirl23'); 7 nicknameb = await nicknameb save(); 8	 9 // create message linked to nicknames 10 let message = new parse object('message'); 11 message set('text', 'hi! how are you?'); 12 message set('sender', nicknamea); 13 message set('receiver', nicknamea); 14 message = await message save(); 15 console log('success'); 2 실시간 쿼리 활성화 이제 닉네임 닉네임 과 메시지 메시지 클래스를 생성했으므로, 실시간 쿼리 기능을 활성화해야 합니다 back4app 대시보드로 이동하여 앱 설정 앱 설정 > 서버 설정 서버 설정 > 서버 url 및 실시간 쿼리 서버 url 및 실시간 쿼리 back4app 서브도메인을 활성화한 후, 실시간 쿼리를 활성화하고 어떤 db 클래스를 활성화할지 선택할 수 있습니다 새 클래스를 선택하고 변경 사항을 저장하는 것을 잊지 마세요 다음으로 할 일은 두 개의 구성 요소로 구성된 채팅 앱을 만드는 것입니다 chatsetup chatsetup 과 livechat livechat 입니다 3 채팅 설정 구성 요소 만들기 이 구성 요소는 발신자와 수신자 nickname nickname 객체를 생성하고 설정하는 역할을 하며, livechat livechat 구성 요소 구조의 컨테이너 역할도 합니다 설정 부분의 레이아웃은 닉네임을 위한 두 개의 입력 필드와 설정 기능을 트리거하는 버튼만 있습니다 src src 디렉토리에 chatsetup js chatsetup js 라는 새 파일을 만들고 (또는 chatsetup tsx chatsetup tsx ) 다음 코드를 사용하세요 chatsetup js 1 import react, { usestate } from "react"; 2 import " /app css"; 3 import { button, input } from "antd"; 4 import parse from "parse"; 5 import { livechat } from " /livechat"; 6	 7 export const chatsetup = () => { 8 // state variables holding input values and results 9 const \[sendernicknameinput, setsendernicknameinput] = usestate(""); 10 const \[sendernicknameid, setsendernicknameid] = usestate(null); 11 const \[receivernicknameinput, setreceivernicknameinput] = usestate(""); 12 const \[receivernicknameid, setreceivernicknameid] = usestate(null); 13	 14 // create or retrieve nickname objects and start livechat component 15 const startlivechat = async () => { 16 const sendernicknamename = sendernicknameinput; 17 const receivernicknamename = receivernicknameinput; 18	 19 // check if user informed both nicknames 20 if (sendernicknamename === null || receivernicknamename === null) { 21 alert("please inform both sender and receiver nicknames!"); 22 return false; 23 } 24	 25 // check if sender nickname already exists, if not create new parse object 26 let sendernicknameobject = null; 27 try { 28 const senderparsequery = new parse query("nickname"); 29 senderparsequery equalto("name", sendernicknamename); 30 const senderparsequeryresult = await senderparsequery first(); 31 if ( 32 senderparsequeryresult !== undefined && 33 senderparsequeryresult !== null 34 ) { 35 sendernicknameobject = senderparsequeryresult; 36 } else { 37 sendernicknameobject = new parse object("nickname"); 38 sendernicknameobject set("name", sendernicknamename); 39 sendernicknameobject = await sendernicknameobject save(); 40 } 41 } catch (error) { 42 alert(error); 43 return false; 44 } 45	 46 // check if receiver nickname already exists, if not create new parse object 47 let receivernicknameobject = null; 48 try { 49 const receiverparsequery = new parse query("nickname"); 50 receiverparsequery equalto("name", receivernicknamename); 51 const receiverparsequeryresult = await receiverparsequery first(); 52 if ( 53 receiverparsequeryresult !== undefined && 54 receiverparsequeryresult !== null 55 ) { 56 receivernicknameobject = receiverparsequeryresult; 57 } else { 58 receivernicknameobject = new parse object("nickname"); 59 receivernicknameobject set("name", receivernicknamename); 60 receivernicknameobject = await receivernicknameobject save(); 61 } 62 } catch (error) { 63 alert(error); 64 return false; 65 } 66	 67 // set nickname objects ids, so live chat component is instantiated 68 setsendernicknameid(sendernicknameobject id); 69 setreceivernicknameid(receivernicknameobject id); 70 return true; 71 }; 72	 73 return ( 74 \<div> 75 \<div classname="header"> 76 \<img 77 classname="header logo" 78 alt="back4app logo" 79 src={ 80 "https //blog back4app com/wp content/uploads/2019/05/back4app white logo 500px png" 81 } 82 /> 83 \<p classname="header text bold">{"react on back4app"}\</p> 84 \<p classname="header text">{"live query chat app"}\</p> 85 \</div> 86 \<div classname="container"> 87 {sendernicknameid === null && receivernicknameid === null && ( 88 \<div> 89 \<input 90 classname="form input" 91 value={sendernicknameinput} 92 onchange={(event) => setsendernicknameinput(event target value)} 93 placeholder={"sender (your) nickname"} 94 size="large" 95 /> 96 \<input 97 classname="form input" 98 value={receivernicknameinput} 99 onchange={(event) => setreceivernicknameinput(event target value)} 100 placeholder={"receiver (their) nickname"} 101 size="large" 102 /> 103 \<button 104 type="primary" 105 classname="form button" 106 color={"#208aec"} 107 size={"large"} 108 onclick={startlivechat} 109 > 110 start live chat 111 \</button> 112 \</div> 113 )} 114 {sendernicknameid !== null && receivernicknameid !== null && ( 115 \<livechat 116 sendernicknamename={sendernicknameinput} 117 sendernicknameid={sendernicknameid} 118 receivernicknamename={receivernicknameinput} 119 receivernicknameid={receivernicknameid} 120 /> 121 )} 122 \</div> 123 \</div> 124 ); 125 }; chatsetup tsx 1 import react, { usestate, fc, reactelement } from "react"; 2 import " /app css"; 3 import { button, input } from "antd"; 4 import { livechat } from " /livechat"; 5 import parse from "parse"; 6	 7 export const chatsetup fc<{}> = () reactelement => { 8 // state variables holding input values and results 9 const \[sendernicknameinput, setsendernicknameinput] = usestate(""); 10 const \[sendernicknameid, setsendernicknameid] = usestate\<string | null>(null); 11 const \[receivernicknameinput, setreceivernicknameinput] = usestate(""); 12 const \[receivernicknameid, setreceivernicknameid] = usestate\<string | null>(null); 13	 14 // create or retrieve nickname objects and start livechat component 15 const startlivechat = async () promise\<boolean> => { 16 const sendernicknamename string = sendernicknameinput; 17 const receivernicknamename string = receivernicknameinput; 18	 19 // check if user informed both nicknames 20 if (sendernicknamename === "" || receivernicknamename === "") { 21 alert("please inform both sender and receiver nicknames!"); 22 return false; 23 } 24	 25 // check if sender nickname already exists, if not create new parse object 26 let sendernicknameobject parse object | null = null; 27 try { 28 const senderparsequery parse query = new parse query("nickname"); 29 senderparsequery equalto("name", sendernicknamename); 30 const senderparsequeryresult parse object | undefined = await senderparsequery first(); 31 if ( 32 senderparsequeryresult !== undefined 33 ) { 34 sendernicknameobject = senderparsequeryresult; 35 } else { 36 sendernicknameobject = new parse object("nickname"); 37 if (sendernicknameobject !== null) { 38 sendernicknameobject set("name", sendernicknamename); 39 sendernicknameobject = await sendernicknameobject save(); 40 } 41 } 42 } catch (error) { 43 alert(error); 44 return false; 45 } 46	 47 // check if receiver nickname already exists, if not create new parse object 48 let receivernicknameobject parse object | null = null; 49 try { 50 const receiverparsequery parse query = new parse query("nickname"); 51 receiverparsequery equalto("name", receivernicknamename); 52 const receiverparsequeryresult parse object | undefined = await receiverparsequery first(); 53 if ( 54 receiverparsequeryresult !== undefined 55 ) { 56 receivernicknameobject = receiverparsequeryresult; 57 } else { 58 receivernicknameobject = new parse object("nickname"); 59 if (receivernicknameobject !== null) { 60 receivernicknameobject set("name", receivernicknamename); 61 receivernicknameobject = await receivernicknameobject save(); 62 } 63 } 64 } catch (error any) { 65 alert(error); 66 return false; 67 } 68	 69 // set nickname objects ids, so live chat component is instantiated 70 if (sendernicknameobject !== null && receivernicknameobject !== null) { 71 setsendernicknameid(sendernicknameobject id); 72 setreceivernicknameid(receivernicknameobject id); 73 } 74 return true; 75 }; 76	 77 return ( 78 \<div> 79 \<div classname="header"> 80 \<img 81 classname="header logo" 82 alt="back4app logo" 83 src={ 84 "https //blog back4app com/wp content/uploads/2019/05/back4app white logo 500px png" 85 } 86 /> 87 \<p classname="header text bold">{"react on back4app"}\</p> 88 \<p classname="header text">{"live query chat app"}\</p> 89 \</div> 90 \<div classname="container"> 91 {sendernicknameid === null && receivernicknameid === null && ( 92 \<div> 93 \<input 94 classname="form input" 95 value={sendernicknameinput} 96 onchange={(event) => setsendernicknameinput(event target value)} 97 placeholder={"sender (your) nickname"} 98 size="large" 99 /> 100 \<input 101 classname="form input" 102 value={receivernicknameinput} 103 onchange={(event) => setreceivernicknameinput(event target value)} 104 placeholder={"receiver (their) nickname"} 105 size="large" 106 /> 107 \<button 108 type="primary" 109 classname="form button" 110 color={"#208aec"} 111 size={"large"} 112 onclick={startlivechat} 113 > 114 start live chat 115 \</button> 116 \</div> 117 )} 118 {sendernicknameid !== null && receivernicknameid !== null && ( 119 \<livechat 120 sendernicknamename={sendernicknameinput} 121 sendernicknameid={sendernicknameid} 122 receivernicknamename={receivernicknameinput} 123 receivernicknameid={receivernicknameid} 124 /> 125 )} 126 \</div> 127 \</div> 128 ); 129 }; 다음 사항에 유의하십시오 livechat livechat 구성 요소는 설정 프로세스가 성공적으로 완료되고 모든 상태 변수가 올바르게 설정되었을 때만 초기화되고 렌더링됩니다 마찬가지로, 설정 입력은 프로세스 후에 숨겨지며 자식 구성 요소 레이아웃이 렌더링됩니다 4 라이브 채팅 구성 요소 만들기 이 livechat livechat 구성 요소는 메시지 메시지 의 전시 및 전송을 처리합니다 이 메시지는 두 개의 닉네임 닉네임 으로 초기화 시 매개변수로 전달됩니다 이 구성 요소에서 useparsequery useparsequery 훅을 사용하여 @parse/react @parse/react 에서 live query를 설정하여 이 채팅 인스턴스와 관련된 메시지 메시지 객체를 검색합니다 src src 디렉토리에 다음과 같은 이름의 새 파일을 만드세요 livechat js livechat js (또는 livechat tsx livechat tsx ) 그리고 다음 코드를 삽입하세요 livechat js 1 import react, { usestate } from "react"; 2 import " /app css"; 3 import { button, input, tooltip } from "antd"; 4 import { syncoutlined } from "@ant design/icons"; 5 import parse from "parse"; 6 import { useparsequery } from "@parse/react"; 7	 8 export const livechat = (props) => { 9 // state variable to hold message text input 10 const \[messageinput, setmessageinput] = usestate(""); 11	 12 // create parse query for live querying using useparsequery hook 13 const parsequery = new parse query("message"); 14 // get messages that involve both nicknames 15 parsequery containedin("sender", \[ 16 props sendernicknameid, 17 props receivernicknameid, 18 ]); 19 parsequery containedin("receiver", \[ 20 props sendernicknameid, 21 props receivernicknameid, 22 ]); 23 // set results ordering 24 parsequery ascending("createdat"); 25	 26 // include nickname fields, to enable name getting on list 27 parsequery includeall(); 28	 29 // declare hook and variables to hold hook responses 30 const { islive, isloading, issyncing, results, count, error, reload } = 31 useparsequery(parsequery, { 32 enablelocaldatastore true, // enables cache in local datastore (default true) 33 enablelivequery true, // enables live query for real time update (default true) 34 }); 35	 36 // message sender handler 37 const sendmessage = async () => { 38 try { 39 const messagetext = messageinput; 40	 41 // get sender and receiver nickname parse objects 42 const sendernicknameobjectquery = new parse query("nickname"); 43 sendernicknameobjectquery equalto("objectid", props sendernicknameid); 44 let sendernicknameobject = await sendernicknameobjectquery first(); 45 const receivernicknameobjectquery = new parse query("nickname"); 46 receivernicknameobjectquery equalto("objectid", props receivernicknameid); 47 let receivernicknameobject = await receivernicknameobjectquery first(); 48	 49 // create new message object and save it 50 let message = new parse object("message"); 51 message set("text", messagetext); 52 message set("sender", sendernicknameobject); 53 message set("receiver", receivernicknameobject); 54 message save(); 55	 56 // clear input 57 setmessageinput(""); 58 } catch (error) { 59 alert(error); 60 } 61 }; 62	 63 // helper to format createdat value on message 64 const formatdatetotime = (date) => { 65 return `${date gethours()} ${date getminutes()} ${date getseconds()}`; 66 }; 67	 68 return ( 69 \<div> 70 \<div classname="flex between"> 71 \<h2 class="list heading">{`${props sendernicknamename} sending, ${props receivernicknamename} receiving!`}\</h2> 72 \<tooltip title="reload"> 73 \<button 74 onclick={reload} 75 type="primary" 76 shape="circle" 77 icon={\<syncoutlined />} 78 /> 79 \</tooltip> 80 \</div> 81 {results && ( 82 \<div classname="messages"> 83 {results 84 sort((a, b) => a get("createdat") > b get("createdat")) 85 map((result) => ( 86 \<div 87 key={result id} 88 classname={ 89 result get("sender") id === props sendernicknameid 90 ? "message sent" 91 "message received" 92 } 93 > 94 \<p classname="message bubble">{result get("text")}\</p> 95 \<p classname="message time"> 96 {formatdatetotime(result get("createdat"))} 97 \</p> 98 \<p classname="message name"> 99 {result get("sender") get("name")} 100 \</p> 101 \</div> 102 ))} 103 \</div> 104 )} 105 \<div classname="new message"> 106 \<h2 classname="new message title">new message\</h2> 107 \<input 108 classname="form input" 109 value={messageinput} 110 onchange={(event) => setmessageinput(event target value)} 111 placeholder={"your message "} 112 size="large" 113 /> 114 \<button 115 type="primary" 116 classname="form button" 117 color={"#208aec"} 118 size={"large"} 119 onclick={sendmessage} 120 > 121 send message 122 \</button> 123 \</div> 124 \<div> 125 {isloading && \<p>{"loading…"}\</p>} 126 {issyncing && \<p>{"syncing…"}\</p>} 127 {islive ? \<p>{"status live"}\</p> \<p>{"status offline"}\</p>} 128 {error && \<p>{error message}\</p>} 129 {count && \<p>{`count ${count}`}\</p>} 130 \</div> 131 \</div> 132 ); 133 }; livechat tsx 1 import react, { usestate, fc, reactelement } from "react"; 2 import " /app css"; 3 import { button, input, tooltip } from "antd"; 4 import { syncoutlined } from "@ant design/icons"; 5 import parse from "parse"; 6 import { useparsequery } from "@parse/react"; 7	 8 type livechatprops = { 9 sendernicknameid string, 10 sendernicknamename string, 11 receivernicknameid string, 12 receivernicknamename string, 13 } 14	 15 export const livechat fc\<livechatprops> = (props livechatprops) reactelement => { 16 const parse = require("parse/dist/parse min js"); 17 // state variable to hold message text input 18 const \[messageinput, setmessageinput] = usestate(""); 19	 20 // create parse query for live querying using useparsequery hook 21 const parsequery parse query = new parse query("message"); 22 // get messages that involve both nicknames 23 parsequery containedin("sender", \[ 24 props sendernicknameid, 25 props receivernicknameid, 26 ]); 27 parsequery containedin("receiver", \[ 28 props sendernicknameid, 29 props receivernicknameid, 30 ]); 31 // set results ordering 32 parsequery ascending("createdat"); 33	 34 // include nickname fields, to enable name getting on list 35 parsequery includeall(); 36	 37 // declare hook and variables to hold hook responses 38 const { islive, isloading, issyncing, results, count, error, reload } = 39 useparsequery(parsequery, { 40 enablelocaldatastore true, // enables cache in local datastore (default true) 41 enablelivequery true, // enables live query for real time update (default true) 42 }); 43	 44 // message sender handler 45 const sendmessage = async () => { 46 try { 47 const messagetext string = messageinput; 48	 49 // get sender and receiver nickname parse objects 50 const sendernicknameobjectquery parse query = new parse query("nickname"); 51 sendernicknameobjectquery equalto("objectid", props sendernicknameid); 52 let sendernicknameobject parse object | undefined = await sendernicknameobjectquery first(); 53 const receivernicknameobjectquery parse query = new parse query("nickname"); 54 receivernicknameobjectquery equalto("objectid", props receivernicknameid); 55 let receivernicknameobject parse object | undefined = await receivernicknameobjectquery first(); 56	 57 // create new message object and save it 58 let message parse object = new parse object("message"); 59 message set("text", messagetext); 60 message set("sender", sendernicknameobject); 61 message set("receiver", receivernicknameobject); 62 message save(); 63	 64 // clear input 65 setmessageinput(""); 66 } catch (error any) { 67 alert(error); 68 } 69 }; 70	 71 // helper to format createdat value on message 72 const formatdatetotime = (date date) string => { 73 return `${date gethours()} ${date getminutes()} ${date getseconds()}`; 74 }; 75	 76 return ( 77 \<div> 78 \<div classname="flex between"> 79 \<h2 class="list heading">{`${props sendernicknamename} sending, ${props receivernicknamename} receiving!`}\</h2> 80 \<tooltip title="reload"> 81 \<button 82 onclick={reload} 83 type="primary" 84 shape="circle" 85 icon={\<syncoutlined />} 86 /> 87 \</tooltip> 88 \</div> 89 {results && ( 90 \<div classname="messages"> 91 {results 92 sort((a, b) => a get("createdat") > b get("createdat")) 93 map((result) => ( 94 \<div 95 key={result id} 96 classname={ 97 result get("sender") id === props sendernicknameid 98 ? "message sent" 99 "message received" 100 } 101 > 102 \<p classname="message bubble">{result get("text")}\</p> 103 \<p classname="message time"> 104 {formatdatetotime(result get("createdat"))} 105 \</p> 106 \<p classname="message name"> 107 {result get("sender") get("name")} 108 \</p> 109 \</div> 110 ))} 111 \</div> 112 )} 113 \<div classname="new message"> 114 \<h2 classname="new message title">new message\</h2> 115 \<input 116 classname="form input" 117 value={messageinput} 118 onchange={(event) => setmessageinput(event target value)} 119 placeholder={"your message "} 120 size="large" 121 /> 122 \<button 123 type="primary" 124 classname="form button" 125 color={"#208aec"} 126 size={"large"} 127 onclick={sendmessage} 128 > 129 send message 130 \</button> 131 \</div> 132 \<div> 133 {isloading && \<p>{"loading…"}\</p>} 134 {issyncing && \<p>{"syncing…"}\</p>} 135 {islive ? \<p>{"status live"}\</p> \<p>{"status offline"}\</p>} 136 {error && \<p>{error message}\</p>} 137 {count && \<p>{`count ${count}`}\</p>} 138 \</div> 139 \</div> 140 ); 141 }; 이 구성 요소 구조를 네 부분으로 나누어 레이아웃을 더 잘 이해할 수 있도록 하겠습니다 맨 위에는 메시지의 parse query parse query 및 parse react 훅 설정이 있습니다 여기에서 props props 매개변수가 쿼리가 우리가 원하는 메시지를 검색할 수 있도록 하는 방법을 볼 수 있습니다; 그 후에 sendmessage sendmessage 함수가 있으며, 이는 새로운 message message 객체를 생성하여 이 채팅 인스턴스에서 사용되는 nickname nickname 과 관련이 있습니다 메시지 날짜 값을 포맷하는 도우미 함수도 있습니다; 이제 jsx 코드 안에는 parse react 훅 변수와 관련된 상태 플래그와 연결 재로드 버튼이 있습니다; 마지막으로, message message 목록을 볼 수 있으며, 렌더링된 목록 항목의 스타일은 발신자 값에 의해 결정됩니다 하단에는 간단한 텍스트 입력 및 버튼이 있는 메시지 전송 부분이 있습니다 마지막으로, 완전히 구성 요소 레이아웃을 렌더링하려면 이러한 클래스를 app css app css 파일에 추가하고, 앱을 테스트해 보겠습니다 app css 1 @import ' antd/dist/antd css'; 2	 3 html { 4 box sizing border box; 5 outline none; 6 overflow auto; 7 } 8	 9 body { 10 margin 0; 11 background color #fff; 12 } 13	 14 , 15 before, 16 after { 17 margin 0; 18 padding 0; 19 box sizing inherit; 20 } 21	 22 h1, 23 h2, 24 h3, 25 h4, 26 h5, 27 h6 { 28 margin 0; 29 font weight bold; 30 } 31	 32 li { 33 list style none; 34 } 35	 36 p { 37 margin 0; 38 } 39	 40 flex between { 41 display flex; 42 align items center; 43 justify content space between; 44 } 45	 46 list heading { 47 font weight bold; 48 } 49	 50 app { 51 text align center; 52 } 53	 54 container { 55 width 100%; 56 max width 500px; 57 margin auto; 58 padding 20px 0; 59 text align left; 60 } 61	 62 header { 63 align items center; 64 padding 25px 0; 65 background color #208aec; 66 } 67	 68 header logo { 69 height 55px; 70 margin bottom 20px; 71 object fit contain; 72 } 73	 74 header text bold { 75 margin bottom 3px; 76 color rgba(255, 255, 255, 0 9); 77 font size 16px; 78 font weight bold; 79 } 80	 81 header text { 82 color rgba(255, 255, 255, 0 9); 83 font size 15px; 84 } 85	 86 heading { 87 font size 22px; 88 } 89	 90 form wrapper { 91 margin top 20px; 92 margin bottom 10px; 93 } 94	 95 form input { 96 margin bottom 20px; 97 } 98	 99 form button { 100 width 100%; 101 } 102	 103 messages { 104 margin top 25px; 105 } 106	 107 message sent { 108 position relative; 109 width 50%; 110 margin left auto; 111 } 112	 113 message received { 114 position relative; 115 width 50%; 116 } 117	 118 message bubble { 119 padding 12px; 120 border radius 25px; 121 background color rgba(0, 0, 0, 0 2); 122 } 123	 124 message sent message bubble { 125 background color #1e88e5; 126 color #fff; 127 } 128	 129 message time { 130 position absolute; 131 top 35%; 132 left 62px; 133 font size 13px; 134 color rgba(0, 0, 0, 0 35); 135 transform translatey( 50%); 136 } 137	 138 message sent message time { 139 left initial; 140 right 62px; 141 } 142	 143 message name { 144 margin top 5px; 145 color rgba(0, 0, 0, 0 55); 146 font size 13px; 147 font weight 600; 148 } 149	 150 message sent message name { 151 text align right; 152 } 153	 154 new message { 155 padding top 15px; 156 margin top 20px; 157 margin bottom 10px; 158 border top 1px solid rgba(0, 0, 0, 0 12); 159 } 160	 161 new message title { 162 margin bottom 15px; 163 } 5 채팅 애플리케이션 테스트 실시간 채팅 앱을 테스트하려면 chatsetup chatsetup 컴포넌트를 app js app js (또는 app tsx app tsx ) jsx 코드에서 선언하고 호출하세요 다음은 그렇게 할 수 있는 방법의 예입니다 app js or app tsx 1 import react from "react"; 2 import " /app css"; 3 import { initializeparse } from "@parse/react"; 4 import { chatsetup } from " /chatsetup"; 5	 6 // your parse initialization configuration goes here 7 // note the live query url instead of the regular server url 8 const parse application id = "your parse application id"; 9 // const parse server url = "https //parseapi back4app com/"; 10 const parse live query url = "https //your app name b4a io/"; 11 const parse javascript key = "your parse javascript key"; 12	 13 // initialize parse using @parse/react instead of regular parse js sdk 14 initializeparse( 15 parse live query url, 16 parse application id, 17 parse javascript key 18 ); 19	 20 function app() { 21 return ( 22 \<div classname="app"> 23 \<chatsetup /> 24 \</div> 25 ); 26 } 27	 28 export default app; 콘솔에서 yarn start yarn start 를 실행하여 앱을 시작하세요 이제 다음 화면이 표시되며, 여기서 채팅을 시작하기 위해 보내는 사람과 받는 사람의 닉네임을 입력해야 합니다 앱과 실시간 쿼리가 어떻게 작동하는지 더 잘 보기 위해 두 개의 다른 브라우저 창에서 동일한 앱을 열고 나란히 배치하세요 한 창에서 메시지를 보내면, 닉네임이 일치하고 연결이 활성화되어 있다면 다른 창에서 즉시 나타나는 것을 볼 수 있어야 합니다 결론 이 가이드를 마치면서, 현실적인 애플리케이션 예제에서 parse의 실시간 쿼리를 위한 parse react 훅을 사용하는 방법을 배웠습니다