From ee4711380174f7add1e9e9b5cfd8bf33aab0cfba Mon Sep 17 00:00:00 2001 From: kkosang Date: Tue, 6 May 2025 20:06:05 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20yaml=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pubspec.lock | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++-- pubspec.yaml | 2 ++ 2 files changed, 85 insertions(+), 3 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 656c2c0..6fda208 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -334,6 +334,54 @@ packages: url: "https://pub.dev" source: hosted version: "2.6.1" + flutter_secure_storage: + dependency: "direct main" + description: + name: flutter_secure_storage + sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" + url: "https://pub.dev" + source: hosted + version: "9.2.4" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247" + url: "https://pub.dev" + source: hosted + version: "3.1.3" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + url: "https://pub.dev" + source: hosted + version: "3.1.2" flutter_svg: dependency: "direct main" description: @@ -460,10 +508,10 @@ packages: dependency: transitive description: name: js - sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.6.7" json_annotation: dependency: transitive description: @@ -600,6 +648,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + url: "https://pub.dev" + source: hosted + version: "2.2.17" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + url: "https://pub.dev" + source: hosted + version: "2.4.1" path_provider_linux: dependency: transitive description: @@ -689,7 +761,7 @@ packages: source: hosted version: "1.4.2+1" shared_preferences: - dependency: transitive + dependency: "direct main" description: name: shared_preferences sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" @@ -909,6 +981,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + win32: + dependency: transitive + description: + name: win32 + sha256: dc6ecaa00a7c708e5b4d10ee7bec8c270e9276dfcab1783f57e9962d7884305f + url: "https://pub.dev" + source: hosted + version: "5.12.0" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 43c4870..0d9d9be 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,6 +49,8 @@ dependencies: image_picker: ^1.1.2 cloud_firestore: ^5.6.6 gif: ^2.3.0 + shared_preferences: ^2.5.3 + flutter_secure_storage: ^9.2.4 dev_dependencies: flutter_test: From 2226778990259cfc76a28af6e2b8b5253f1a8320 Mon Sep 17 00:00:00 2001 From: kkosang Date: Tue, 6 May 2025 20:21:41 +0900 Subject: [PATCH 2/3] =?UTF-8?q?refactor:=20=EC=86=8C=EC=85=9C=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20dto=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=B6=84=EA=B8=B0=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - email 정보 받도록 dto 수정, 로그인 이탈 시 분기처리 --- android/app/src/main/AndroidManifest.xml | 13 + ios/Podfile.lock | 1368 +++++++++++++++++ ios/Runner/RunnerDebug.entitlements | 4 + .../local/login_local_datasource.dart | 26 + .../remote/auth_remote_datasource.dart | 16 +- .../remote/login_remote_datasource.dart | 10 +- .../data/models/request/login_request.dart | 5 +- .../data/models/response/login_response.dart | 6 +- .../default_login_repository.dart | 6 +- .../domain/model/identity/phone_number.dart | 2 + lib/auth/domain/model/sign_up_state.dart | 16 + .../domain/repositories/login_repository.dart | 2 +- lib/auth/domain/usecases/login_usecase.dart | 6 +- .../phone_number_verification_usecase.dart | 9 +- .../congratulation/congratulation_page.dart | 14 +- .../identity/identity_verification_page.dart | 5 +- .../pages/login/login_viewmodel.dart | 49 +- lib/core/utills/network/dio_provider.dart | 20 +- lib/main.dart | 47 +- 19 files changed, 1573 insertions(+), 51 deletions(-) create mode 100644 lib/auth/data/datasources/local/login_local_datasource.dart create mode 100644 lib/auth/domain/model/sign_up_state.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 70d1b84..cadf740 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -54,6 +54,19 @@ + + + + + + + + + 11.10.0) - Firebase/CoreOnly (11.10.0): - FirebaseCore (~> 11.10.0) + - Firebase/Firestore (11.10.0): + - Firebase/CoreOnly + - FirebaseFirestore (~> 11.10.0) - firebase_auth (5.5.2): - Firebase/Auth (= 11.10.0) - firebase_core @@ -30,7 +1232,30 @@ PODS: - FirebaseCore (~> 11.10.0) - FirebaseCoreInternal (11.10.0): - "GoogleUtilities/NSData+zlib (~> 8.0)" + - FirebaseFirestore (11.10.0): + - FirebaseCore (~> 11.10.0) + - FirebaseCoreExtension (~> 11.10.0) + - FirebaseFirestoreInternal (= 11.10.0) + - FirebaseSharedSwift (~> 11.0) + - FirebaseFirestoreInternal (11.10.0): + - abseil/algorithm (~> 1.20240722.0) + - abseil/base (~> 1.20240722.0) + - abseil/container/flat_hash_map (~> 1.20240722.0) + - abseil/memory (~> 1.20240722.0) + - abseil/meta (~> 1.20240722.0) + - abseil/strings/strings (~> 1.20240722.0) + - abseil/time (~> 1.20240722.0) + - abseil/types (~> 1.20240722.0) + - FirebaseAppCheckInterop (~> 11.0) + - FirebaseCore (~> 11.10.0) + - "gRPC-C++ (~> 1.69.0)" + - gRPC-Core (~> 1.69.0) + - leveldb-library (~> 1.22) + - nanopb (~> 3.30910.0) + - FirebaseSharedSwift (11.12.0) - Flutter (1.0.0) + - flutter_secure_storage (6.0.0): + - Flutter - fluttertoast (0.0.2): - Flutter - GoogleUtilities/AppDelegateSwizzler (8.0.2): @@ -54,9 +1279,112 @@ PODS: - GoogleUtilities/Reachability (8.0.2): - GoogleUtilities/Logger - GoogleUtilities/Privacy + - "gRPC-C++ (1.69.0)": + - "gRPC-C++/Implementation (= 1.69.0)" + - "gRPC-C++/Interface (= 1.69.0)" + - "gRPC-C++/Implementation (1.69.0)": + - abseil/algorithm/container (~> 1.20240722.0) + - abseil/base/base (~> 1.20240722.0) + - abseil/base/config (~> 1.20240722.0) + - abseil/base/core_headers (~> 1.20240722.0) + - abseil/base/log_severity (~> 1.20240722.0) + - abseil/base/no_destructor (~> 1.20240722.0) + - abseil/cleanup/cleanup (~> 1.20240722.0) + - abseil/container/flat_hash_map (~> 1.20240722.0) + - abseil/container/flat_hash_set (~> 1.20240722.0) + - abseil/container/inlined_vector (~> 1.20240722.0) + - abseil/flags/flag (~> 1.20240722.0) + - abseil/flags/marshalling (~> 1.20240722.0) + - abseil/functional/any_invocable (~> 1.20240722.0) + - abseil/functional/bind_front (~> 1.20240722.0) + - abseil/functional/function_ref (~> 1.20240722.0) + - abseil/hash/hash (~> 1.20240722.0) + - abseil/log/absl_check (~> 1.20240722.0) + - abseil/log/absl_log (~> 1.20240722.0) + - abseil/log/check (~> 1.20240722.0) + - abseil/log/globals (~> 1.20240722.0) + - abseil/log/log (~> 1.20240722.0) + - abseil/memory/memory (~> 1.20240722.0) + - abseil/meta/type_traits (~> 1.20240722.0) + - abseil/numeric/bits (~> 1.20240722.0) + - abseil/random/bit_gen_ref (~> 1.20240722.0) + - abseil/random/distributions (~> 1.20240722.0) + - abseil/random/random (~> 1.20240722.0) + - abseil/status/status (~> 1.20240722.0) + - abseil/status/statusor (~> 1.20240722.0) + - abseil/strings/cord (~> 1.20240722.0) + - abseil/strings/str_format (~> 1.20240722.0) + - abseil/strings/strings (~> 1.20240722.0) + - abseil/synchronization/synchronization (~> 1.20240722.0) + - abseil/time/time (~> 1.20240722.0) + - abseil/types/optional (~> 1.20240722.0) + - abseil/types/span (~> 1.20240722.0) + - abseil/types/variant (~> 1.20240722.0) + - abseil/utility/utility (~> 1.20240722.0) + - "gRPC-C++/Interface (= 1.69.0)" + - "gRPC-C++/Privacy (= 1.69.0)" + - gRPC-Core (= 1.69.0) + - "gRPC-C++/Interface (1.69.0)" + - "gRPC-C++/Privacy (1.69.0)" + - gRPC-Core (1.69.0): + - gRPC-Core/Implementation (= 1.69.0) + - gRPC-Core/Interface (= 1.69.0) + - gRPC-Core/Implementation (1.69.0): + - abseil/algorithm/container (~> 1.20240722.0) + - abseil/base/base (~> 1.20240722.0) + - abseil/base/config (~> 1.20240722.0) + - abseil/base/core_headers (~> 1.20240722.0) + - abseil/base/log_severity (~> 1.20240722.0) + - abseil/base/no_destructor (~> 1.20240722.0) + - abseil/cleanup/cleanup (~> 1.20240722.0) + - abseil/container/flat_hash_map (~> 1.20240722.0) + - abseil/container/flat_hash_set (~> 1.20240722.0) + - abseil/container/inlined_vector (~> 1.20240722.0) + - abseil/flags/flag (~> 1.20240722.0) + - abseil/flags/marshalling (~> 1.20240722.0) + - abseil/functional/any_invocable (~> 1.20240722.0) + - abseil/functional/bind_front (~> 1.20240722.0) + - abseil/functional/function_ref (~> 1.20240722.0) + - abseil/hash/hash (~> 1.20240722.0) + - abseil/log/check (~> 1.20240722.0) + - abseil/log/globals (~> 1.20240722.0) + - abseil/log/log (~> 1.20240722.0) + - abseil/memory/memory (~> 1.20240722.0) + - abseil/meta/type_traits (~> 1.20240722.0) + - abseil/numeric/bits (~> 1.20240722.0) + - abseil/random/bit_gen_ref (~> 1.20240722.0) + - abseil/random/distributions (~> 1.20240722.0) + - abseil/random/random (~> 1.20240722.0) + - abseil/status/status (~> 1.20240722.0) + - abseil/status/statusor (~> 1.20240722.0) + - abseil/strings/cord (~> 1.20240722.0) + - abseil/strings/str_format (~> 1.20240722.0) + - abseil/strings/strings (~> 1.20240722.0) + - abseil/synchronization/synchronization (~> 1.20240722.0) + - abseil/time/time (~> 1.20240722.0) + - abseil/types/optional (~> 1.20240722.0) + - abseil/types/span (~> 1.20240722.0) + - abseil/types/variant (~> 1.20240722.0) + - abseil/utility/utility (~> 1.20240722.0) + - BoringSSL-GRPC (= 0.0.37) + - gRPC-Core/Interface (= 1.69.0) + - gRPC-Core/Privacy (= 1.69.0) + - gRPC-Core/Interface (1.69.0) + - gRPC-Core/Privacy (1.69.0) - GTMSessionFetcher/Core (4.4.0) + - image_picker_ios (0.0.1): + - Flutter - kakao_flutter_sdk_common (1.9.7-3): - Flutter + - leveldb-library (1.22.6) + - nanopb (3.30910.0): + - nanopb/decode (= 3.30910.0) + - nanopb/encode (= 3.30910.0) + - nanopb/decode (3.30910.0) + - nanopb/encode (3.30910.0) + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS - RecaptchaInterop (101.0.0) - screen_protector (1.2.1): - Flutter @@ -65,18 +1393,27 @@ PODS: - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS + - sign_in_with_apple (0.0.1): + - Flutter DEPENDENCIES: + - cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`) - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - Flutter (from `Flutter`) + - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) + - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - kakao_flutter_sdk_common (from `.symlinks/plugins/kakao_flutter_sdk_common/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - screen_protector (from `.symlinks/plugins/screen_protector/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) + - sign_in_with_apple (from `.symlinks/plugins/sign_in_with_apple/ios`) SPEC REPOS: trunk: + - abseil + - BoringSSL-GRPC - Firebase - FirebaseAppCheckInterop - FirebaseAuth @@ -84,28 +1421,48 @@ SPEC REPOS: - FirebaseCore - FirebaseCoreExtension - FirebaseCoreInternal + - FirebaseFirestore + - FirebaseFirestoreInternal + - FirebaseSharedSwift - GoogleUtilities + - "gRPC-C++" + - gRPC-Core - GTMSessionFetcher + - leveldb-library + - nanopb - RecaptchaInterop - ScreenProtectorKit EXTERNAL SOURCES: + cloud_firestore: + :path: ".symlinks/plugins/cloud_firestore/ios" firebase_auth: :path: ".symlinks/plugins/firebase_auth/ios" firebase_core: :path: ".symlinks/plugins/firebase_core/ios" Flutter: :path: Flutter + flutter_secure_storage: + :path: ".symlinks/plugins/flutter_secure_storage/ios" fluttertoast: :path: ".symlinks/plugins/fluttertoast/ios" + image_picker_ios: + :path: ".symlinks/plugins/image_picker_ios/ios" kakao_flutter_sdk_common: :path: ".symlinks/plugins/kakao_flutter_sdk_common/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/darwin" screen_protector: :path: ".symlinks/plugins/screen_protector/ios" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" + sign_in_with_apple: + :path: ".symlinks/plugins/sign_in_with_apple/ios" SPEC CHECKSUMS: + abseil: a05cc83bf02079535e17169a73c5be5ba47f714b + BoringSSL-GRPC: dded2a44897e45f28f08ae87a55ee4bcd19bc508 + cloud_firestore: aef3217af294cd35afda47e63112d306f4c9a2e2 Firebase: 1fe1c0a7d9aaea32efe01fbea5f0ebd8d70e53a2 firebase_auth: e37065f3f80ff90580c13ad0e5a48e3bb8d2ad77 firebase_core: 432718558359a8c08762151b5f49bb0f093eb6e0 @@ -115,15 +1472,26 @@ SPEC CHECKSUMS: FirebaseCore: 8344daef5e2661eb004b177488d6f9f0f24251b7 FirebaseCoreExtension: 6f357679327f3614e995dc7cf3f2d600bdc774ac FirebaseCoreInternal: ef4505d2afb1d0ebbc33162cb3795382904b5679 + FirebaseFirestore: 3f1488ff7739cb3c5d10e572bc4e9fcd8e8cb4ac + FirebaseFirestoreInternal: 97a2bb5f16951c77753c860d3519379702ab6f8a + FirebaseSharedSwift: d2475748a2d2a36242ed13baa34b2acda846c925 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12 fluttertoast: 21eecd6935e7064cc1fcb733a4c5a428f3f24f0f GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d + "gRPC-C++": cc207623316fb041a7a3e774c252cf68a058b9e8 + gRPC-Core: 860978b7db482de8b4f5e10677216309b5ff6330 GTMSessionFetcher: 75b671f9e551e4c49153d4c4f8659ef4f559b970 + image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 kakao_flutter_sdk_common: 3dc8492c202af7853585d151490b1c5c6b7576cb + leveldb-library: cc8b8f8e013647a295ad3f8cd2ddf49a6f19be19 + nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 RecaptchaInterop: 11e0b637842dfb48308d242afc3f448062325aba screen_protector: 6f92086bd2f2f4b54f54913289b9d1310610140b ScreenProtectorKit: 83a6281b02c7a5902ee6eac4f5045f674e902ae4 shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 + sign_in_with_apple: f3bf75217ea4c2c8b91823f225d70230119b8440 PODFILE CHECKSUM: f8c2dcdfb50bb67645580d28a6bf814fca30bdec diff --git a/ios/Runner/RunnerDebug.entitlements b/ios/Runner/RunnerDebug.entitlements index 903def2..80b5221 100644 --- a/ios/Runner/RunnerDebug.entitlements +++ b/ios/Runner/RunnerDebug.entitlements @@ -4,5 +4,9 @@ aps-environment development + com.apple.developer.applesignin + + Default + diff --git a/lib/auth/data/datasources/local/login_local_datasource.dart b/lib/auth/data/datasources/local/login_local_datasource.dart new file mode 100644 index 0000000..19730b9 --- /dev/null +++ b/lib/auth/data/datasources/local/login_local_datasource.dart @@ -0,0 +1,26 @@ +import 'dart:developer'; + +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; + +class LoginLocalDataSource{ + static final _storage = FlutterSecureStorage(); + static const _jwtKey = 'jwt_token'; + + /// JWT 저장 + static Future saveToken(String token) async { + await _storage.write(key: _jwtKey, value: token); + log(name: 'Dio Interceptor', '토큰 저장 : $token'); + } + + /// JWT 불러오기 + static Future loadToken() async { + final token = await _storage.read(key: _jwtKey); + log(name: 'Dio Interceptor', '토큰 불러오기: $token'); + return token; + } + + /// JWT 삭제 + static Future deleteToken() async { + await _storage.delete(key: _jwtKey); + } +} diff --git a/lib/auth/data/datasources/remote/auth_remote_datasource.dart b/lib/auth/data/datasources/remote/auth_remote_datasource.dart index 8fe9a7a..5953b5e 100644 --- a/lib/auth/data/datasources/remote/auth_remote_datasource.dart +++ b/lib/auth/data/datasources/remote/auth_remote_datasource.dart @@ -1,4 +1,8 @@ +import 'dart:developer'; + import '../../../../core/utills/network/auth_api_service.dart'; +import '../../../../main.dart'; +import '../../../domain/model/sign_up_state.dart'; import '../../models/request/login_request.dart'; class AuthRemoteDataSource { @@ -9,11 +13,21 @@ class AuthRemoteDataSource { Future loginWithOauth({ required String type, required String id, + required String email, }) async { final response = await api.login( - LoginRequest(oauthType: type, oauthId: id), + LoginRequest(oauthType: type, oauthId: id, email: email), ); final jwt = response.headers.value('Authorization') ?? ''; + final userStatus = response.data['memberStatus'] ?? ''; + + final state = SignUpState.fromString(userStatus); + await saveSignUpState(state); + + log( + name: 'LoginViewModel::loginWithOauth', + 'success: $userStatus', + ); return jwt; } diff --git a/lib/auth/data/datasources/remote/login_remote_datasource.dart b/lib/auth/data/datasources/remote/login_remote_datasource.dart index cd210f6..438cb4a 100644 --- a/lib/auth/data/datasources/remote/login_remote_datasource.dart +++ b/lib/auth/data/datasources/remote/login_remote_datasource.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:kakao_flutter_sdk_user/kakao_flutter_sdk_user.dart'; import 'package:sign_in_with_apple/sign_in_with_apple.dart'; @@ -15,8 +17,7 @@ class LoginRemoteDataSource { } Future loginWithApple() async { - final AuthorizationCredentialAppleID - credential = await SignInWithApple.getAppleIDCredential( + final credential = await SignInWithApple.getAppleIDCredential( scopes: [ AppleIDAuthorizationScopes.email, AppleIDAuthorizationScopes.fullName, @@ -28,6 +29,11 @@ class LoginRemoteDataSource { ), ), ); + + log( + name: 'LoginRemoteDataSource::loginWithApple', + 'success: ${credential.identityToken}', + ); return credential; } } diff --git a/lib/auth/data/models/request/login_request.dart b/lib/auth/data/models/request/login_request.dart index 31d9d88..4358de7 100644 --- a/lib/auth/data/models/request/login_request.dart +++ b/lib/auth/data/models/request/login_request.dart @@ -1,8 +1,9 @@ class LoginRequest { final String oauthType; final String oauthId; + final String email; - LoginRequest({required this.oauthType, required this.oauthId}); + LoginRequest({required this.oauthType, required this.oauthId, required this.email}); - Map toJson() => {'oauthType': oauthType, 'oauthId': oauthId}; + Map toJson() => {'oauthType': oauthType, 'oauthId': oauthId, 'email': email}; } diff --git a/lib/auth/data/models/response/login_response.dart b/lib/auth/data/models/response/login_response.dart index 5ce88bf..51145e1 100644 --- a/lib/auth/data/models/response/login_response.dart +++ b/lib/auth/data/models/response/login_response.dart @@ -1,13 +1,13 @@ class LoginResponse { - final String memberStatus; + final String userStatus; final String jwt; - LoginResponse({required this.memberStatus, required this.jwt}); + LoginResponse({required this.userStatus, required this.jwt}); factory LoginResponse.fromJson( Map json, { required String jwt, }) { - return LoginResponse(memberStatus: json['memberStatus'], jwt: jwt); + return LoginResponse(userStatus: json['userStatus'], jwt: jwt); } } diff --git a/lib/auth/data/repositories/default_login_repository.dart b/lib/auth/data/repositories/default_login_repository.dart index b07e1cc..f007561 100644 --- a/lib/auth/data/repositories/default_login_repository.dart +++ b/lib/auth/data/repositories/default_login_repository.dart @@ -21,11 +21,11 @@ class DefaultLoginRepository implements LoginRepository { @override Future loginWithApple() { - throw loginRemoteDataSource.loginWithApple(); + return loginRemoteDataSource.loginWithApple(); } @override - Future loginOauth(String type, String id) { - return authRemoteDataSource.loginWithOauth(type: type, id: id); + Future loginOauth(String type, String id, String email) { + return authRemoteDataSource.loginWithOauth(type: type, id: id,email: email); } } diff --git a/lib/auth/domain/model/identity/phone_number.dart b/lib/auth/domain/model/identity/phone_number.dart index f4a3117..fd51619 100644 --- a/lib/auth/domain/model/identity/phone_number.dart +++ b/lib/auth/domain/model/identity/phone_number.dart @@ -1,4 +1,6 @@ +import 'dart:developer'; + class PhoneNumber { final String value; diff --git a/lib/auth/domain/model/sign_up_state.dart b/lib/auth/domain/model/sign_up_state.dart new file mode 100644 index 0000000..b0072c6 --- /dev/null +++ b/lib/auth/domain/model/sign_up_state.dart @@ -0,0 +1,16 @@ +enum SignUpState { + NONE, + SIGNUP, + IDENTIFY, + CODE_SURVEY, + CODE_PROFILE_IMAGE, + PENDING, + DONE; + + static SignUpState fromString(String? value) { + return SignUpState.values.firstWhere( + (e) => e.name == value, + orElse: () => SignUpState.NONE, + ); + } +} diff --git a/lib/auth/domain/repositories/login_repository.dart b/lib/auth/domain/repositories/login_repository.dart index fa76f39..6dcaa36 100644 --- a/lib/auth/domain/repositories/login_repository.dart +++ b/lib/auth/domain/repositories/login_repository.dart @@ -2,7 +2,7 @@ import 'package:kakao_flutter_sdk_user/kakao_flutter_sdk_user.dart'; import 'package:sign_in_with_apple/sign_in_with_apple.dart'; abstract class LoginRepository { - Future loginOauth(String type, String id); + Future loginOauth(String type, String id, String email); Future loginWithKakao(); Future loginWithApple(); } diff --git a/lib/auth/domain/usecases/login_usecase.dart b/lib/auth/domain/usecases/login_usecase.dart index 3203eb9..31adfba 100644 --- a/lib/auth/domain/usecases/login_usecase.dart +++ b/lib/auth/domain/usecases/login_usecase.dart @@ -15,12 +15,12 @@ class LoginUseCase { return await repository.loginWithApple(); } - Future callOauth(LoginType type, String id) async { + Future callOauth(LoginType type, String id, String email) async { switch (type) { case LoginType.kakao: - return await repository.loginOauth('KAKAO', id); + return await repository.loginOauth('KAKAO', id, email); case LoginType.apple: - return await repository.loginOauth('APPLE', id); + return await repository.loginOauth('APPLE', id, email); } } } diff --git a/lib/auth/domain/usecases/phone_number_verification_usecase.dart b/lib/auth/domain/usecases/phone_number_verification_usecase.dart index c8c6c47..d171bb0 100644 --- a/lib/auth/domain/usecases/phone_number_verification_usecase.dart +++ b/lib/auth/domain/usecases/phone_number_verification_usecase.dart @@ -1,9 +1,11 @@ import 'dart:developer'; import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:code_l/main.dart'; import 'package:firebase_auth/firebase_auth.dart'; import '../model/identity/phone_number.dart'; +import '../model/sign_up_state.dart'; import '../repositories/phone_number_repository.dart'; class PhoneNumberVerificationUseCase { @@ -15,6 +17,7 @@ class PhoneNumberVerificationUseCase { PhoneNumber phoneNumber, Function(String) onCodeSent, ) async { + log(name: 'PhoneNumberVerificationUseCase : sendCode', '핸드폰 번호 : ${phoneNumber.value}'); await _authRepository.verifyPhoneNumber(phoneNumber.value, onCodeSent); } @@ -36,6 +39,7 @@ class PhoneNumberVerificationUseCase { Future saveUser() async { final user = FirebaseAuth.instance.currentUser; + log(name: 'PhoneNumberVerificationUseCase : saveUser', 'user phone : ${user}' ); if (user != null) { final uid = user.uid; final phone = PhoneNumber.fromInternational(user.phoneNumber!); @@ -44,10 +48,13 @@ class PhoneNumberVerificationUseCase { .collection('users') .doc(uid) .set({ - 'phoneNumber': phone, + 'phoneNumber': phone.value, 'createdAt': FieldValue.serverTimestamp(), }); log(name: 'PhoneNumberVerificationUseCase : saveUser', 'firestore에 사용자 정보 저장 완료'); + + saveSignUpState(SignUpState.IDENTIFY); + log(name: 'PhoneNumberVerificationUseCase : saveUser', '전화번호 인증 완료'); } else { log(name: 'PhoneNumberVerificationUseCase : saveUser', 'firestore에 사용자 정보 저장 실패'); } diff --git a/lib/auth/presentation/pages/congratulation/congratulation_page.dart b/lib/auth/presentation/pages/congratulation/congratulation_page.dart index a31ea27..33d4955 100644 --- a/lib/auth/presentation/pages/congratulation/congratulation_page.dart +++ b/lib/auth/presentation/pages/congratulation/congratulation_page.dart @@ -1,4 +1,5 @@ import 'package:code_l/auth/presentation/pages/congratulation/widgets/congratulation_app_bar.dart'; +import 'package:code_l/sign_up/presentation/pages/profile_name/profile_name_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import '../../../../core/utills/design/app_colors.dart'; @@ -13,11 +14,11 @@ class CongratulationPage extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: const CongratulationAppBar(), - body: _buildContentField(), + body: _buildContentField(context), ); } - Widget _buildContentField() { + Widget _buildContentField(BuildContext context) { return SafeArea( child: Padding( padding: const EdgeInsets.symmetric(horizontal: AppGaps.gap20), @@ -105,7 +106,14 @@ class CongratulationPage extends StatelessWidget { child: CongratulationConfirmButton( enabled: true, text: "코드 프로필 작성하기", - onPressed: () {}, + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const ProfileNamePage(), + ), + ); + }, ), ), ], diff --git a/lib/auth/presentation/pages/identity/identity_verification_page.dart b/lib/auth/presentation/pages/identity/identity_verification_page.dart index 7e83b13..7b34cdf 100644 --- a/lib/auth/presentation/pages/identity/identity_verification_page.dart +++ b/lib/auth/presentation/pages/identity/identity_verification_page.dart @@ -3,10 +3,11 @@ import 'package:code_l/auth/presentation/pages/identity/widgets/identity_verific import 'package:code_l/auth/presentation/widgets/auth_confirm_button.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../../core/utills/design/app_colors.dart'; import '../../../../core/utills/design/app_gaps.dart'; import '../../../../core/utills/design/app_typography.dart'; -import '../login/login_page.dart'; +import '../terms_and_conditions/terms_and_condition_page.dart'; class PhoneVerificationPage extends ConsumerWidget { const PhoneVerificationPage({Key? key}) : super(key: key); @@ -27,7 +28,7 @@ class PhoneVerificationPage extends ConsumerWidget { viewModel.verifyCode(() { Navigator.pushReplacement( context, - MaterialPageRoute(builder: (context) => const LoginPage()), + MaterialPageRoute(builder: (context) => const TermsAndConditionPage()), ); }); } diff --git a/lib/auth/presentation/pages/login/login_viewmodel.dart b/lib/auth/presentation/pages/login/login_viewmodel.dart index 4ca3fa0..0ce5917 100644 --- a/lib/auth/presentation/pages/login/login_viewmodel.dart +++ b/lib/auth/presentation/pages/login/login_viewmodel.dart @@ -1,9 +1,9 @@ import 'dart:developer'; +import 'package:code_l/auth/data/datasources/local/login_local_datasource.dart'; import 'package:code_l/auth/domain/model/login_type.dart'; import 'package:flutter/cupertino.dart'; import 'package:kakao_flutter_sdk_user/kakao_flutter_sdk_user.dart'; -import 'package:sign_in_with_apple/sign_in_with_apple.dart'; import '../../../domain/usecases/login_usecase.dart'; @@ -14,55 +14,48 @@ class LoginViewModel extends ChangeNotifier { Future loginWithKakao() async { try { - bool installed = await isKakaoTalkInstalled(); + User user = await useCase.callKakaoLogin(LoginType.kakao); - OAuthToken token = - installed - ? await UserApi.instance.loginWithKakaoTalk() - : await UserApi.instance.loginWithKakaoAccount(); - - final user = await UserApi.instance.me(); - - log(name: 'LoginViewModel::login', 'success: $token'); - await loginOauth(LoginType.kakao, user.id.toString()); + log(name: 'LoginViewModel::login', 'success: user id = ${user.id}, email = ${user.kakaoAccount?.email}'); + await loginOauth(LoginType.kakao, user.id.toString(), user.kakaoAccount?.email ?? ''); } catch (e) { log(name: 'LoginViewModel::login', 'error: $e'); } } - Future loginOauth(LoginType type, String id) async { - try { - final jwt = await useCase.callOauth(type, id); - - log(name: 'LoginViewModel::loginOauth', 'success: $jwt'); - } catch (e) { - log(name: 'LoginViewModel::loginOauth', 'error: $e'); - } - } - Future loginWithApple() async { try { // 애플 인증 정보 가져오기 - final credential = useCase.callAppleLogin(LoginType.apple); + final credential = await useCase.callAppleLogin(LoginType.apple); // authorizationCode와 identityToken 확인 - final authorizationCode = credential.then( - (value) => value.authorizationCode, - ); - final identityToken = credential.then((value) => value.identityToken); + final authorizationCode = credential.authorizationCode; + final identityToken = credential.userIdentifier; + final email = credential.email; if (authorizationCode == null || identityToken == null) { throw Exception("Authorization Code 또는 Identity Token을 가져오지 못했습니다."); } // 백엔드로 OAuth 인증 요청 - await loginOauth(LoginType.apple, identityToken.toString()); + await loginOauth(LoginType.apple, identityToken.toString(),email.toString()); log( name: 'LoginViewModel::loginWithApple', - 'success: ${identityToken.toString()}', + 'success: $identityToken', ); } catch (e) { log(name: 'LoginViewModel::loginWithApple', 'error: $e'); } } + + Future loginOauth(LoginType type, String id, String email) async { + try { + final jwt = await useCase.callOauth(type, id, email); + + await LoginLocalDataSource.saveToken(jwt); + log(name: 'LoginViewModel::loginOauth', 'success: $jwt'); + } catch (e) { + log(name: 'LoginViewModel::loginOauth', 'error: $e'); + } + } } diff --git a/lib/core/utills/network/dio_provider.dart b/lib/core/utills/network/dio_provider.dart index 5c9a8b3..2c5cf33 100644 --- a/lib/core/utills/network/dio_provider.dart +++ b/lib/core/utills/network/dio_provider.dart @@ -1,9 +1,10 @@ +import 'package:code_l/auth/data/datasources/local/login_local_datasource.dart'; import 'package:dio/dio.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; final dioProvider = Provider((ref) { - return Dio( + final dio = Dio( BaseOptions( baseUrl: dotenv.env['BASE_URL'] ?? '', connectTimeout: const Duration(seconds: 5), @@ -11,4 +12,21 @@ final dioProvider = Provider((ref) { headers: {'Content-Type': 'application/json'}, ), ); + + dio.interceptors.add(InterceptorsWrapper( + onRequest: (options, handler) async { + final token = await LoginLocalDataSource.loadToken(); + + if (token != null && token.isNotEmpty) { + options.headers['Authorization'] = 'Bearer $token'; + } + + return handler.next(options); + }, + onError: (e, handler) { + return handler.next(e); + }, + )); + + return dio; }); diff --git a/lib/main.dart b/lib/main.dart index 975646d..b2ec713 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,8 @@ +import 'package:code_l/auth/presentation/pages/identity/identity_verification_page.dart'; import 'package:code_l/auth/presentation/pages/login/login_page.dart'; import 'package:code_l/auth/presentation/pages/terms_and_conditions/terms_and_condition_page.dart'; import 'package:code_l/sign_up/presentation/pages/pending_approval/pending_approval_page.dart'; +import 'package:code_l/sign_up/presentation/pages/profile_image/profile_image_page.dart'; import 'package:code_l/sign_up/presentation/pages/profile_interest/profile_intereset_page.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; @@ -8,6 +10,9 @@ import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:kakao_flutter_sdk_user/kakao_flutter_sdk_user.dart'; import 'package:screen_protector/screen_protector.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'auth/domain/model/sign_up_state.dart'; main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -28,7 +33,7 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( title: 'Kakao Login Demo', - home: const PendingApprovalPage(), + home: const InitialRouteLoader(), theme: ThemeData( primarySwatch: Colors.blue, scaffoldBackgroundColor: Colors.white, @@ -36,3 +41,43 @@ class MyApp extends StatelessWidget { ); } } + +// todo : 패키지,아키텍처 리펙토링 +Future saveSignUpState(SignUpState state) async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setString('sign_up_state', state.name); +} + +Future loadSignUpState() async { + final prefs = await SharedPreferences.getInstance(); + final value = prefs.getString('sign_up_state'); + return SignUpState.fromString(value); +} + +class InitialRouteLoader extends StatelessWidget { + const InitialRouteLoader({super.key}); + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: loadSignUpState(), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return const Scaffold( + body: Center(child: CircularProgressIndicator()), + ); + } + + return switch (snapshot.data!) { + SignUpState.SIGNUP => const PhoneVerificationPage(), + SignUpState.IDENTIFY => const TermsAndConditionPage(), + SignUpState.CODE_SURVEY => const ProfileInterestPage(), + SignUpState.CODE_PROFILE_IMAGE => const ProfileImagePage(), + SignUpState.PENDING => const PendingApprovalPage(), + SignUpState.DONE => const LoginPage(), // todo home + SignUpState.NONE => const LoginPage(), + }; + }, + ); + } +} From 2dbbc435c32d532fc874f6abcc4b2aebde7fa2d5 Mon Sep 17 00:00:00 2001 From: kkosang Date: Tue, 6 May 2025 20:24:22 +0900 Subject: [PATCH 3/3] =?UTF-8?q?refactor:=20=EC=95=A0=ED=94=8C=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EC=8B=9D=EB=B3=84=EC=9E=90=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/auth/presentation/pages/login/login_viewmodel.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/auth/presentation/pages/login/login_viewmodel.dart b/lib/auth/presentation/pages/login/login_viewmodel.dart index 0ce5917..ca31f9e 100644 --- a/lib/auth/presentation/pages/login/login_viewmodel.dart +++ b/lib/auth/presentation/pages/login/login_viewmodel.dart @@ -29,19 +29,19 @@ class LoginViewModel extends ChangeNotifier { final credential = await useCase.callAppleLogin(LoginType.apple); // authorizationCode와 identityToken 확인 final authorizationCode = credential.authorizationCode; - final identityToken = credential.userIdentifier; + final id = credential.userIdentifier; final email = credential.email; - if (authorizationCode == null || identityToken == null) { + if (authorizationCode == null || id == null) { throw Exception("Authorization Code 또는 Identity Token을 가져오지 못했습니다."); } // 백엔드로 OAuth 인증 요청 - await loginOauth(LoginType.apple, identityToken.toString(),email.toString()); + await loginOauth(LoginType.apple, id.toString(),email.toString()); log( name: 'LoginViewModel::loginWithApple', - 'success: $identityToken', + 'success: $id', ); } catch (e) { log(name: 'LoginViewModel::loginWithApple', 'error: $e');