ReactJS
Users
리액트 앱에서 Parse와 Google 로그인 통합 가이드
9 분
리액트 구글 로그인 소개 지난 튜토리얼에서는 parse user parse user 클래스를 사용하여 앱에 사용자 로그인/로그아웃 기능을 추가했습니다 이제 구글 로그인 기능을 사용하여 구글에서 사용자 데이터를 가져오고, 로그인하거나 가입하거나 기존 사용자와 연결하는 방법을 배울 것입니다 이를 위해 react google login react google login 라이브러리를 설치하고 구성할 것입니다 parse user linkwith parse user linkwith 메서드는 각기 다른 제공자가 요청하는 올바른 매개변수만 전달하면 모든 서드파티 인증 방법을 사용하여 사용자를 가입시키고 로그인하는 역할을 합니다 사용자 데이터를 새 또는 기존 parse user parse user 에 연결한 후, parse는 장치에 유효한 사용자 세션을 저장합니다 이후 current current 과 같은 메서드를 호출하면 일반 로그인과 마찬가지로 사용자 데이터를 성공적으로 가져올 수 있습니다 전제 조건 이 튜토리얼을 완료하려면 다음이 필요합니다 리액트 앱이 생성되고 back4app에 연결됨 이전 가이드를 완료하여 parse user 클래스와 parse user login 메서드에 대한 이해를 높이세요 https //www back4app com/docs/react/working with users/react user login 이 가이드에서 제공하는 화면 레이아웃을 테스트/사용하려면 ant design ant design 라이브러리를 설정해야 합니다 목표 리액트 앱에서 구글 로그인을 사용하여 사용자 로그인 기능을 구축하는 것입니다 1 의존성 설치 react에서 google 로그인을 활성화하는 가장 인기 있는 방법은 react google login react google login 을 사용하는 것입니다 공식 문서 를 따라 설정하세요 google 자격 증명 페이지에서 oauth clientid clientid 를 생성하고, 일반적으로 http //localhost 3000 http //localhost 3000 을 허용된 url에 추가하여 로컬 개발 주소를 설정하세요 2 parse와 함께 google 로그인을 사용하기 이제 google sign in 인증 모달을 호출할 때 응답을 처리할 새로운 메서드를 userlogin userlogin 컴포넌트 안에 생성해 보겠습니다 사용자가 google로 로그인하면 이 호출은 google에서 사용자 데이터를 가져오고, id, idtoken idtoken , 및 google 이메일을 저장해야 합니다 그런 다음 이 함수는 parse user linkwith parse user linkwith 메서드와 이러한 자격 증명을 사용하여 parse에 로그인하려고 시도합니다 사용자가 이미 이 google 인증을 사용하여 가입한 경우, linkwith linkwith 는 기존 계정을 사용하여 그를 로그인시킵니다 javascript 1 const handlegoogleloginloginresponse = async function(response) { 2 // check if response has an error 3 if (response error !== undefined) { 4 console log(`error ${response error}`); 5 return false; 6 } else { 7 try { 8 // gather google user info 9 const usergoogleid = response googleid; 10 const usertokenid = response tokenid; 11 const useremail = response profileobj email; 12 // try to login on parse using linkwith and these credentials 13 // create a new parse user object 14 const usertologin = new parse user(); 15 // set username and email to match google email 16 usertologin set('username', useremail); 17 usertologin set('email', useremail); 18 try { 19 let loggedinuser = await usertologin 20 linkwith('google', { 21 authdata {id usergoogleid, id token usertokenid}, 22 }); 23 // login returns the corresponding parseuser object 24 alert( 25 `success! user ${loggedinuser get('username')} has successfully signed in!`, 26 ); 27 // update state variable holding current user 28 getcurrentuser(); 29 return true; 30 } catch (error) { 31 // error can be caused by wrong parameters or lack of internet connection 32 alert(`error! ${error message}`); 33 return false; 34 } 35 } catch (error) { 36 console log("error gathering google user info, please try again!") 37 return false; 38 } 39 } 40 }1 const handlegoogleloginloginresponse = async function(response any) promise\<boolean> { 2 // check if response has an error 3 if (response error !== undefined) { 4 console log(`error ${response error}`); 5 return false; 6 } else { 7 try { 8 // gather google user info 9 const usergoogleid string = response googleid; 10 const usertokenid string = response tokenid; 11 const useremail string = response profileobj email; 12 // try to login on parse using linkwith and these credentials 13 // create a new parse user object 14 const usertologin parse user = new parse user(); 15 // set username and email to match google email 16 usertologin set('username', useremail); 17 usertologin set('email', useremail); 18 try { 19 let loggedinuser parse user = await usertologin 20 linkwith('google', { 21 authdata {id usergoogleid, id token usertokenid}, 22 }); 23 // login returns the corresponding parseuser object 24 alert( 25 `success! user ${loggedinuser get('username')} has successfully signed in!`, 26 ); 27 // update state variable holding current user 28 getcurrentuser(); 29 return true; 30 } catch (error any) { 31 // error can be caused by wrong parameters or lack of internet connection 32 alert(`error! ${error message}`); 33 return false; 34 } 35 } catch (error any) { 36 console log("error gathering google user info, please try again!") 37 return false; 38 } 39 } 40 } 그 후, react google login react google login googlelogin googlelogin 컴포넌트를 사용하여 google signin 모달을 호출하고, 이를 jsx 코드에 추가해야 합니다 google의 기본 스타일을 사용하거나 이 가이드에서 따르는 방식처럼 사용자 정의 스타일을 만들 수 있습니다 다음은 전체 userlogin userlogin 컴포넌트 코드입니다 react google login react google login 버튼과 그것이 이전에 생성된 모달 응답 메서드와 어떻게 연결되어 있는지 주목하세요 javascript 1 import react, { usestate } from 'react'; 2 import parse from 'parse/dist/parse min js'; 3 import googlelogin from 'react google login'; 4 import ' /app css'; 5 import { button, divider, input } from 'antd'; 6	 7 export const userlogin = () => { 8 // state variables 9 const \[username, setusername] = usestate(''); 10 const \[password, setpassword] = usestate(''); 11 const \[currentuser, setcurrentuser] = usestate(null); 12	 13 const douserlogin = async function () { 14 // note that these values come from state variables that we've declared before 15 const usernamevalue = username; 16 const passwordvalue = password; 17 try { 18 const loggedinuser = await parse user login(usernamevalue, passwordvalue); 19 // login returns the corresponding parseuser object 20 alert( 21 `success! user ${loggedinuser get( 22 'username' 23 )} has successfully signed in!` 24 ); 25 // to verify that this is in fact the current user, `current` can be used 26 const currentuser = await parse user current(); 27 console log(loggedinuser === currentuser); 28 // clear input fields 29 setusername(''); 30 setpassword(''); 31 // update state variable holding current user 32 getcurrentuser(); 33 return true; 34 } catch (error) { 35 // error can be caused by wrong parameters or lack of internet connection 36 alert(`error! ${error message}`); 37 return false; 38 } 39 }; 40	 41 const douserlogout = async function () { 42 try { 43 await parse user logout(); 44 // to verify that current user is now empty, currentasync can be used 45 const currentuser = await parse user current(); 46 if (currentuser === null) { 47 alert('success! no user is logged in anymore!'); 48 } 49 // update state variable holding current user 50 getcurrentuser(); 51 return true; 52 } catch (error) { 53 alert(`error! ${error message}`); 54 return false; 55 } 56 }; 57	 58 // function that will return current user and also update current username 59 const getcurrentuser = async function () { 60 const currentuser = await parse user current(); 61 // update state variable holding current user 62 setcurrentuser(currentuser); 63 return currentuser; 64 }; 65	 66 const handlegoogleloginloginresponse = async function (response) { 67 // check if response has an error 68 if (response error !== undefined) { 69 console log(`error ${response error}`); 70 return false; 71 } else { 72 try { 73 // gather google user info 74 const usergoogleid = response googleid; 75 const usertokenid = response tokenid; 76 const useremail = response profileobj email; 77 // try to login on parse using linkwith and these credentials 78 // create a new parse user object 79 const usertologin = new parse user(); 80 // set username and email to match google email 81 usertologin set('username', useremail); 82 usertologin set('email', useremail); 83 try { 84 let loggedinuser = await usertologin linkwith('google', { 85 authdata { id usergoogleid, id token usertokenid }, 86 }); 87 // login returns the corresponding parseuser object 88 alert( 89 `success! user ${loggedinuser get( 90 'username' 91 )} has successfully signed in!` 92 ); 93 // update state variable holding current user 94 getcurrentuser(); 95 return true; 96 } catch (error) { 97 // error can be caused by wrong parameters or lack of internet connection 98 alert(`error! ${error message}`); 99 return false; 100 } 101 } catch (error) { 102 console log('error gathering google user info, please try again!'); 103 return false; 104 } 105 } 106 }; 107	 108 return ( 109 \<div> 110 \<div classname="header"> 111 \<img 112 classname="header logo" 113 alt="back4app logo" 114 src={ 115 'https //blog back4app com/wp content/uploads/2019/05/back4app white logo 500px png' 116 } 117 /> 118 \<p classname="header text bold">{'react on back4app'}\</p> 119 \<p classname="header text">{'user login'}\</p> 120 \</div> 121 {currentuser === null && ( 122 \<div classname="container"> 123 \<h2 classname="heading">{'user login'}\</h2> 124 \<divider /> 125 \<div classname="form wrapper"> 126 \<input 127 value={username} 128 onchange={(event) => setusername(event target value)} 129 placeholder="username" 130 size="large" 131 classname="form input" 132 /> 133 \<input 134 value={password} 135 onchange={(event) => setpassword(event target value)} 136 placeholder="password" 137 size="large" 138 type="password" 139 classname="form input" 140 /> 141 \</div> 142 \<div classname="form buttons"> 143 \<button 144 onclick={() => douserlogin()} 145 type="primary" 146 classname="form button" 147 color={'#208aec'} 148 size="large" 149 block 150 > 151 log in 152 \</button> 153 \</div> 154 \<divider /> 155 \<div classname="login social"> 156 \<div classname="login social item login social item facebook"> 157 \<img classname="login social item image" src={'https //findicons com/files/icons/2830/clean social icons/250/facebook png'} alt=""/> 158 \</div> 159 \<googlelogin 160 clientid="108490793456 0flm4qh8ek4cb4krt7e06980o4sjvado apps googleusercontent com" 161 render={renderprops => ( 162 \<div classname="login social item"> 163 \<img onclick={renderprops onclick} classname="login social item image" src={'data\ image/png;base64,ivborw0kggoaaaansuheugaaan8aaadicamaaad5w+jtaaabwvbmvex////qqzu0qfnchft7vauufppk7f08gvr0o/zzofb7uqd7uadpncp/vqdqpzaspk7plhrpoysyoklpoyzpnst97ovwgnskpensxfh8wgavouhplrj+9/b0pad4xsoix5fd7+h74udvenlru0f61tt1r6vuc2vtzvvxiop5zsvpnjb93p4nefofrpeqxpnk5dbus2zt9u++0vtuvyfelfwq1rt2u7j86ejyl5hrsz7/9+x80nt94637xdx8yu//+/d93jb+785sjvwdu/j+9nzc1fvv9p7a5v1frmdl8+nd4spru34zqklohwd0qktwhnpwdjf1ly/5ssl82ibuzjjyidl3pyfswdjwezb1mi7vazn8zwh+68l7wjdq04okszlurkrfuig7tjkgsersq1dsusqateq4o31kk9pjnrq/qmrijuvjmcrho5uyzqvkmmtioqjbqhlesjcm1x3qaaaltuleqvr4no2c2x/a2bwafrnibattyiiwmnusm46begyczdy7ctc47iztdkbjdj1u09ad9v9/qcqeskdlbrpx4pfvys9y+nzovefcbxpcb0hrlh+en9cnzg/lrn+fhieto+nuo9pmlqzkbezpyqhrrzz1zporwm4ddwuilauakhpfb5uiuzyicblsbfsodlm/kysgwv6izihta3l4kkip1670khljvqwjyvooqm2biak1j7f3lb81nbkkap6rnalzpo5vrpbp2okqq3nbowpyiz6kvqa23fkx1dmlwak2jgozovtrkpmt1k5rjguyqk5ugsi3z1h7wrxoza1xqglgflqk8zq975ip3rpn6dkda+r5esfr54vsymd4mjcjtrmmhs6tlc1pshfmpstqnta3vbmo2zloiuw5thch0lmzkqsu6+fhw46kigqhynlasxpmslubr8kd0ba77fboztvyi/oqhtoocx4ossqhlldldnympxyuei2rylhwrmnwi9orlkhpm9uipahqyzvzxojgjirhzx8wz80lspn8z30kxca3l4haj7dexym1k5vsmvg9ceoysm0vsao2yjkzrbfizjedjbxojkt1crgzojeq1cs3d1ypyt/tjddybb0dh3seo8d14qdhmnqn+bugktgb7hzzp45du0y0er2ytdseo3e+28njxcrovwdbvq8rt8pavq8st7mfrf6l9nwi5hrnewtbvh4mcbrs9r/ceuuh5aafmnpkktbjt+7ojnqtvr3e6h2d3xu7ykkgur8vgr65wacicjn/3pi8nijxzyyfsntohpxoiaw+uzhfbtjvwhkr0iyf3b8grhdiyvapcqecujn+fhd8f4awhtfbxvkjgjkboiaotxtf/vwitrlhidtfuybwrhbu8l5rqjp6zcy+tjq7iefl9bbh2slp6hftrowus6h2jvx25gkv6ehxpazsfaqybwotgit9iothtdwkrmdt/exzz6xk9e4wrh+h4/9yfplc5pxk6bsuoxjn/z0lf40e10vuziq2wbsbzfoooai99m6f6hevzx71r6dh5rfrgygsvx3wi0dvhle2rheehgw/rasf8ryjil5kvvwira/l+subezl58hw8odxh/d6afjzjpz58fqgrv2h93qb8y/gz/byqhimjhsct9fjqozqyscdxr2w/mbxv2qzfgpxpumt+brzcscn6pcf5fedwe/jrieetgwxd4mum5rt8fkbpjtfx2ejx6rmcb78jc6gqmmpg8oogtufyjuy6rn8zhk839qzb7wmfkzt4ubdb4qvlutke364e5fxgw8/goz/bzgwnv3og56iqpoy0wmsfwa8vvay1jm2d/ulp4beofb+wfmm43gxoetxcmpvvcpejkhwedbdyyp3cchzhvr1cubefmulvh0tm58hxs200m0kln2tp5cf47tphhysnpv/q4gk5kbqvwl8wjohdz5wjja7bqpixwpymhluezeb/9aksd4b+i4y7l5wtjrtao8vwu48stwo38vwb+qp+n7dwjopew/ilut/gpe0hjdzpdk/utg7e4/k9p0nt4ott6okvofwyehrc4/09gqrpv08e6f4cfjomv/2nywd+bqwx+tgzfnuxkz+o6exgdul89q/tbwj2z8v4yepr80svj5j3unplj4nxe2y/elt7xiqps/przm8r+4fq5sgdwf0o+j2yhxi4t7qj9vrcz285gfpt7xr74epr89tl2w+e0uulkun3co3ghz19uoyf3wldll/muwt5lqogvpucxx4o+r1b8ps8qx6lag+1esfurin67z/uun+igxkn5ffqg98hvw9+bp12fx7zdb+dre/lbe7o9mench5j6q9tp7rbs9t7z51d39rrb7jt+qpss1va6z/w01vllzh7s6v1o9z+ihydq8/p3n+bov5lzv7u3on9wmc7g1skzn9+b99+4i6er6z2d3f1fozx0g9glznnm+sd3b+gbbnypr3cxuigc2bs7hes2hfa90oon99c3u/j/i4hfsvzd7gvfpb3pkzfeh8zkmh1iyhsun/g1t6w39xjr8ha2k3kawdxwpn9i8/zuhxld4c0hn/v+moge4yrmyyqk703eh6r6xmcaiewzf7z0up1mso/k4gbuj4+ae+xw7m5ymri7xjcb3x6bggwhm/+aawho8og8hbm+d13fjj0ak5y00iampe7rzxewgdg9ocfesdsavgczvg9c300oh7o3gqxwgui8k02x2ycwuxs9q/8juqmvh9mejq7f5oapyps6sctpqp6fjcz53rxt8c/qmt/4mxhoabcfpfn4effotkti4fgklif1lrj5hrj096xqm122gdptlfv7qlmel5uftxzk8nrsmxdeyp5bbm+d/wz8ehq39xkkkfvoszpz/nps/x/ndwti1eg0iycmlv9vbxrzgmabuamnah05tbnuohbra4w7mowrzbfu5bamwzj55me7nswobljeqg+hdyt8vwl60xszjvti0rnjqhv2n3s09gj+bqsnoi05phryoh5pie3ngg82umadb1f7wc3dzq+wbwb9f/5alowb8hid9oewmwn85t/bswmgyi3bdsibegwrxvojetnxmzcwhyggs9g+k6t1fdytnkbmzs2uy5byklzz392mrljkh68htlaal7pd3yxuvgr/iw6ge2hh05oj5wtiypaahljiqjvu4kpnk/vsmryrppj+sl5zvsrgp5vcbcp6qic6pxsjj68rdkityf81igyhy/pjff0p2kkvzdwcdwyxzbxr6rcep0yzejthyw+iym6nxfcmqnw9jek5aqih8f1ewveap3ht9laqnx5vymfeotxixb5jeqghmv3my+al3d7d3jb4nq3bgokszduaxox7i9u+897+789ybx1n/n5m8bk0iuh2jgct9v18ug//rno8csg83qxx8tq01bxqzviosn0uuvb0u2/yhluz1vcgyfuak23u6dv8dydvxl1+696+2+ioz3+677tp5eqnbxv0ewm9gn98byoe6fm7x+bcwvibviboyc6fhv1ohr3fsshtxf1ikk+ctbnjcbcatq52bdssx11vr2mqupzrn+v1wr9fn82vq/op2rruav7s97+dnspbrxih9fhxkj4suqgpufpyfwulrysbglmollt5j7mecsbfn7mqgancx6riez4odghnztx8perdcsuj2/cdbza3yyjhmbmjr19xasc8tkgb0n/j1+oigy+bdinkffuf/bjuztbt6aal0hvzga4rfzghllwiotnoqbb9pkxj5wguerdcchi3ljiekmqwzvnjhvvmwzefpkxjzegusgcoer8fscocdqbgetk4cjmkbpuzravmsekxmus4w9/so7ksenfbhy1fltgm7n/ivzxt4hxhestvs+x6jneq5phze1rknrgejdozxyur+kzgf0g6krxz+mmpgveca6ltebxgisshtw9zhecbqee0wunkxr7hfwjvde3ypujuusxopnoo/o0/dgdlyg7aazi56vnyzyh1hla99mhi4/duoiror5o6qi/pdxx69mart3otfkkhjqd76qpq0vkssovq7s/jwdxq2uysusufuftg0udq6k4qrgymzfigoe4ngixfuepm7zxi6qhulplbmcxhpafijlk37p2wlorsiqvjv2dm/ikfsbb96uq0yvtmbmpm4jzhhsomhec7musrfyzjxt0mep6ctcox5qsqo1+gobobi6vzmkzltsma+prfot2lwvmqbwnvycbepfi2b9xb7x5g0vy9lsubbndwaa6zvmpgygwrm3g1dfgkqnforrc+jm3rtzvepkriayhnzc1i5sdsmlk/whcjdwqyatpyaaaaabjru5erkjggg=='} alt=""/> 164 \</div> 165 )} 166 buttontext="login" 167 onsuccess={handlegoogleloginloginresponse} 168 onfailure={handlegoogleloginloginresponse} 169 cookiepolicy={'single host origin'} 170 /> 171 \<div classname="login social item"> 172 \<img classname="login social item image" src={'data\ image/png;base64,ivborw0kggoaaaansuheugaaaoeaaadhcamaaaajbsjiaaaav1bmvewzmzn///+skpkvlzwrkzh19fwgokdg4ocampqjo6p8/py+vr7l5ex29vb5+fniymirq6vq0ndp6enw1ta3t7fu1ns7u7uwsldkysrv7+/b29vi4uktra2ojcyuaaajguleqvr4no2d6zrqiaygkdsldrsrvh3v/zppq6pjjculxhlnod9/av8lauistkjsq8oxaetzfgjkclqhrboojxxoglcvae0dyz33doyitxdoubnhnrvhdysfqjoacfd7c4f39whxhfvj99cig/obryesbbpwqjjl7apfn7kl1rifmfavziebchzq2fqgdqwe9cgi/ckcctqfn/cyevh6sbqfnxapajysd4hnwacalpd4qmtc3gnjlsnqwprgjiwgqjnkgjq4hbxqrxvnrr4sk/atbpqzhjejd2ax7yfhh8hjihloyudjzb4tjzd3rwrko2k8wgu8chtlkreoteirrha+yzkhj8uibje+amwj9krihb9yh5vzdq+krijzurkhxllxclfivnhrjfwsoityijqbhzeik5rqzkl6vhrctiukogiud5nriix0flpw8gkxcffdkmdmkiieh9gntd8lhxebcdfrkvtleqixliq1il0+try5gj6rturwss+bnhdzf0pofgdnt7geavmrd+k9jiecwq5esqx3tzmthihcfwbot1gaw/avgnmtdocrer+dgyynzpye0lb0oskjfz3uzzvxphbqwpxhqzpkltxundxhbjtqgdhf9g9pihsv+4cjbkkxtilnhd5ntbj5rejs7vdaj05us9mfny+rxmrbvzu54rebc9acqhd/vssdj3jgd+3mvcq2agpjzwyg2nemr9d/qprwh89j0xwxn/xsactxwgyltpzp+53ymju1exu8pnxm04shbv4uba/befibumwzn6ewpk5kc27p29worbp5uw02sk8rbo6tsw+xy/1bwddv3j3cotpz612xu9s0x6bv+qthsxpenxqm8mvox2weijnq1rvf1wysx0mjz4vbzwd7v9prxmm2n6foadt1byu4zsywwtko/cevr2qkzaqds2jb1xz3pmyqwg2v7/jkabb1dqbdjnefr8acmyadccvwf355p24x0tcqkym986e9hsuktvjp2x/6on7u/aptc/l8381lzfpnjczxmbgp+nlt11x3gr3tdpt8n6xufboe4tp76rwgduv2qooonxpwwalrikx48fx7myftopvbpj7kiurceqdxsoljyrcgd8gxitiy6yrduov7nsyoscbsxeapqhbekqfsqaiikxcnsytehjkbgigc+rfz6dikkeny6loi93kgz5xcenanietgeahxcpsealmomggbuk0zvclbysc4yapzypwftxgoirz/okbudrtjumiyngeiu0mameiwweeb8o0fqrhsbmucvkveckeijg0i+pphqt1mxs0pfgyyem2/iiosiew4hvyirxpaitjiqpstep37ekhquwipb4oqowzdso/uhagvf0jgtgza2cj+hiycdibq8yo0zzzq+wkv7+xztfj8hirtdcv/0k+i59dfusoqmoelxopyrfthafqt7tv2uvjcjxcqtatcffof4uztj+xmfbchxu/fiqfojcsqx0qto3xir6h3rmac+gjxwjsppw3aq4gl60ytp4nwq6h3nuqc+gi1+i8c5ueme2o1ni6fhokh1lpzcqjzok6odtm9zan6cbdacxnbcoaqlwiuryelzmcjte7vawynh1drzbfsngocuovmma2oezjoqhu6taezfeoewinoaialcuk8mgukhzrxgyvckzu0corqj9x+e/4nvokvlbzfjdak0+69cmvmfl2eafno72vpxfbeagnfdk9q1zrio2c161itujx23p7gbveqymp/r/1hsonpcukgbfxcwtgdie4ilp6lqwbtm06o9nw0oatvokheoyf+dunjhhqd9vh3jowtoiw344ndtccjfqd4sxffnh2x2l7kp8j3ekqdepl7rn8pd2wov7gxpb9dpilh3pzgivr7rgry6xopodo98cdlqmrm817fpodei7/0ez5iy0bihuh7clwec1/e8qz7cdwbgl5oli9wrxfdnohxsytxt67wkxerxaudrijddxnbzj+ul/connbr4729hkrn/ehuwfeds4k+hfztwaj1eqouoih6cxxpigdzskf1e2ovgdhilfhocjg3vr00/zkbo/miqlh9g49iykebcbu73o6fxf8brtcodkfdcykybbgqdtbccywubrgqdcl4kfv6rymyg/mm3xsqlx1hgidh23ttqwk5xv/9jevtpgjcgw5himmnab+v50qdjtr7jjgitwo0tpgoa9obpcelsdyjdl/gnzynnh1cwl+prfhvv8riutxv9k/ynpxihfqz2b7klbwaeb6nrffmjqkr0/rdnwdz74u2kkhiykr38jui8eh3ttfu97l+mqy4oelootv535+qwfageygoponaqlduzu0weh1kfor6wr+mzqaqbrv/rda2mprjfjsaerzqriju/hftml6glg2mnfp5lcrakuaoura+g0okibl9is0pfuzgjfr8mih8qvjdqwq9bmi4kzvq7xyde3nyzq3scifapojlunfjiccn0lojk43ghhchycj/vrilcos6uqxchb5tsjoij4dfnqocljjaivr3trupvjhvh2ghq5+mpfu6ezumve2kxobehqgqfuoe7ih5lcj7sk165tzduzolqe4ryvd80tlvl6n5xwxcjtuz0pfdpj4knxqgiuee4hdczb9/ce5k+uugvf6kf6kl5rl4ljc6taepwldctdyjp2vvhqgijjtuynn56mgesr0pkblrfsbmpmlngy5bnikumnbdao54j6aayatniu1wrxncpj6ej3yvpxhhmltyp35nbwrkrmtp2tjnlfvnaybpeszjyxhhz+fdfnexfbb+qdrdobtcs3qecjocnhiddv0zmqfu7zmqjjyc49gthmnroe/n7dkhfeazkksjkce1ewc9s5iss3dacnbscm/7ovgix9kwaexyz3qameka4305z1tqcbnpfmb0vhbnggqik1u++nlolg1nel415tikc0va7dmrjaj7rpq7x33vpvnfvzfltu3sl8resowhhfqsgcjex1p/lu7yl1vtbeb9qd6fjo05b1k7z1nxoy5ijldvzfu2b09lvzqb1x5/alyvmmpfht+c/6dv/qmh/ovcu90claaaaabjru5erkjggg=='} alt=""/> 173 \</div> 174 \</div> 175 \<p classname="form hint">don't have an account? \<a classname="form link" href="#">sign up\</a>\</p> 176 \</div> 177 )} 178 {currentuser !== null && ( 179 \<div classname="container"> 180 \<h2 classname="heading">{'user screen'}\</h2> 181 \<divider /> 182 \<h2 classname="heading">{`hello ${currentuser get('username')}!`}\</h2> 183 \<div classname="form buttons"> 184 \<button 185 onclick={() => douserlogout()} 186 type="primary" 187 classname="form button" 188 color={'#208aec'} 189 size="large" 190 block 191 > 192 log out 193 \</button> 194 \</div> 195 \</div> 196 )} 197 \</div> 198 ); 199 };1 import react, { usestate, fc, reactelement } from 'react'; 2 import ' /app css'; 3 import { button, divider, input } from 'antd'; 4 import googlelogin from 'react google login'; 5 const parse = require('parse/dist/parse min js'); 6	 7 export const userlogin fc<{}> = () reactelement => { 8 // state variables 9 const \[username, setusername] = usestate(''); 10 const \[password, setpassword] = usestate(''); 11 const \[currentuser, setcurrentuser] = usestate\<parse object | null>(null); 12	 13 const douserlogin = async function () promise\<boolean> { 14 // note that these values come from state variables that we've declared before 15 const usernamevalue string = username; 16 const passwordvalue string = password; 17 try { 18 const loggedinuser parse user = await parse user login(usernamevalue, passwordvalue); 19 // login returns the corresponding parseuser object 20 alert( 21 `success! user ${loggedinuser get('username')} has successfully signed in!`, 22 ); 23 // to verify that this is in fact the current user, `current` can be used 24 const currentuser parse user = await parse user current(); 25 console log(loggedinuser === currentuser); 26 // clear input fields 27 setusername(''); 28 setpassword(''); 29 // update state variable holding current user 30 getcurrentuser(); 31 return true; 32 } catch (error any) { 33 // error can be caused by wrong parameters or lack of internet connection 34 alert(`error! ${error message}`); 35 return false; 36 } 37 }; 38	 39 const douserlogout = async function () promise\<boolean> { 40 try { 41 await parse user logout(); 42 // to verify that current user is now empty, currentasync can be used 43 const currentuser parse user = await parse user current(); 44 if (currentuser === null) { 45 alert('success! no user is logged in anymore!'); 46 } 47 // update state variable holding current user 48 getcurrentuser(); 49 return true; 50 } catch (error any) { 51 alert(`error! ${error message}`); 52 return false; 53 } 54 }; 55	 56 // function that will return current user and also update current username 57 const getcurrentuser = async function () promise\<parse user | null> { 58 const currentuser (parse user | null) = await parse user current(); 59 // update state variable holding current user 60 setcurrentuser(currentuser); 61 return currentuser; 62 } 63	 64 const handlegoogleloginloginresponse = async function(response any) promise\<boolean> { 65 // check if response has an error 66 if (response error !== undefined) { 67 console log(`error ${response error}`); 68 return false; 69 } else { 70 try { 71 // gather google user info 72 const usergoogleid string = response googleid; 73 const usertokenid string = response tokenid; 74 const useremail string = response profileobj email; 75 // try to login on parse using linkwith and these credentials 76 // create a new parse user object 77 const usertologin parse user = new parse user(); 78 // set username and email to match google email 79 usertologin set('username', useremail); 80 usertologin set('email', useremail); 81 try { 82 let loggedinuser parse user = await usertologin 83 linkwith('google', { 84 authdata {id usergoogleid, id token usertokenid}, 85 }); 86 // login returns the corresponding parseuser object 87 alert( 88 `success! user ${loggedinuser get('username')} has successfully signed in!`, 89 ); 90 // update state variable holding current user 91 getcurrentuser(); 92 return true; 93 } catch (error any) { 94 // error can be caused by wrong parameters or lack of internet connection 95 alert(`error! ${error message}`); 96 return false; 97 } 98 } catch (error any) { 99 console log("error gathering google user info, please try again!") 100 return false; 101 } 102 } 103 } 104	 105 return ( 106 \<div> 107 \<div classname="header"> 108 \<img 109 classname="header logo" 110 alt="back4app logo" 111 src={ 112 'https //blog back4app com/wp content/uploads/2019/05/back4app white logo 500px png' 113 } 114 /> 115 \<p classname="header text bold">{'react on back4app'}\</p> 116 \<p classname="header text">{'user login'}\</p> 117 \</div> 118 {currentuser === null && ( 119 \<div classname="container"> 120 \<h2 classname="heading">{'user login'}\</h2> 121 \<divider /> 122 \<div classname="form wrapper"> 123 \<input 124 value={username} 125 onchange={(event) => setusername(event target value)} 126 placeholder="username" 127 size="large" 128 classname="form input" 129 /> 130 \<input 131 value={password} 132 onchange={(event) => setpassword(event target value)} 133 placeholder="password" 134 size="large" 135 type="password" 136 classname="form input" 137 /> 138 \</div> 139 \<div classname="form buttons"> 140 \<button 141 onclick={() => douserlogin()} 142 type="primary" 143 classname="form button" 144 color={'#208aec'} 145 size="large" 146 block 147 > 148 log in 149 \</button> 150 \</div> 151 \<divider /> 152 \<div classname="login social"> 153 \<div classname="login social item login social item facebook"> 154 \<img classname="login social item image" src={'https //findicons com/files/icons/2830/clean social icons/250/facebook png'} alt=""/> 155 \</div> 156 \<googlelogin 157 clientid="108490793456 0flm4qh8ek4cb4krt7e06980o4sjvado apps googleusercontent com" 158 render={renderprops => ( 159 \<div classname="login social item"> 160 \<img onclick={renderprops onclick} classname="login social item image" src={'data\ image/png;base64,ivborw0kggoaaaansuheugaaan8aaadicamaaad5w+jtaaabwvbmvex////qqzu0qfnchft7vauufppk7f08gvr0o/zzofb7uqd7uadpncp/vqdqpzaspk7plhrpoysyoklpoyzpnst97ovwgnskpensxfh8wgavouhplrj+9/b0pad4xsoix5fd7+h74udvenlru0f61tt1r6vuc2vtzvvxiop5zsvpnjb93p4nefofrpeqxpnk5dbus2zt9u++0vtuvyfelfwq1rt2u7j86ejyl5hrsz7/9+x80nt94637xdx8yu//+/d93jb+785sjvwdu/j+9nzc1fvv9p7a5v1frmdl8+nd4spru34zqklohwd0qktwhnpwdjf1ly/5ssl82ibuzjjyidl3pyfswdjwezb1mi7vazn8zwh+68l7wjdq04okszlurkrfuig7tjkgsersq1dsusqateq4o31kk9pjnrq/qmrijuvjmcrho5uyzqvkmmtioqjbqhlesjcm1x3qaaaltuleqvr4no2c2x/a2bwafrnibattyiiwmnusm46begyczdy7ctc47iztdkbjdj1u09ad9v9/qcqeskdlbrpx4pfvys9y+nzovefcbxpcb0hrlh+en9cnzg/lrn+fhieto+nuo9pmlqzkbezpyqhrrzz1zporwm4ddwuilauakhpfb5uiuzyicblsbfsodlm/kysgwv6izihta3l4kkip1670khljvqwjyvooqm2biak1j7f3lb81nbkkap6rnalzpo5vrpbp2okqq3nbowpyiz6kvqa23fkx1dmlwak2jgozovtrkpmt1k5rjguyqk5ugsi3z1h7wrxoza1xqglgflqk8zq975ip3rpn6dkda+r5esfr54vsymd4mjcjtrmmhs6tlc1pshfmpstqnta3vbmo2zloiuw5thch0lmzkqsu6+fhw46kigqhynlasxpmslubr8kd0ba77fboztvyi/oqhtoocx4ossqhlldldnympxyuei2rylhwrmnwi9orlkhpm9uipahqyzvzxojgjirhzx8wz80lspn8z30kxca3l4haj7dexym1k5vsmvg9ceoysm0vsao2yjkzrbfizjedjbxojkt1crgzojeq1cs3d1ypyt/tjddybb0dh3seo8d14qdhmnqn+bugktgb7hzzp45du0y0er2ytdseo3e+28njxcrovwdbvq8rt8pavq8st7mfrf6l9nwi5hrnewtbvh4mcbrs9r/ceuuh5aafmnpkktbjt+7ojnqtvr3e6h2d3xu7ykkgur8vgr65wacicjn/3pi8nijxzyyfsntohpxoiaw+uzhfbtjvwhkr0iyf3b8grhdiyvapcqecujn+fhd8f4awhtfbxvkjgjkboiaotxtf/vwitrlhidtfuybwrhbu8l5rqjp6zcy+tjq7iefl9bbh2slp6hftrowus6h2jvx25gkv6ehxpazsfaqybwotgit9iothtdwkrmdt/exzz6xk9e4wrh+h4/9yfplc5pxk6bsuoxjn/z0lf40e10vuziq2wbsbzfoooai99m6f6hevzx71r6dh5rfrgygsvx3wi0dvhle2rheehgw/rasf8ryjil5kvvwira/l+subezl58hw8odxh/d6afjzjpz58fqgrv2h93qb8y/gz/byqhimjhsct9fjqozqyscdxr2w/mbxv2qzfgpxpumt+brzcscn6pcf5fedwe/jrieetgwxd4mum5rt8fkbpjtfx2ejx6rmcb78jc6gqmmpg8oogtufyjuy6rn8zhk839qzb7wmfkzt4ubdb4qvlutke364e5fxgw8/goz/bzgwnv3og56iqpoy0wmsfwa8vvay1jm2d/ulp4beofb+wfmm43gxoetxcmpvvcpejkhwedbdyyp3cchzhvr1cubefmulvh0tm58hxs200m0kln2tp5cf47tphhysnpv/q4gk5kbqvwl8wjohdz5wjja7bqpixwpymhluezeb/9aksd4b+i4y7l5wtjrtao8vwu48stwo38vwb+qp+n7dwjopew/ilut/gpe0hjdzpdk/utg7e4/k9p0nt4ott6okvofwyehrc4/09gqrpv08e6f4cfjomv/2nywd+bqwx+tgzfnuxkz+o6exgdul89q/tbwj2z8v4yepr80svj5j3unplj4nxe2y/elt7xiqps/przm8r+4fq5sgdwf0o+j2yhxi4t7qj9vrcz285gfpt7xr74epr89tl2w+e0uulkun3co3ghz19uoyf3wldll/muwt5lqogvpucxx4o+r1b8ps8qx6lag+1esfurin67z/uun+igxkn5ffqg98hvw9+bp12fx7zdb+dre/lbe7o9mench5j6q9tp7rbs9t7z51d39rrb7jt+qpss1va6z/w01vllzh7s6v1o9z+ihydq8/p3n+bov5lzv7u3on9wmc7g1skzn9+b99+4i6er6z2d3f1fozx0g9glznnm+sd3b+gbbnypr3cxuigc2bs7hes2hfa90oon99c3u/j/i4hfsvzd7gvfpb3pkzfeh8zkmh1iyhsun/g1t6w39xjr8ha2k3kawdxwpn9i8/zuhxld4c0hn/v+moge4yrmyyqk703eh6r6xmcaiewzf7z0up1mso/k4gbuj4+ae+xw7m5ymri7xjcb3x6bggwhm/+aawho8og8hbm+d13fjj0ak5y00iampe7rzxewgdg9ocfesdsavgczvg9c300oh7o3gqxwgui8k02x2ycwuxs9q/8juqmvh9mejq7f5oapyps6sctpqp6fjcz53rxt8c/qmt/4mxhoabcfpfn4effotkti4fgklif1lrj5hrj096xqm122gdptlfv7qlmel5uftxzk8nrsmxdeyp5bbm+d/wz8ehq39xkkkfvoszpz/nps/x/ndwti1eg0iycmlv9vbxrzgmabuamnah05tbnuohbra4w7mowrzbfu5bamwzj55me7nswobljeqg+hdyt8vwl60xszjvti0rnjqhv2n3s09gj+bqsnoi05phryoh5pie3ngg82umadb1f7wc3dzq+wbwb9f/5alowb8hid9oewmwn85t/bswmgyi3bdsibegwrxvojetnxmzcwhyggs9g+k6t1fdytnkbmzs2uy5byklzz392mrljkh68htlaal7pd3yxuvgr/iw6ge2hh05oj5wtiypaahljiqjvu4kpnk/vsmryrppj+sl5zvsrgp5vcbcp6qic6pxsjj68rdkityf81igyhy/pjff0p2kkvzdwcdwyxzbxr6rcep0yzejthyw+iym6nxfcmqnw9jek5aqih8f1ewveap3ht9laqnx5vymfeotxixb5jeqghmv3my+al3d7d3jb4nq3bgokszduaxox7i9u+897+789ybx1n/n5m8bk0iuh2jgct9v18ug//rno8csg83qxx8tq01bxqzviosn0uuvb0u2/yhluz1vcgyfuak23u6dv8dydvxl1+696+2+ioz3+677tp5eqnbxv0ewm9gn98byoe6fm7x+bcwvibviboyc6fhv1ohr3fsshtxf1ikk+ctbnjcbcatq52bdssx11vr2mqupzrn+v1wr9fn82vq/op2rruav7s97+dnspbrxih9fhxkj4suqgpufpyfwulrysbglmollt5j7mecsbfn7mqgancx6riez4odghnztx8perdcsuj2/cdbza3yyjhmbmjr19xasc8tkgb0n/j1+oigy+bdinkffuf/bjuztbt6aal0hvzga4rfzghllwiotnoqbb9pkxj5wguerdcchi3ljiekmqwzvnjhvvmwzefpkxjzegusgcoer8fscocdqbgetk4cjmkbpuzravmsekxmus4w9/so7ksenfbhy1fltgm7n/ivzxt4hxhestvs+x6jneq5phze1rknrgejdozxyur+kzgf0g6krxz+mmpgveca6ltebxgisshtw9zhecbqee0wunkxr7hfwjvde3ypujuusxopnoo/o0/dgdlyg7aazi56vnyzyh1hla99mhi4/duoiror5o6qi/pdxx69mart3otfkkhjqd76qpq0vkssovq7s/jwdxq2uysusufuftg0udq6k4qrgymzfigoe4ngixfuepm7zxi6qhulplbmcxhpafijlk37p2wlorsiqvjv2dm/ikfsbb96uq0yvtmbmpm4jzhhsomhec7musrfyzjxt0mep6ctcox5qsqo1+gobobi6vzmkzltsma+prfot2lwvmqbwnvycbepfi2b9xb7x5g0vy9lsubbndwaa6zvmpgygwrm3g1dfgkqnforrc+jm3rtzvepkriayhnzc1i5sdsmlk/whcjdwqyatpyaaaaabjru5erkjggg=='} alt=""/> 161 \</div> 162 )} 163 buttontext="login" 164 onsuccess={handlegoogleloginloginresponse} 165 onfailure={handlegoogleloginloginresponse} 166 cookiepolicy={'single host origin'} 167 /> 168 \<div classname="login social item"> 169 \<img classname="login social item image" src={'data\ image/png;base64,ivborw0kggoaaaansuheugaaaoeaaadhcamaaaajbsjiaaaav1bmvewzmzn///+skpkvlzwrkzh19fwgokdg4ocampqjo6p8/py+vr7l5ex29vb5+fniymirq6vq0ndp6enw1ta3t7fu1ns7u7uwsldkysrv7+/b29vi4uktra2ojcyuaaajguleqvr4no2d6zrqiaygkdsldrsrvh3v/zppq6pjjculxhlnod9/av8lauistkjsq8oxaetzfgjkclqhrboojxxoglcvae0dyz33doyitxdoubnhnrvhdysfqjoacfd7c4f39whxhfvj99cig/obryesbbpwqjjl7apfn7kl1rifmfavziebchzq2fqgdqwe9cgi/ckcctqfn/cyevh6sbqfnxapajysd4hnwacalpd4qmtc3gnjlsnqwprgjiwgqjnkgjq4hbxqrxvnrr4sk/atbpqzhjejd2ax7yfhh8hjihloyudjzb4tjzd3rwrko2k8wgu8chtlkreoteirrha+yzkhj8uibje+amwj9krihb9yh5vzdq+krijzurkhxllxclfivnhrjfwsoityijqbhzeik5rqzkl6vhrctiukogiud5nriix0flpw8gkxcffdkmdmkiieh9gntd8lhxebcdfrkvtleqixliq1il0+try5gj6rturwss+bnhdzf0pofgdnt7geavmrd+k9jiecwq5esqx3tzmthihcfwbot1gaw/avgnmtdocrer+dgyynzpye0lb0oskjfz3uzzvxphbqwpxhqzpkltxundxhbjtqgdhf9g9pihsv+4cjbkkxtilnhd5ntbj5rejs7vdaj05us9mfny+rxmrbvzu54rebc9acqhd/vssdj3jgd+3mvcq2agpjzwyg2nemr9d/qprwh89j0xwxn/xsactxwgyltpzp+53ymju1exu8pnxm04shbv4uba/befibumwzn6ewpk5kc27p29worbp5uw02sk8rbo6tsw+xy/1bwddv3j3cotpz612xu9s0x6bv+qthsxpenxqm8mvox2weijnq1rvf1wysx0mjz4vbzwd7v9prxmm2n6foadt1byu4zsywwtko/cevr2qkzaqds2jb1xz3pmyqwg2v7/jkabb1dqbdjnefr8acmyadccvwf355p24x0tcqkym986e9hsuktvjp2x/6on7u/aptc/l8381lzfpnjczxmbgp+nlt11x3gr3tdpt8n6xufboe4tp76rwgduv2qooonxpwwalrikx48fx7myftopvbpj7kiurceqdxsoljyrcgd8gxitiy6yrduov7nsyoscbsxeapqhbekqfsqaiikxcnsytehjkbgigc+rfz6dikkeny6loi93kgz5xcenanietgeahxcpsealmomggbuk0zvclbysc4yapzypwftxgoirz/okbudrtjumiyngeiu0mameiwweeb8o0fqrhsbmucvkveckeijg0i+pphqt1mxs0pfgyyem2/iiosiew4hvyirxpaitjiqpstep37ekhquwipb4oqowzdso/uhagvf0jgtgza2cj+hiycdibq8yo0zzzq+wkv7+xztfj8hirtdcv/0k+i59dfusoqmoelxopyrfthafqt7tv2uvjcjxcqtatcffof4uztj+xmfbchxu/fiqfojcsqx0qto3xir6h3rmac+gjxwjsppw3aq4gl60ytp4nwq6h3nuqc+gi1+i8c5ueme2o1ni6fhokh1lpzcqjzok6odtm9zan6cbdacxnbcoaqlwiuryelzmcjte7vawynh1drzbfsngocuovmma2oezjoqhu6taezfeoewinoaialcuk8mgukhzrxgyvckzu0corqj9x+e/4nvokvlbzfjdak0+69cmvmfl2eafno72vpxfbeagnfdk9q1zrio2c161itujx23p7gbveqymp/r/1hsonpcukgbfxcwtgdie4ilp6lqwbtm06o9nw0oatvokheoyf+dunjhhqd9vh3jowtoiw344ndtccjfqd4sxffnh2x2l7kp8j3ekqdepl7rn8pd2wov7gxpb9dpilh3pzgivr7rgry6xopodo98cdlqmrm817fpodei7/0ez5iy0bihuh7clwec1/e8qz7cdwbgl5oli9wrxfdnohxsytxt67wkxerxaudrijddxnbzj+ul/connbr4729hkrn/ehuwfeds4k+hfztwaj1eqouoih6cxxpigdzskf1e2ovgdhilfhocjg3vr00/zkbo/miqlh9g49iykebcbu73o6fxf8brtcodkfdcykybbgqdtbccywubrgqdcl4kfv6rymyg/mm3xsqlx1hgidh23ttqwk5xv/9jevtpgjcgw5himmnab+v50qdjtr7jjgitwo0tpgoa9obpcelsdyjdl/gnzynnh1cwl+prfhvv8riutxv9k/ynpxihfqz2b7klbwaeb6nrffmjqkr0/rdnwdz74u2kkhiykr38jui8eh3ttfu97l+mqy4oelootv535+qwfageygoponaqlduzu0weh1kfor6wr+mzqaqbrv/rda2mprjfjsaerzqriju/hftml6glg2mnfp5lcrakuaoura+g0okibl9is0pfuzgjfr8mih8qvjdqwq9bmi4kzvq7xyde3nyzq3scifapojlunfjiccn0lojk43ghhchycj/vrilcos6uqxchb5tsjoij4dfnqocljjaivr3trupvjhvh2ghq5+mpfu6ezumve2kxobehqgqfuoe7ih5lcj7sk165tzduzolqe4ryvd80tlvl6n5xwxcjtuz0pfdpj4knxqgiuee4hdczb9/ce5k+uugvf6kf6kl5rl4ljc6taepwldctdyjp2vvhqgijjtuynn56mgesr0pkblrfsbmpmlngy5bnikumnbdao54j6aayatniu1wrxncpj6ej3yvpxhhmltyp35nbwrkrmtp2tjnlfvnaybpeszjyxhhz+fdfnexfbb+qdrdobtcs3qecjocnhiddv0zmqfu7zmqjjyc49gthmnroe/n7dkhfeazkksjkce1ewc9s5iss3dacnbscm/7ovgix9kwaexyz3qameka4305z1tqcbnpfmb0vhbnggqik1u++nlolg1nel415tikc0va7dmrjaj7rpq7x33vpvnfvzfltu3sl8resowhhfqsgcjex1p/lu7yl1vtbeb9qd6fjo05b1k7z1nxoy5ijldvzfu2b09lvzqb1x5/alyvmmpfht+c/6dv/qmh/ovcu90claaaaabjru5erkjggg=='} alt=""/> 170 \</div> 171 \</div> 172 \<p classname="form hint">don't have an account? \<a classname="form link" href="#">sign up\</a>\</p> 173 \</div> 174 )} 175 {currentuser !== null && ( 176 \<div classname="container"> 177 \<h2 classname="heading">{'user screen'}\</h2> 178 \<divider /> 179 \<h2 classname="heading">{`hello ${currentuser get('username')}!`}\</h2> 180 \<div classname="form buttons"> 181 \<button 182 onclick={() => douserlogout()} 183 type="primary" 184 classname="form button" 185 color={'#208aec'} 186 size="large" 187 block 188 > 189 log out 190 \</button> 191 \</div> 192 \</div> 193 )} 194 \</div> 195 ); 196 }; 이 클래스를 app css app css 파일에 추가하면 이 컴포넌트의 레이아웃을 완전히 렌더링할 수 있습니다 app css 1 @import ' antd/dist/antd css'; 2	 3 app { 4 text align center; 5 } 6	 7 html { 8 box sizing border box; 9 outline none; 10 overflow auto; 11 } 12	 13 , 14 before, 15 after { 16 margin 0; 17 padding 0; 18 box sizing inherit; 19 } 20	 21 h1, 22 h2, 23 h3, 24 h4, 25 h5, 26 h6 { 27 margin 0; 28 font weight bold; 29 } 30	 31 p { 32 margin 0; 33 } 34	 35 body { 36 margin 0; 37 background color #fff; 38 } 39	 40 container { 41 width 100%; 42 max width 900px; 43 margin auto; 44 padding 20px 0; 45 text align left; 46 } 47	 48 header { 49 align items center; 50 padding 25px 0; 51 background color #208aec; 52 } 53	 54 header logo { 55 height 55px; 56 margin bottom 20px; 57 object fit contain; 58 } 59	 60 header text bold { 61 margin bottom 3px; 62 color rgba(255, 255, 255, 0 9); 63 font size 16px; 64 font weight bold; 65 } 66	 67 header text { 68 color rgba(255, 255, 255, 0 9); 69 font size 15px; 70 } 71	 72 heading { 73 font size 22px; 74 } 75	 76 flex { 77 display flex; 78 } 79	 80 flex between { 81 display flex; 82 justify content space between; 83 } 84	 85 flex child { 86 flex 0 0 45%; 87 } 88	 89 heading button { 90 margin left 12px; 91 } 92	 93 list item { 94 padding bottom 15px; 95 margin bottom 15px; 96 border bottom 1px solid rgba(0, 0, 0, 0 06); 97 text align left; 98 } 99	 100 list item title { 101 color rgba(0, 0, 0, 0 87); 102 font size 17px; 103 } 104	 105 list item description { 106 color rgba(0, 0, 0, 0 5); 107 font size 15px; 108 } 109	 110 form input { 111 margin bottom 20px; 112 } 113	 114 login social { 115 display flex; 116 justify content center; 117 margin bottom 30px; 118 } 119	 120 login social item { 121 width 54px; 122 height 54px; 123 border radius 54px; 124 padding 12px; 125 margin 0 12px; 126 border 1px solid #e6e6e6; 127 box shadow 0 2px 4px #d6d6d6; 128 } 129	 130 login social item facebook { 131 padding 4px; 132 background color #3c5b9b; 133 } 134	 135 login social item image { 136 width 100%; 137 } 138	 139 form hint { 140 color rgba(0, 0, 0, 0 5); 141 font size 16px; 142 text align center; 143 } 새로운 기능을 테스트해 보세요 google에 로그인할 수 있었고 parse linkwith linkwith 호출이 성공적이었다면, 다음과 같은 성공 메시지를 볼 수 있어야 합니다 3 사용자 로그인 및 세션 생성 확인 google 로그인이 제대로 작동했는지 확인하려면, parse 대시보드를 확인하고 새로운 사용자 사용자 (google 인증 데이터가 다른 사용자에 속하지 않는 경우)와 google authdata authdata 매개변수를 포함하고 있는지 확인할 수 있습니다 대시보드에서 유효한 세션이 생성되었는지 확인할 수도 있으며, 해당 사용자 사용자 객체에 대한 포인터를 포함하고 있습니다 4 기존 사용자를 google 로그인에 연결하기 또 다른 linkwith linkwith 가능한 사용법은 기존 사용자를 다른 인증 제공자, 이 경우 google과 연결하는 것입니다 다음 기능을 추가하여 linkwith linkwith 를 호출합니다 이는 userlogin userlogin 구성 요소에 로그인하는 것과 동일한 방식입니다 여기서 유일한 차이점은 빈 parse user parse user 에서 메서드를 호출하는 대신 현재 로그인한 사용자 객체에서 사용한다는 것입니다 javascript 1 const handlegoogleloginlinkresponse = async function(response) { 2 // check if response has an error 3 if (response error !== undefined) { 4 console log(`error ${response error}`); 5 return false; 6 } else { 7 try { 8 // gather google user info 9 const usergoogleid = response googleid; 10 const usertokenid = response tokenid; 11 // try to link current parse user using linkwith and these credentials 12 // get current user 13 const usertolink = await parse user current(); 14 try { 15 let loggedinuser = await usertolink 16 linkwith('google', { 17 authdata {id usergoogleid, id token usertokenid}, 18 }); 19 // login returns the corresponding parseuser object 20 alert( 21 `success! user ${loggedinuser get( 22 'username', 23 )} has successfully linked his google account!`, 24 ); 25 // update state variable holding current user 26 getcurrentuser(); 27 return true; 28 } catch (error) { 29 // error can be caused by wrong parameters or lack of internet connection 30 alert(`error! ${error message}`); 31 return false; 32 } 33 } catch (error) { 34 console log("error gathering google user info, please try again!") 35 return false; 36 } 37 } 38 }1 const handlegoogleloginlinkresponse = async function(response any) promise\<boolean> { 2 // check if response has an error 3 if (response error !== undefined) { 4 console log(`error ${response error}`); 5 return false; 6 } else { 7 try { 8 // gather google user info 9 const usergoogleid string = response googleid; 10 const usertokenid string = response tokenid; 11 // try to link current parse user using linkwith and these credentials 12 // get current user 13 const usertolink parse user = await parse user current(); 14 try { 15 let loggedinuser parse user = await usertolink 16 linkwith('google', { 17 authdata {id usergoogleid, id token usertokenid}, 18 }); 19 // login returns the corresponding parseuser object 20 alert( 21 `success! user ${loggedinuser get( 22 'username', 23 )} has successfully linked his google account!`, 24 ); 25 // update state variable holding current user 26 getcurrentuser(); 27 return true; 28 } catch (error any) { 29 // error can be caused by wrong parameters or lack of internet connection 30 alert(`error! ${error message}`); 31 return false; 32 } 33 } catch (error any) { 34 console log("error gathering google user info, please try again!") 35 return false; 36 } 37 } 38 } 이 기능을 홈 화면의 다른 react google login react google login 구성 요소에 할당하세요 이 구성 요소는 앱에 현재 사용자가 로그인했을 때만 표시됩니다 새로운 기능을 테스트하고, parse user parse user 객체 authdata authdata 값이 새로운 인증 제공자 데이터로 업데이트될 것임을 주의하세요 사용자가 실제로 parse 서버 대시보드에서 업데이트되었는지 확인하세요 결론 이 가이드의 끝에서, google sign in을 사용하여 react에서 parse 사용자에게 로그인, 가입 또는 기존 사용자를 연결하는 방법을 배웠습니다 다음 가이드에서는 apple sign in을 사용하는 방법을 보여드리겠습니다