문의 시 사용하시는 SDK 버전 정보와 플랫폼(Android / iOS) 및 디벨로퍼스 앱ID를 알려주세요.
플러터 기반 파이어 베이스 oidc 카카오 로그인을 구현했는데 잘 동작을 했었는데 android 패키지명을 바꾸고 나서는 'log in to 앱이름 with this kakao account 에서 continue를 눌러도 반응이 없어요. 파이어 베이스에 네이티브 앱키와 client secret 키도 다 넣었거든요. ios에서는 작동을 잘 하고, e-mail 로그인도 잘 작동을 하거든요. 어디에 문제가 있을까요?
안녕하세요.
확인을 위해 앱 ID 부탁드립니다.
앱ID
https://developers.kakao.com/app에 표시되는 ID 값 입니다.
숫자로된 ID 입니다
ex) 123456
1224324 입니다
파이어 베이스 oidc 는 REST-API 방식 로그인아닌가요?
말씀하신 내용과 사용하신 방식은 네이티브SDK방식인데요?
위 내용과 무관하게 작동하지 않는 이유는 인가코드요청 후 되돌아갈 커스텀 스킴 설정이 잘못되었을 것으로 추정됩니다.
확인해보시겠어요?
androidmanifest.xml 파일입니다.
<!-- 카카오 로그인 커스텀 URL 스킴 설정 -->
<activity
android:name="com.kakao.sdk.flutter.AuthCodeCustomTabsActivity"
android:exported="true">
<intent-filter android:label="flutter_web_auth">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- "kakao${YOUR_NATIVE_APP_KEY}://oauth" 형식의 앱 실행 스킴 설정 -->
<!-- 카카오 로그인 Redirect URI -->
<data android:scheme="kakaob1d0f2c5937624cb80581652a7bdc9c4" android:host="oauth"/>
</intent-filter>
</activity>
<meta-data
android:name="com.kakao.sdk.AppKey"
android:value="b1d0f2c5937624cb80581652a7bdc9c4" />
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-4176602818093200~4201007376"/>
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
</queries>
androidmanifest.xml 에는 특별한점이 없네요.
SDK init 구문도 공유부탁드려요.
main 함수 입니다
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter_naver_map/flutter_naver_map.dart';
import 'package:kakao_flutter_sdk_user/kakao_flutter_sdk_user.dart';
import 'package:parking_lot_help/naver_map_screen.dart';
import 'package:get/get.dart';
import 'firebase_options.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:app_tracking_transparency/app_tracking_transparency.dart';
import 'package:new_version_plus/new_version_plus.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
KakaoSdk.init(
nativeAppKey: 'b1d0f2c5937624cb80581652a7bdc9c4',
javaScriptAppKey: 'fbfaccd738e584997b5e2c3bb6ab3a6a',
);
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
await FlutterNaverMap().init(
clientId: "uqhgnmkjna",
onAuthFailed: (ex) {
print("********* 네이버맵 인증오류 : $ex *********");
});
unawaited(MobileAds.instance.initialize());
await AppTrackingTransparency.requestTrackingAuthorization();
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
// 앱이 시작되자마자 업데이트 확인 함수를 호출합니다.
_checkVersion();
}
void _checkVersion() async {
final newVersion = NewVersionPlus(
// iOS 앱의 App Store ID를 입력합니다. (Apple Developer 계정에서 확인)
// 예시: iOSId: 'com.google.Vespa',
iOSId: '6471499022',
// 안드로이드 앱의 Play Store 패키지 이름을 입력합니다. (build.gradle에서 확인)
// 예시: androidId: 'com.google.android.apps.maps',
androidId: 'com.msh.parkinglothelp',
// 본인 앱의 패키지 이름으로 변경하세요.
iOSAppStoreCountry: 'KR',
androidPlayStoreCountry: 'KR'
);
// 잠시 딜레이를 주어 앱의 다른 초기화 작업들이 안정적으로 끝날 시간을 줍니다. (선택 사항)
await Future.delayed(const Duration(milliseconds: 1500));
try {
// getVersionStatus()를 호출하여 스토어의 최신 버전 정보를 가져옵니다.
final status = await newVersion.getVersionStatus();
// status가 null이 아니고, canUpdate가 true일 경우 (즉, 업데이트가 필요할 경우)
if (status != null && status.canUpdate) {
// 사용자에게 업데이트를 알리는 다이얼로그를 띄웁니다.
newVersion.showUpdateDialog(
context: context,
versionStatus: status,
dialogTitle: '🎉 새로운 업데이트', // 다이얼로그 제목
dialogText: '더욱 향상된 기능을 만나보세요!\n새로운 버전 ${status.storeVersion}이 출시되었습니다.', // 다이얼로그 내용
updateButtonText: '지금 업데이트', // 업데이트 버튼 텍스트
dismissButtonText: '나중에 할게요', // 닫기 버튼 텍스트
allowDismissal: true, // 사용자가 다이얼로그를 닫을 수 있도록 허용
);
}
} catch (e) {
// 버전 확인 중 에러가 발생할 경우 로그를 출력합니다.
print('업데이트 버전 확인 중 오류 발생: $e');
}
}
@override
Widget build(BuildContext context) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
title: 'parking_lot_help',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: NaverMapScreen()
);
}
}
javaScriptAppKey 도 설정하셨네요? 로그인 시, 어떤 함수 사용하시나요?
카카오 로그인 관련 코드블러 모두 공유 부탁드려요.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:kakao_flutter_sdk_user/kakao_flutter_sdk_user.dart';
import 'package:get/get.dart';
import 'package:parking_lot_help/naver_map_screen.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:parking_lot_help/const.dart';
class KakaoLogin extends StatefulWidget {
const KakaoLogin({super.key});
@override
State<KakaoLogin> createState() => _KakaoLoginState();
}
class _KakaoLoginState extends State<KakaoLogin> {
CollectionReference userRef = FirebaseFirestore.instance.collection('users');
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () async {
await _signInWithKakao();
if (FirebaseAuth.instance.currentUser != null) {
userRef.doc(FirebaseAuth.instance.currentUser!.uid).get().then((
doc,
) async {
if (!doc.exists) {
await userRef.doc(FirebaseAuth.instance.currentUser!.uid).set({
'userId': FirebaseAuth.instance.currentUser!.uid,
'pointMoney': SEEDMONEYPOINT,
'giveHelpNum': 0,
'getHelpNum': 0,
'giveRewardNum': 0,
'getRewardNum': 0,
'rewardRate': 101,
'waitingPointToMoney': 0,
});
print('*************새로운 문서를 생성했습니다************');
} else {
print('*************이미 문서가 존재합니다.**************');
}
});
}
Get.offAll(() => NaverMapScreen());
},
child: Card(
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(7)),
child: Image.asset(
'asset/image/kakao_login_large_wide.png',
height: 60,
)
),
);
}
Future<void> _signInWithKakao() async {
// 카카오 로그인 구현 예제
// 카카오톡 실행 가능 여부 확인
// 카카오톡 실행이 가능하면 카카오톡으로 로그인, 아니면 카카오계정으로 로그인
if (await isKakaoTalkInstalled()) {
try {
OAuthToken token = await UserApi.instance.loginWithKakaoTalk();
var provider = OAuthProvider('oidc.parking_lot_help');
var credential = provider.credential(
idToken: token.idToken,
accessToken: token.accessToken,
);
await FirebaseAuth.instance.signInWithCredential(credential);
print('카카오톡으로 로그인 성공');
} catch (error) {
print('카카오톡으로 로그인 실패 $error');
// 사용자가 카카오톡 설치 후 디바이스 권한 요청 화면에서 로그인을 취소한 경우,
// 의도적인 로그인 취소로 보고 카카오계정으로 로그인 시도 없이 로그인 취소로 처리 (예: 뒤로 가기)
if (error is PlatformException && error.code == 'CANCELED') {
return;
}
// 카카오톡에 연결된 카카오계정이 없는 경우, 카카오계정으로 로그인
try {
OAuthToken token = await UserApi.instance.loginWithKakaoAccount();
var provider = OAuthProvider('oidc.parking_lot_help');
var credential = provider.credential(
idToken: token.idToken,
accessToken: token.accessToken,
);
await FirebaseAuth.instance.signInWithCredential(credential);
print('카카오계정으로 로그인 성공');
} catch (error) {
print('카카오계정으로 로그인 실패 $error');
}
}
} else {
try {
OAuthToken token = await UserApi.instance.loginWithKakaoAccount();
var provider = OAuthProvider('oidc.parking_lot_help');
var credential = provider.credential(
idToken: token.idToken,
accessToken: token.accessToken,
);
await FirebaseAuth.instance.signInWithCredential(credential);
print('카카오계정으로 로그인 성공');
} catch (error) {
print('카카오계정으로 로그인 실패 $error');
}
}
}
}
실행하신 환경은 어떻게 되나요?
안드로이드 스튜디오에서 안드로이드 시뮬레이터에서요
여기 로그인 요청 전후 로그 찍어보시겠어요?
D/EGL_emulation(19333): app_time_stats: avg=11.84ms min=2.70ms max=27.10ms count=58
[GETX] GOING TO ROUTE /LogInScreen
[GETX] REMOVING ROUTE /
W/WindowOnBackDispatcher(19333): OnBackInvokedCallback is not enabled for the application.
W/WindowOnBackDispatcher(19333): Set 'android:enableOnBackInvokedCallback="true"' in the application manifest.
D/EGL_emulation(19333): app_time_stats: avg=18.33ms min=11.79ms max=62.63ms count=55
E/Surface (19333): freeAllBuffers: 1 buffers were freed while being dequeued!
E/Surface (19333): getSlotFromBufferLocked: unknown buffer: 0x0
D/EGL_emulation(19333): app_time_stats: avg=104.57ms min=5.81ms max=1338.65ms count=14
D/ProfileInstaller(19333): Installing profile for com.msh.parkinglothelp
D/EGL_emulation(19333): app_time_stats: avg=35.46ms min=1.87ms max=648.22ms count=23
D/CustomTabsCommonClient(19333): Choosing com.android.chrome as custom tabs browser
E/OpenGLRenderer(19333): Unable to match the desired swap behavior.
그리고 자꾸 이 에러가 떠요
throw MissingPluginException(‘No implementation found for method $method on channel $name’);
네. 공유하신 코드는 모두 정상이고 로그로 원인을 분석할 수 없고
말씀하신 증상은 카카오계정로그인으로 진입 후 앱을 재호출 하지 못 하는 증상으로 앞서 공유하신 코드상 정상이므로 원인 파악 불가합니다.
빈프로젝트에 카카오로그인만 적용하셔서 비교 테스트 해보시면 좋을 것 같습니다.
제가 이리 저리 많이 해 봤는데 안 됐거든요. 죄송한데 혹시 판교 카카오 개발자 사무실로 맥북을 들고 방문하면 봐 주시기도 하시나요?
카카오로그인 외 다른부분에 영향 있을 것으로 추정됩니다.
카카오로그인만 단독 정상 작동하도록 구현하셔서 비교해보시면 좋을 것 같습니다.
아쉽지만 별도 디버깅은 해드리지 않고 있습니다.