---
title: 리액트에서 페이스북 로그인 구현: 개발자 가이드
slug: docs-kr/react/working-with-users/react-facebook-login
description: 리액트 앱에서 react-facebook-login을 활용해 페이스북 로그인을 구현하고 Parse로 사용자 인증을 연결하는 방법을 배워보세요.
image: https://www.back4app.com/images/logo-social.png
createdAt: 2024-03-19T17:19:48.511Z
---

# 리액트 페이스북 로그인

## 소개

지난 튜토리얼에서는 <font color="#2166ae">`Parse.User`</font> 클래스를 사용하여 앱에 사용자 로그인/로그아웃 기능을 추가했습니다. 이제 페이스북 로그인을 사용하여 페이스북에서 사용자 데이터를 가져오고, 로그인하거나 가입하거나 기존 사용자와 연결하는 방법을 배울 것입니다. 또한 이를 달성하기 위해 <font color="#2166ae">`react-facebook-login` </font> 라이브러리를 설치하고 구성할 것입니다.

&#x20;<font color="#2166ae">`Parse.User.linkWith`</font> 메서드는 각기 다른 제공자가 요청하는 올바른 매개변수를 전달하는 한, 모든 서드파티 인증 방법을 사용하여 사용자를 가입시키고 로그인시키는 역할을 합니다. 사용자 데이터를 새 또는 기존 <font color="#2166ae">`Parse.User`</font>에 연결한 후, Parse는 귀하의 장치에 유효한 사용자 세션을 저장합니다. 이후 <font color="#2166ae">`current` </font>과 같은 메서드를 호출하면 일반 로그인과 마찬가지로 사용자 데이터를 성공적으로 검색할 수 있습니다.

## 전제 조건

:::hint{type="info"}
이 튜토리얼을 완료하려면 다음이 필요합니다:

-  React 앱이 생성되고 [Back4App에 연결됨](https://www.back4app.com/docs/react/quickstart).
- 이전 가이드를 완료하여 [Parse.User 클래스와 Parse.User.logIn 메서드에 대한 더 나은 이해를 가질 수 있습니다.](https://www.back4app.com/docs/react/working-with-users/react-user-login)
- 이 가이드에서 제공하는 화면 레이아웃을 테스트/사용하려면 [<font color="#2166ae">`Ant Design`</font> 라이브러리를 설정해야 합니다.](https://ant.design/docs/react/introduce)
:::

## 목표

React 앱에서 Parse를 사용하여 Facebook 로그인을 통한 사용자 로그인 기능을 구축합니다.

## 1 - 의존성 설치

React에서 Facebook 로그인을 활성화하는 가장 인기 있는 방법은 <font color="#2166ae">`react-facebook-login`</font>을 사용하는 것입니다. [공식 문서](https://github.com/keppelen/react-facebook-login/)를 따라 설정하세요.

lib 구성 요소를 사용하려면 유효한 Facebook 앱 <font color="#2166ae">`appID`</font>, 을 알려야 하므로, Facebook for Developers 대시보드에서 하나를 생성하세요. 그들의 [공식 가이드](https://developers.facebook.com/docs/development/create-an-app/). 그 후, 앱에 Facebook 로그인 기능을 추가하고 WWW 옵션을 선택하여 <font color="#2166ae">`https://localhost:3000`</font>을 웹사이트 URL로 입력하세요.

Facebook 로그인을 사용하려면 연결이 안전해야 하며, 개발 환경에서 로그인할 때도 마찬가지입니다. 따라서 로컬 인증서를 생성하고, 운영 체제에서 전체 권한을 부여하고, 파일을 프로젝트에 추가하여 React 앱에 HTTPS 액세스를 설정해야 합니다. 각 운영 체제마다 특성이 다를 수 있으므로, 이 가이드에서는 다루지 않습니다.

인증서를 생성한 후, <font color="#2166ae">`scripts` </font>세션에서 <font color="#2166ae">`package.json`</font> 파일의 시작 명령을 다음과 같이 변경하세요:

```json
1   "start": "export HTTPS=true&&SSL_CRT_FILE=cert.pem&&SSL_KEY_FILE=key.pem react-scripts start",
```

Typescript를 사용하는 경우, <font color="#2166ae">`src` </font>폴더에 <font color="#2166ae">`react-facebook-login.d.ts`</font>라는 새 파일을 생성하고, <font color="#2166ae">`react-facebook-login`</font> 모듈 선언을 포함하여 스타일 없는 버전을 사용하세요:

:::CodeblockTabs
react-facebook-login.d.ts

```typescript
1   declare module 'react-facebook-login/dist/facebook-login-render-props';
```
:::

## 2 - 파스와 함께 페이스북 로그인 사용하기

이제 Facebook 로그인 인증 모달을 호출할 때 응답을 처리할 새로운 메서드를 <font color="#2166ae">`UserLogIn` </font>컴포넌트 안에 생성해 보겠습니다. 사용자가 Facebook으로 로그인하면 이 호출은 Facebook에서 사용자 데이터를 검색하고 <font color="#2166ae">`id`</font>, <font color="#2166ae">`accessToken`</font>, 그리고 프로필 <font color="#2166ae">`email`</font>. 그런 다음, 이 함수는 <font color="#2166ae">`Parse.User.linkWith`</font> 메서드와 이러한 자격 증명을 사용하여 Parse에 로그인하려고 시도합니다. 사용자가 이미 이 Facebook 인증을 사용하여 가입한 경우, <font color="#2166ae">`linkWith` </font>는 기존 계정을 사용하여 그를 로그인시킬 것입니다.

:::CodeblockTabs
JavaScript

```javascript
1	const handleFacebookLoginLoginResponse = 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 Facebook user info
9	      const userId = response.id;
10	      const userEmail = response.email;
11	      const userAccessToken = response.accessToken;
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 facebook profile email
16	      userToLogin.set('username', userEmail);
17	      userToLogin.set('email', userEmail);
18	      try {
19	        let loggedInUser = 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) {
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 Facebook user info, please try again!")
37	      return false;
38	    }
39	  }
40	}
```

```typescript
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	}
```
:::

그 후, <font color="#2166ae">`react-facebook-login`</font> <font color="#2166ae">`FacebookLogin`</font> 컴포넌트를 사용하여 Facebook 로그인 모달을 호출하고, 이를 JSX 코드에 추가해야 합니다. Facebook의 기본 스타일을 사용하거나 이 가이드에서 따르는 방법처럼 사용자 정의 스타일을 만들 수 있습니다. 다음은 전체 <font color="#2166ae">`UserLogin` </font> 컴포넌트 코드입니다. <font color="#2166ae">`react-facebook-login`</font> 버튼과 그것이 이전에 생성된 모달 응답 메서드와 어떻게 연결되어 있는지 주목하세요.

:::CodeblockTabs
JavaScript

```javascript
1	import React, { useState } from 'react';
2	import Parse from 'parse/dist/parse.min.js';
3	import FacebookLogin from 'react-facebook-login/dist/facebook-login-render-props';
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 handleFacebookLoginLoginResponse = 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 Facebook user info
74	        const userId = response.id;
75	        const userEmail = response.email;
76	        const userAccessToken = response.accessToken;
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 facebook profile email
81	        userToLogin.set('username', userEmail);
82	        userToLogin.set('email', userEmail);
83	        try {
84	          let loggedInUser = await userToLogin.linkWith('facebook', {
85	            authData: { id: userId, access_token: userAccessToken },
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 Facebook 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	            <FacebookLogin
157	              appId="4573670722644997"
158	              fields="email"
159	              callback={handleFacebookLoginLoginResponse}
160	              render={(renderProps) => (
161	                <div className="login-social-item login-social-item--facebook">
162	                  <img onClick={renderProps.onClick}  className="login-social-item__image" src={'https://findicons.com/files/icons/2830/clean_social_icons/250/facebook.png'} alt=""/>
163	                </div>
164	              )}
165	            />
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 className="login-social-item">
170	              <img className="login-social-item__image" src={'data:image/png;base64,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'} alt=""/>
171	            </div>
172	          </div>
173	          <p className="form__hint">Don't have an account? <a className="form__link" href="#">Sign up</a></p>
174	        </div>
175	      )}
176	      {currentUser !== null && (
177	        <div className="container">
178	          <h2 className="heading">{'User Screen'}</h2>
179	          <Divider />
180	          <h2 className="heading">{`Hello ${currentUser.get('username')}!`}</h2>
181	          <div className="form_buttons">
182	            <Button
183	              onClick={() => doUserLogOut()}
184	              type="primary"
185	              className="form_button"
186	              color={'#208AEC'}
187	              size="large"
188	              block
189	            >
190	              Log Out
191	            </Button>
192	          </div>
193	        </div>
194	      )}
195	    </div>
196	  );
197	};
```

```typescript
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	};
```
:::

이 구성 요소의 레이아웃을 완전히 렌더링하려면 <font color="#2166ae">`App.css`</font> 파일에 이러한 클래스를 추가하세요.

:::CodeblockTabs
App.css

```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 <font color="#2166ae">`linkWith`</font> 호출이 성공적이었다면, 다음과 같은 성공 메시지를 볼 수 있어야 합니다.

![](https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/ju89eb7Laa8HBiuWLN4ml_image.png)

## 3 - 사용자 로그인 확인 및 세션 생성

Facebook 로그인이 작동했는지 확인하려면, Parse 대시보드를 확인하고 새로운 <font color="#2166ae">`사용자`</font> (당신의 Facebook 인증 데이터가 다른 사용자에 속하지 않는 경우)와 함께 Facebook <font color="#2166ae">`authData`</font> 매개변수를 확인할 수 있습니다.

![](https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/Fz64NIvC6JXLdq5BEZeZe_image.png)

대시보드에서 유효한 세션이 생성되었음을 확인할 수 있으며, 해당 <font color="#2166ae">`사용자`</font> 객체에 대한 포인터가 포함되어 있습니다.

![](https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/OpSekb5DCXz2CI3Enp-qR_image.png)

## 4 - 기존 사용자를 Facebook 로그인에 연결하기

또 다른 <font color="#2166ae">`linkWith`</font> 가능한 사용법은 기존 사용자를 다른 인증 제공자, 이 경우에는 Facebook과 연결하는 것입니다. 다음 기능을 추가하여 <font color="#2166ae">`linkWith`</font>를 호출합니다. 이는 <font color="#2166ae">`UserLogIn`</font> 구성 요소에 로그인하는 것과 동일한 방식입니다. 여기서 유일한 차이점은 빈 <font color="#2166ae">`Parse.User`</font>,에서 메서드를 호출하는 대신 현재 로그인된 사용자 객체에서 사용한다는 것입니다.

:::CodeblockTabs
JavaScript

```javascript
1	const handleFacebookLoginLinkResponse = 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 Facebook user info
9	      const userId = response.id;
10	      const userAccessToken = response.accessToken;
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('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) {
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 Facebook user info, please try again!")
35	      return false;
36	    }
37	  }
38	}
```

```typescript
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	}
```
:::

이 기능을 홈 화면의 다른 <font color="#2166ae">`react-facebook-login`</font> 구성 요소에 할당하세요. 이 구성 요소는 앱에 현재 사용자가 로그인했을 때만 표시됩니다. 새로운 기능을 테스트하고, <font color="#2166ae">`Parse.User`</font> 객체 <font color="#2166ae">`authData`</font> 값이 새로운 인증 제공자 데이터로 업데이트될 것임을 주의하세요. 사용자가 실제로 Parse 서버 대시보드에서 업데이트되었는지 확인하세요.

![](https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/6r0_4qP2ScDfW6IJyWL8E_image.png)

## 결론

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