리액트에서 페이스북 로그인 구현: 개발자 가이드
리액트 페이스북 로그인
소개
지난 튜토리얼에서는 Parse.User 클래스를 사용하여 앱에 사용자 로그인/로그아웃 기능을 추가했습니다. 이제 페이스북 로그인을 사용하여 페이스북에서 사용자 데이터를 가져오고, 로그인하거나 가입하거나 기존 사용자와 연결하는 방법을 배울 것입니다. 또한 이를 달성하기 위해 react-facebook-login 라이브러리를 설치하고 구성할 것입니다.
Parse.User.linkWith 메서드는 각기 다른 제공자가 요청하는 올바른 매개변수를 전달하는 한, 모든 서드파티 인증 방법을 사용하여 사용자를 가입시키고 로그인시키는 역할을 합니다. 사용자 데이터를 새 또는 기존 Parse.User에 연결한 후, Parse는 귀하의 장치에 유효한 사용자 세션을 저장합니다. 이후 current 과 같은 메서드를 호출하면 일반 로그인과 마찬가지로 사용자 데이터를 성공적으로 검색할 수 있습니다.
전제 조건
이 튜토리얼을 완료하려면 다음이 필요합니다:
목표
React 앱에서 Parse를 사용하여 Facebook 로그인을 통한 사용자 로그인 기능을 구축합니다.
1 - 의존성 설치
React에서 Facebook 로그인을 활성화하는 가장 인기 있는 방법은 react-facebook-login을 사용하는 것입니다. 공식 문서를 따라 설정하세요.
lib 구성 요소를 사용하려면 유효한 Facebook 앱 appID, 을 알려야 하므로, Facebook for Developers 대시보드에서 하나를 생성하세요. 그들의 공식 가이드. 그 후, 앱에 Facebook 로그인 기능을 추가하고 WWW 옵션을 선택하여 https://localhost:3000을 웹사이트 URL로 입력하세요.
Facebook 로그인을 사용하려면 연결이 안전해야 하며, 개발 환경에서 로그인할 때도 마찬가지입니다. 따라서 로컬 인증서를 생성하고, 운영 체제에서 전체 권한을 부여하고, 파일을 프로젝트에 추가하여 React 앱에 HTTPS 액세스를 설정해야 합니다. 각 운영 체제마다 특성이 다를 수 있으므로, 이 가이드에서는 다루지 않습니다.
인증서를 생성한 후, scripts 세션에서 package.json 파일의 시작 명령을 다음과 같이 변경하세요:
1 "start": "export HTTPS=true&&SSL_CRT_FILE=cert.pem&&SSL_KEY_FILE=key.pem react-scripts start",Typescript를 사용하는 경우, src 폴더에 react-facebook-login.d.ts라는 새 파일을 생성하고, react-facebook-login 모듈 선언을 포함하여 스타일 없는 버전을 사용하세요:
1 declare module 'react-facebook-login/dist/facebook-login-render-props';2 - 파스와 함께 페이스북 로그인 사용하기
이제 Facebook 로그인 인증 모달을 호출할 때 응답을 처리할 새로운 메서드를 UserLogIn 컴포넌트 안에 생성해 보겠습니다. 사용자가 Facebook으로 로그인하면 이 호출은 Facebook에서 사용자 데이터를 검색하고 id, accessToken, 그리고 프로필 email. 그런 다음, 이 함수는 Parse.User.linkWith 메서드와 이러한 자격 증명을 사용하여 Parse에 로그인하려고 시도합니다. 사용자가 이미 이 Facebook 인증을 사용하여 가입한 경우, linkWith 는 기존 계정을 사용하여 그를 로그인시킬 것입니다.
1 const handleFacebookLoginLoginResponse = 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 Facebook user info
9 const userId: string = response.id;
10 const userEmail: string = response.email;
11 const userAccessToken: string = response.accessToken;
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 facebook profile email
16 userToLogin.set('username', userEmail);
17 userToLogin.set('email', userEmail);
18 try {
19 let loggedInUser: Parse.User = await userToLogin
20 .linkWith('facebook', {
21 authData: {id: userId, access_token: userAccessToken},
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 Facebook user info, please try again!")
37 return false;
38 }
39 }
40 }그 후, react-facebook-login FacebookLogin 컴포넌트를 사용하여 Facebook 로그인 모달을 호출하고, 이를 JSX 코드에 추가해야 합니다. Facebook의 기본 스타일을 사용하거나 이 가이드에서 따르는 방법처럼 사용자 정의 스타일을 만들 수 있습니다. 다음은 전체 UserLogin 컴포넌트 코드입니다. react-facebook-login 버튼과 그것이 이전에 생성된 모달 응답 메서드와 어떻게 연결되어 있는지 주목하세요.
1 import React, { useState, FC, ReactElement } from 'react';
2 import './App.css';
3 import { Button, Divider, Input } from 'antd';
4 import FacebookLogin from 'react-facebook-login/dist/facebook-login-render-props';
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 handleFacebookLoginLoginResponse = 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 Facebook user info
72 const userId: string = response.id;
73 const userEmail: string = response.email;
74 const userAccessToken: string = response.accessToken;
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 facebook profile email
79 userToLogin.set('username', userEmail);
80 userToLogin.set('email', userEmail);
81 try {
82 let loggedInUser: Parse.User = await userToLogin
83 .linkWith('facebook', {
84 authData: {id: userId, access_token: userAccessToken},
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 Facebook 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 <FacebookLogin
154 appId="4573670722644997"
155 fields="email"
156 callback={handleFacebookLoginLoginResponse}
157 render={(renderProps: {onClick: () => void}) => (
158 <div className="login-social-item login-social-item--facebook">
159 <img onClick={renderProps.onClick} className="login-social-item__image" src={'https://findicons.com/files/icons/2830/clean_social_icons/250/facebook.png'} alt=""/>
160 </div>
161 )}
162 />
163 <div className="login-social-item">
164 <img className="login-social-item__image" src={'data:image/png;base64,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'} alt=""/>
165 </div>
166 <div className="login-social-item">
167 <img className="login-social-item__image" src={'data:image/png;base64,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'} alt=""/>
168 </div>
169 </div>
170 <p className="form__hint">Don't have an account? <a className="form__link" href="#">Sign up</a></p>
171 </div>
172 )}
173 {currentUser !== null && (
174 <div className="container">
175 <h2 className="heading">{'User Screen'}</h2>
176 <Divider />
177 <h2 className="heading">{`Hello ${currentUser.get('username')}!`}</h2>
178 <div className="form_buttons">
179 <Button
180 onClick={() => doUserLogOut()}
181 type="primary"
182 className="form_button"
183 color={'#208AEC'}
184 size="large"
185 block
186 >
187 Log Out
188 </Button>
189 </div>
190 </div>
191 )}
192 </div>
193 );
194 };이 구성 요소의 레이아웃을 완전히 렌더링하려면 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 }새로운 기능을 테스트해 보세요. Facebook에 로그인할 수 있었고 Parse linkWith 호출이 성공적이었다면, 다음과 같은 성공 메시지를 볼 수 있어야 합니다.

3 - 사용자 로그인 확인 및 세션 생성
Facebook 로그인이 작동했는지 확인하려면, Parse 대시보드를 확인하고 새로운 사용자 (당신의 Facebook 인증 데이터가 다른 사용자에 속하지 않는 경우)와 함께 Facebook authData 매개변수를 확인할 수 있습니다.

대시보드에서 유효한 세션이 생성되었음을 확인할 수 있으며, 해당 사용자 객체에 대한 포인터가 포함되어 있습니다.

4 - 기존 사용자를 Facebook 로그인에 연결하기
또 다른 linkWith 가능한 사용법은 기존 사용자를 다른 인증 제공자, 이 경우에는 Facebook과 연결하는 것입니다. 다음 기능을 추가하여 linkWith를 호출합니다. 이는 UserLogIn 구성 요소에 로그인하는 것과 동일한 방식입니다. 여기서 유일한 차이점은 빈 Parse.User,에서 메서드를 호출하는 대신 현재 로그인된 사용자 객체에서 사용한다는 것입니다.
1 const handleFacebookLoginLinkResponse = 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 Facebook user info
9 const userId: string = response.id;
10 const userAccessToken: string = response.accessToken;
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('facebook', {
17 authData: {id: userId, access_token: userAccessToken},
18 });
19 // logIn returns the corresponding ParseUser object
20 alert(
21 `Success! User ${loggedInUser.get(
22 'username',
23 )} has successfully linked his Facebook 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 Facebook user info, please try again!")
35 return false;
36 }
37 }
38 }이 기능을 홈 화면의 다른 react-facebook-login 구성 요소에 할당하세요. 이 구성 요소는 앱에 현재 사용자가 로그인했을 때만 표시됩니다. 새로운 기능을 테스트하고, Parse.User 객체 authData 값이 새로운 인증 제공자 데이터로 업데이트될 것임을 주의하세요. 사용자가 실제로 Parse 서버 대시보드에서 업데이트되었는지 확인하세요.

결론
이 가이드의 끝에서, Facebook 로그인을 사용하여 React에서 Parse 사용자에게 로그인, 가입 또는 기존 사용자를 연결하는 방법을 배웠습니다. 다음 가이드에서는 Apple 로그인 사용 방법을 보여드리겠습니다.