---
title: Parse 서버와 Flutter에서 Apple 로그인을 구현하는 방법
slug: docs-kr/flutter/parse-sdk/users/flutter-sign-in-with-apple
description: 이 튜토리얼은 Parse 서버에서 Flutter 앱에 Apple 로그인을 통합하는 방법을 설명합니다.
image: https://www.back4app.com/images/logo-social.png
createdAt: 2024-03-18T13:41:31.848Z
---

# Parse에서 Flutter로 Apple로 로그인하기

## 소개

Parse Server는 제3자 인증을 지원합니다.

이 가이드에서는 Parse에서 Flutter 앱에 Apple로 로그인을 지원하는 방법을 배웁니다.

## 전제 조건

**이 튜토리얼을 완료하려면 다음이 필요합니다:**

:::hint{type="info"}
- [Flutter 버전 2.2.x 이상](https://flutter.dev/docs/get-started/install)
- [Android Studio ](https://developer.android.com/studio) 또는 [VS Code 설치](https://code.visualstudio.com/) (과 [플러그인](https://docs.flutter.dev/get-started/editor) Dart 및 Flutter)
- Back4App에 연결된 Flutter 앱.
  - **참고:&#x20;**&#x20;[새 Parse 앱 튜토리얼](https://www.back4app.com/docs/get-started/new-parse-app)을 따라 Back4App에서 Parse 앱을 만드는 방법을 배우세요.
- &#x20;[Apple Developer Program](https://developer.apple.com/programs/)에 대한 유료 회원가입.
- 프로젝트에 <font color="#2166ae">`sign_in_with_apple`</font> 플러그인을 설정하세요.
- iOS에서 실행되는 장치(시뮬레이터 아님).
:::

## 목표

Parse Server에서 Flutter 앱으로 Apple로 로그인하기

## 1 - iOS 기본 프로젝트에 Apple로 로그인 기능 추가하기

- Xcode에서<font color="#2166ae">`ios/Runner.xcworkspace`</font> 열기
- 플러그인<font color="#2166ae">`sign_in_with_apple`</font> 설정을 위한 <font color="#2166ae">`Apple로 로그인`</font>을 확인하세요.
- 프로젝트에 대한<font color="#2166ae">`팀`</font> 선택하기.
- Xcode 저장 후 닫기

## 2 - 개발자 포털에서 앱 ID 구성하기

당신의 [Apple Developer 계정](https://developer.apple.com/)에 로그인하고 <font color="#2166ae">`식별자`</font> 섹션으로 이동하세요.

생성한 <font color="#2166ae">`번들 식별자`</font>가 있는지 확인하세요.

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

&#x20;<font color="#2166ae">`번들 식별자`</font>를 클릭하고 아래로 스크롤하세요. <font color="#2166ae">`Apple로 로그인`</font>이 선택되어 있는지 확인하세요.

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

&#x20;<font color="#2166ae">`편집`</font>을 클릭하고 <font color="#2166ae">`기본 앱 ID로 활성화`</font>가 선택되어 있는지 확인하세요.

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

모든 것이 올바르면 저장하고 종료하세요.

## 3 - Apple을 위한 Parse Auth 설정하기

Back4App 웹사이트로 이동하여 로그인한 후 앱을 찾으세요. 그 후, <font color="#2166ae">`서버 설정`</font>을 클릭하고 <font color="#2166ae">`Apple 로그인`</font> 블록을 검색한 후 <font color="#2166ae">`설정`</font>을 선택하세요.

&#x20;<font color="#2166ae">`Apple 로그인`</font> 섹션은 다음과 같습니다:

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/4Ub_ocO9ziz_qnUJ8xgB6_image.png" size="50" width="255" height="314" position="center" showCaption="false"}

이제 아래 필드에 <font color="#2166ae">`번들 ID`</font>를 붙여넣고 저장 버튼을 클릭하면 됩니다.

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/CxqEBlJmrs46J1cocmwas_image.png" size="60" width="599" height="454" position="center" showCaption="false"}

Apple 로그인 통합 중 문제가 발생하면, 채팅을 통해 저희 팀에 문의해 주세요!

## 4 - Apple로 로그인 추가하기

프로젝트가 설정되었으니, 사용자 데이터를 가져오고 Parse에 로그인할 수 있습니다.

문서에 따르면, 사용자 인증 데이터가 포함된 맵을 전송해야 합니다.

```swift
1         final credential = await SignInWithApple.getAppleIDCredential(
2           scopes: [
3             AppleIDAuthorizationScopes.email,
4             AppleIDAuthorizationScopes.fullName,
5           ],
6         );
7 
8         //https://docs.parseplatform.org/parse-server/guide/#apple-authdata
9         //According to the documentation, we must send a Map with user authentication data.
10        //Make sign in with Apple
11        final parseResponse = await ParseUser.loginWith('apple',
12            apple(credential.identityToken!, credential.userIdentifier!));
```

## 5 - Flutter에서 Apple로 로그인하기

이제 Flutter 앱에서 Apple로 로그인하는 예제를 사용해 보겠습니다. 간단한 인터페이스로.

Flutter 프로젝트를 열고, main.dart 파일로 이동하여 모든 코드를 정리한 후 다음으로 교체하세요:

```dart
1   import 'package:flutter/material.dart';
2   import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
3   import 'package:sign_in_with_apple/sign_in_with_apple.dart';
4
5   void main() async {
6     WidgetsFlutterBinding.ensureInitialized();
7
8     final keyApplicationId = 'YOUR_APP_ID_HERE';
9     final keyClientKey = 'YOUR_CLIENT_KEY_HERE';
10    final keyParseServerUrl = 'https://parseapi.back4app.com';
11
12    await Parse().initialize(keyApplicationId, keyParseServerUrl,
13        clientKey: keyClientKey, debug: true);
14
15    runApp(MyApp());
16  }
17
18  class MyApp extends StatelessWidget {
19    Future<bool> hasUserLogged() async {
20      ParseUser? currentUser = await ParseUser.currentUser() as ParseUser?;
21      if (currentUser == null) {
22        return false;
23      }
24      //Checks whether the user's session token is valid
25      final ParseResponse? parseResponse =
26          await ParseUser.getCurrentUserFromServer(currentUser.sessionToken!);
27  
28      if (parseResponse?.success == null || !parseResponse!.success) {
29        //Invalid session. Logout
30        await currentUser.logout();
31        return false;
32      } else {
33        return true;
34      }
35    }
36 
37    @override
38    Widget build(BuildContext context) {
39      return MaterialApp(
40        title: 'Flutter - Sign In with Apple',
41        theme: ThemeData(
42          primarySwatch: Colors.blue,
43          visualDensity: VisualDensity.adaptivePlatformDensity,
44        ),
45        home: FutureBuilder<bool>(
46            future: hasUserLogged(),
47            builder: (context, snapshot) {
48              switch (snapshot.connectionState) {
49                case ConnectionState.none:
50                case ConnectionState.waiting:
51                  return Scaffold(
52                    body: Center(
53                      child: Container(
54                          width: 100,
55                          height: 100,
56                          child: CircularProgressIndicator()),
57                    ),
58                  );
59                default:
60                  if (snapshot.hasData && snapshot.data!) {
61                    return UserPage();
62                  } else {
63                    return HomePage();
64                  }
65              }
66            }),
67      );
68    }
69  }
70  
71  class HomePage extends StatefulWidget {
72    @override
73    _HomePageState createState() => _HomePageState();
74  }
75
76  class _HomePageState extends State<HomePage> {
77    @override
78    Widget build(BuildContext context) {
79      return Scaffold(
80          appBar: AppBar(
81            title: const Text('Flutter - Sign In with Apple'),
82          ),
83          body: Center(
84            child: SingleChildScrollView(
85              padding: const EdgeInsets.all(8),
86              child: Column(
87                crossAxisAlignment: CrossAxisAlignment.stretch,
88                children: [
89                  Container(
90                    height: 200,
91                    child: Image.network(
92                        'http://blog.back4app.com/wp-content/uploads/2017/11/logo-b4a-1-768x175-1.png'),
93                  ),
94                  Center(
95                    child: const Text('Flutter on Back4App',
96                        style:
97                            TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
98                  ),
99                  SizedBox(
100                   height: 100,
101                 ),
102                 Container(
103                   height: 50,
104                   child: ElevatedButton(
105                     child: const Text('Sign In with Apple'),
106                     onPressed: () => doSignInApple(),
107                   ),
108                 ),
109                 SizedBox(
110                   height: 16,
111                 ),
112               ],
113             ),
114           ),
115         ));
116   }
117 
118   void doSignInApple() async {
119     late ParseResponse parseResponse;
120     try {
121       //Set Scope
122       final credential = await SignInWithApple.getAppleIDCredential(
123         scopes: [
124           AppleIDAuthorizationScopes.email,
125           AppleIDAuthorizationScopes.fullName,
126         ],
127       );
128
129       //https://docs.parseplatform.org/parse-server/guide/#apple-authdata
130       //According to the documentation, we must send a Map with user authentication data.
131       //Make sign in with Apple
132       parseResponse = await ParseUser.loginWith('apple',
133           apple(credential.identityToken!, credential.userIdentifier!));
134 
135       if (parseResponse.success) {
136         final ParseUser parseUser = await ParseUser.currentUser() as ParseUser;
137 
138         //Additional Information in User
139         if (credential.email != null) {
140           parseUser.emailAddress = credential.email;
141         }
142         if (credential.givenName != null && credential.familyName != null) {
143           parseUser.set<String>(
144               'name', '${credential.givenName} ${credential.familyName}');
145         }
146         parseResponse = await parseUser.save();
147         if (parseResponse.success) {
148           Message.showSuccess(
149               context: context,
150               message: 'User was successfully with Sign In Apple!',
151               onPressed: () async {
152                 Navigator.pushAndRemoveUntil(
153                   context,
154                   MaterialPageRoute(builder: (context) => UserPage()),
155                   (Route<dynamic> route) => false,
156                 );
157               });
158         } else {
159           Message.showError(
160               context: context, message: parseResponse.error!.message);
161         }
162       } else {
163         Message.showError(
164             context: context, message: parseResponse.error!.message);
165       }
166     } on Exception catch (e) {
167       print(e.toString());
168       Message.showError(context: context, message: e.toString());
169     }
170   }
171 }
172
173 class UserPage extends StatelessWidget {
174   Future<ParseUser?> getUser() async {
175     return await ParseUser.currentUser() as ParseUser?;
176   }
177
178   @override
179   Widget build(BuildContext context) {
180     void doUserLogout() async {
181       final currentUser = await ParseUser.currentUser() as ParseUser;
182       var response = await currentUser.logout();
183       if (response.success) {
184         Message.showSuccess(
185             context: context,
186             message: 'User was successfully logout!',
187             onPressed: () {
188               Navigator.pushAndRemoveUntil(
189                 context,
190                 MaterialPageRoute(builder: (context) => HomePage()),
191                 (Route<dynamic> route) => false,
192               );
193             });
194       } else {
195         Message.showError(context: context, message: response.error!.message);
196       }
197     }
198 
199     return Scaffold(
200         appBar: AppBar(
201           title: Text('Flutter - Sign In with Apple'),
202         ),
203         body: FutureBuilder<ParseUser?>(
204             future: getUser(),
205             builder: (context, snapshot) {
206               switch (snapshot.connectionState) {
207                 case ConnectionState.none:
208                 case ConnectionState.waiting:
209                   return Center(
210                     child: Container(
211                         width: 100,
212                         height: 100,
213                         child: CircularProgressIndicator()),
214                   );
215                 default:
216                   return Padding(
217                     padding: const EdgeInsets.all(8.0),
218                     child: Column(
219                       crossAxisAlignment: CrossAxisAlignment.stretch,
220                       mainAxisAlignment: MainAxisAlignment.center,
221                       children: [
222                         Center(
223                             child: Text(
224                                 'Hello, ${snapshot.data!.get<String>('name')}')),
225                         SizedBox(
226                           height: 16,
227                         ),
228                         Container(
229                           height: 50,
230                           child: ElevatedButton(
231                             child: const Text('Logout'),
232                             onPressed: () => doUserLogout(),
233                           ),
234                         ),
235                       ],
236                     ),
237                   );
238               }
239             }));
240   }
241 }
242 
243 class Message {
244   static void showSuccess(
245       {required BuildContext context,
246       required String message,
247       VoidCallback? onPressed}) {
248     showDialog(
249       context: context,
250       builder: (BuildContext context) {
251         return AlertDialog(
252           title: const Text("Success!"),
253           content: Text(message),
254           actions: <Widget>[
255             new ElevatedButton(
256               child: const Text("OK"),
257               onPressed: () {
258                 Navigator.of(context).pop();
259                 if (onPressed != null) {
260                   onPressed();
261                 }
262               },
263             ),
264           ],
265         );
266       },
267     );
268   }
269 
270   static void showError(
271       {required BuildContext context,
272       required String message,
273       VoidCallback? onPressed}) {
274     showDialog(
275       context: context,
276       builder: (BuildContext context) {
277         return AlertDialog(
278           title: const Text("Error!"),
279           content: Text(message),
280           actions: <Widget>[
281             new ElevatedButton(
282               child: const Text("OK"),
283               onPressed: () {
284                 Navigator.of(context).pop();
285                 if (onPressed != null) {
286                   onPressed();
287                 }
288               },
289             ),
290           ],
291         );
292       },
293     );
294   }
295 }
```

애플리케이션 ID와 클라이언트 키 자격 증명을 찾으려면 [Back4App 웹사이트](https://www.back4app.com/)로 이동하세요.

코드를 <font color="#2166ae">`main.dart`</font>에서 Back4app의 애플리케이션 ID와 클라이언트 키 값으로 업데이트하세요.

- **keyApplicationId =&#x20;**<font color="#2166ae">`앱 ID`</font>
- **keyClientKey =&#x20;**<font color="#2166ae">`클라이언트 키`</font>

프로젝트를 실행하면 앱이 이미지와 같이 로드됩니다.

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/Gprp9LZAobuixIswWSTxz_image.png" size="50" width="313" height="628" position="center" showCaption="false"}

## 결론

이 단계에서, 당신은 Back4app에서 Flutter로 Apple로 로그인 기능을 사용할 수 있습니다.
