2026/03/19
오늘의 이야기
#스치니1000프로젝트 #재미 #행운기원 #Compose #Firebase
🎯 야 너 토요일마다 로또 확인하냐?
나도 맨날 "혹시나~" 하면서 봤거든 ㅋㅋ
근데 이제는 그냥 안 해
AI한테 맡겼어 🤖✨
그것도 구글 Gemini로다가!
그래서 앱 하나 만들었지
👉 "로또 예상번호 by Gemini" 🎱
AI가 분석해서 번호 딱! 뽑아줌
그냥 보고 참고만 하면 됨
재미로 해도 좋고…
혹시 모르는 거잖아? 😏
https://play.google.com/store/apps/details?id=com.billcorea.gptlotto1127
오늘의 이야기
대전의 2023년 5월 18일 날씨
2023년 5월 18일 목요일 대전의 날씨에 대한 자세한 정보를 제공합니다.
대전 날씨 요약: 대전의 날씨는 목요일에 흐리고 비가 올 것으로 예상됩니다. 최고 기온은 화씨 68도, 최저 기온은 화씨 63도입니다. 바람은 시속 6~8마일로 남동쪽에서 불 것입니다.
하루 종일 날씨: 아침에는 흐린 하늘과 가벼운 비가 내릴 것으로 예상됩니다. 최고 기온은 화씨 68도입니다. 오후에는 여전히 흐린 하늘과 가끔 비가 내릴 것으로 예상됩니다. 최저 기온은 화씨 63도입니다.
아침 의상: 아침에는 흐린 하늘과 가벼운 비가 내릴 것으로 예상되므로 우산을 가져오는 것이 좋습니다. 편안한 옷을 입고 따뜻하게 유지하십시오.
낮 의상: 오후에는 여전히 흐린 하늘과 가끔 비가 내릴 것으로 예상되므로 우산을 가져오는 것이 좋습니다. 편안한 옷을 입고 따뜻하게 유지하십시오.
저녁 의상: 저녁에는 흐린 하늘과 가끔 비가 내릴 것으로 예상되므로 우산을 가져오는 것이 좋습니다. 편안한 옷을 입고 따뜻하게 유지하십시오.
대전에서 즐거운 시간 보내시기 바랍니다!
이상은 구글 bard가 말해 주는 이야기입니다.

오늘의 이야기
#스하리1000명프로젝트
스치니들!
내가 만든 이 앱은, 내 폰에 오는 알림 중에서 중요한 키워드가 있는 경우
등록해둔 친구에게 자동으로 전달해주는 앱이야 📲
예를 들어, 카드 결제 알림을 와이프나 자녀에게 보내주거나
이번 달 지출을 달력처럼 확인할 수도 있어!
앱을 함께 쓰려면 친구도 설치 & 로그인해줘야 해.
그래야 친구 목록에서 서로 선택할 수 있으니까~
서로 써보고 불편한 점 있으면 알려줘 🙏
👉 https://play.google.com/store/apps/details?id=com.nari.notify2kakao
오늘의 이야기
PyCharm 2021.2 (Community Edition)에서 codeGPT을 활용해 코드 작성의 도움을 받아 볼 수 있습니다.

먼저 할 일은 Setting - plugin 에서 codeGPT 을 찾아서 설치를 해 보는 것입니다.

이미 설치 되었기 때문에 market에서 검색을 했을 때 installed라고 나오지만, 설치 전이라면 초록색으로 install이라고 나옵니다. install을 클릭해 설치를 해 봅니다.
오늘은 Flask 을 이용해서 간단한 데이터 조회하는 화면을 만들어 보겠습니다.
1. 설치가 되고 나면 pycham 오른쪽 상단에 codeGPT 탭이 보입니다. 해당 탭을 클릭해서 명령어 입력창이 나오게 한 다음, 잘 안 되는 영어로 질문을 해 봅니다.
flask use for firebase realtime database refence data list view
문법이 맞지 않는 엉터리 영어이기는 하지만, 알아 들었나 봅니다.

python 코드을 생성해 주었습니다.

data html 코드도 생성을 해 줍니다.

data.html 이 있기는 하지만 에러가 발생합니다. 해서 해당 에러 내용을 그대로 복사해서 물어보았습니다. data.html 파일의 경로가 문제인 듯합니다. templates라는 폴더를 생성하고 그 안에 data.html을 넣어야 한다고 친절하게 설며을 해 줍니다.
그래도 계속 되는 에러에 열받은(?) 저는 한글로 질문을 해 봅니다.

친절한(?) 설명이 계속됩니다.

여기까지의 설명을 듣고 난 저는 이해를 하였습니다. 그래서 생성된 코드는 다음과 같습니다. ㅋ~
from flask import Flask, render_template
import firebase_admin
from firebase_admin import credentials
from firebase_admin import db
# initialize the Flask app
app = Flask(__name__)
# initialize the Firebase app
cred = credentials.Certificate("./barcodevoucher.json")
firebase_admin.initialize_app(cred, {"databaseURL": "https://barcod........database.app/"})
# define a route to display the data
@app.route("/")
def view_data():
# get a reference to the data in Firebase Realtime Database
ref = db.reference("userInfo").child('4i0HjuGb......0MO1Rvpx2')
# retrieve the data as a dictionary
data = ref.get()
# pass the data to the template and render it
return render_template("data.html", data=data)
# run the Flask app
if __name__ == "__main__":
app.run(debug=True)firebase의 프로젝트 설정에서 받아온 barcode... json 파일과 realtime database의 URL을 입력해 주었고, 데어터를 읽어오기 위해서 uid 값을 넣어서 데이터를 가져올 수 있도록 하였습니다.

참고로 realtime database 의 규칙 설정에 위 그림과 같이 설정을 하고 있기 때문에 $uid을 확인하지 못하면 실제 데이터를 읽어 올 수 없습니다. 그래서 코드에는
ref = db.reference("userInfo").child('4i0HjuGb......0MO1Rvpx2') 와 같이 해서 child 안에 uid 값을 강제로 넣어 주게 됩니다.
<!DOCTYPE html>
<html>
<head>
<title>Data</title>
</head>
<body>
<h1>Data</h1>
<table>
<thead>
<tr>
<th>key</th>
<th>user Name</th>
<th>Tel No</th>
</tr>
</thead>
<tbody>
{% for key in data %}
<tr>
<td>{{ data[key]['key'] }}</td>
<td>{{ data[key]['userName'] }}</td>
<td>{{ data[key]['telNo'] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
다음은 data.html 의 코드 구성을 codeGPT 가 알려준 방식으로 수정해 주었습니다.
짜~잔... 이제 실행된 모습을 살펴보겠습니다.

모양은 허접해 보입니다만, 일단, 오늘의 목표는 달성 입니다. 이렇게 하나둘씩 코드 작업을 배우다 보면 어느 날 문득 그런 생각이 들 것 같습니다.
단순 코딩을 하는 코더는 앞으로 할일이 없어질 거라는... 요즘 읽고 있는 책에서 말하는 것과 같이 사라질 직업 중에 하나가 개발자가 될 것 같은 생각이 드는 시간입니다.
** 이것도 사용량에 따른 요금 부과가 발생할 지도 모릅니다. 참고하세요.
오늘의 이야기
Jetpack Compose는 개발자가 아름답고 반응이 빠르고 효율적인 사용자 인터페이스를 쉽게 빌드할 수 있는 최신 Android UI 도구 키트입니다. Android 개발의 일반적인 작업 중 하나는 뒤로 버튼 누름 이벤트를 처리하는 것입니다.
이 블로그 게시물에서는 Jetpack Compose에서 뒤로 버튼 누르기 이벤트를 처리하는 방법을 살펴보겠습니다.
Android에서 뒤로 버튼은 거의 모든 Android 기기에 있는 하드웨어 버튼입니다.
뒤로 버튼은 앱의 이전 화면이나 활동으로 돌아가는 데 사용됩니다. 뒤로 버튼 누르기 이벤트는 기본적으로 Android 시스템에서 처리하지만 때때로 이 동작을 재정의하고 뒤로 버튼 누르기 이벤트를 직접 처리하려고 합니다.
Jetpack Compose에서는 속성을 사용하여 뒤로 버튼 누름 이벤트를 처리할 수 있습니다 onBackPressedDispatcher. 이 onBackPressedDispatcher속성은 뒤로 버튼 누름 이벤트를 처리하기 위해 개체를 OnBackPressedDispatcherOwner등록할 수 있는 인터페이스입니다
OnBackPressedCallback.
onBackPressedDispatcher속성을 사용하여 Jetpack Compose에서 뒤로 버튼 누름 이벤트를 처리하는 방법을 살펴보겠습니다 .
import androidx.activity.OnBackPressedCallback
import androidx.activity.OnBackPressedDispatcherOwner
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
@Composable
fun BackPressHandler(
enabled: Boolean = true,
onBackPressed: () -> Unit
) {
val backPressedDispatcher = (LocalContext.current as? OnBackPressedDispatcherOwner)?.onBackPressedDispatcher
val callback = remember {
object : OnBackPressedCallback(enabled) {
override fun handleOnBackPressed() {
onBackPressed()
}
}
}
DisposableEffect(callback) {
backPressedDispatcher?.addCallback(callback)
onDispose {
callback.remove()
}
}
}
위의 코드에서 이름이 구성 가능한 함수를 만들었습니다 BackPressHandler. 이 함수는 두 개의 매개변수를 사용합니다.
- enabled: 뒤로 버튼 누름 이벤트 처리 여부를 지정하는 부울 값입니다. 기본적으로 로 설정되어 있습니다 true.
- onBackPressed: 뒤로 버튼을 눌렀을 때 호출되는 람다 함수입니다.
함수에서 먼저 속성 에서 개체를 BackPressHandler가져옵니다 . 그런 다음 함수를 사용하여 개체를 만듭니다 . 객체 는 뒤로 버튼을 누를 때 호출되는 함수를 재정의합니다 . 함수 에서 뒤로 버튼 누름 이벤트를 처리하기 위해 람다 함수를 호출합니다
. onBackPressedDispatcherLocalContext.currentOnBackPressedCallbac krememberOnBackPressedCallbackhandleOnBackPressedhandleOnBackPr essedonBackPressed
그런 다음 함수를 사용하여 개체에 개체를 DisposableEffect등록 및 등록 취소합니다 . 개체가 생성 되면 메서드를 사용하여 개체 에 추가합니다. 컴포저블이 삭제되면 메서드를 사용하여 객체에서 객체를 제거합니다
. OnBackPressedCallbackonBackPressedDispatchercallbackonBackPress edDispatcheraddCallbackBackPressHandlercallbackonBackPressedDis patcherremove
컴포저블을 사용하려면 BackPressHandlerJetpack Compose 계층 구조에서 간단히 호출하고 onBackPressed람다 함수를 전달하여 뒤로 버튼 누르기 이벤트를 처리하면 됩니다.
@Composable
fun MyScreen() {
BackPressHandler(onBackPressed = { /* Handle back button press event */ }) {
// Screen content
}
}MyScreen. 은 BackPressHandler위의 코드에서는 컴포저블 내부에서 컴포저블을 호출했습니다
화면의 내용은 BackPressHandler onBackPressed뒤로 버튼 누름 이벤트를 처리하기 위해 람다 함수를 전달했습니다.
이렇게 오늘도 글 하나를 open GPT을 활용해서 작성해 보았습니다. 직역을 하고 있어서 말의 앞 뒤가 좀 애매한 구석이 없지 않지만, 나름 기술적인 부분에 대해서는 구글 검색을 하는 것만큼이나 기술해 주고 있습니다.
이제 이 코드를 실전에 적용해 보도록 하겠습니다. 도전해 볼 필요가 있는 것 같습니다.
아래는 참고 글 하나 찾아보았습니다.
https://medium.com/better-programming/back-press-handling-in-android-jetpack-compose-42d9ed402d40
Back Press Handling in Android Jetpack Compose
Create a handler function to customize your back press events
betterprogramming.pub
오늘의 이야기
#스하리1000명프로젝트,
有时候和外劳说话很难,对吧?
我制作了一个简单的应用程序,可以帮助您!你用你的语言写作,其他人用他们的语言看到它。
它根据设置自动翻译。
超级方便,可以轻松聊天。有机会就来看看吧!
https://play.google.com/store/apps/details?id=com.billcoreatech.multichat416
오늘의 이야기
안드로이드 gradle 이 8.0.x로 넘어가면서 발생한 이슈에 대한 이야기를 적어 두고자 합니다.
앱을 구현하는 동안에 firebase의 사용자 인증을 사용하고자 했으나, 이것이 인증 오류를 발생시키게 됩니다.
debug에서는 문제가 없었으나, release를 하는 과정에서만 오류를 발생시켜 애를 태우더군요. ㅠㅠ;;
Fatal Exception: com.google.firebase.FirebaseException: An internal error has occurred. [ Instantiation of JsonResponse failed! class com.google.android.gms.internal.firebase-auth-api.zzok ]
at co m.google.firebase.auth.api.internal.zzem.zza(com.google.firebase:firebase-auth@@20.0.0:29)
at co m.google.firebase.auth.api.internal.zzfx.zza(com.google.firebase:firebase-auth@@20.0.0:14)
at co m.google.firebase.auth.api.internal.zzfs.zza(com.google.firebase:firebase-auth@@20.0.0:44)
at co m.google.firebase.auth.api.internal.zzel.zza(com.google.firebase:firebase-auth@@20.0.0:10)
at co m.google.firebase.auth.api.internal.zzac.zza(com.google.firebase:firebase-auth@@20.0.0:2)
at co m.google.android.gms.common.util.DeviceProperties.zza(com.google.android.gms:play-services-basement@@17.1.1:131)
at co m.google.firebase.auth.api.internal.zzfn.zza(com.google.firebase:firebase-auth@@20.0.0:10)
at co m.google.firebase.auth.api.internal.zzep.zza(com.google.firebase:firebase-auth@@20.0.0:53)
Instantiation of JsonResponse failed! ...
말 그대로 응답으로 받는 JSON 결과를 분석할 수 없다는 에러를 발생 시킵니다.
먼저 gradle의 내용 중에 특별한 것을 보겠습니다.
buildTypes {
debug {
resValue("string", "ADMOB_APP_ID", localProperties.getProperty("ADMOB_APP_ID"))
resValue("string", "ADMOB_BANNER_ID", localProperties.getProperty("ADMOB_BANNER_ID"))
}
release {
resValue("string", "ADMOB_APP_ID", localProperties.getProperty("ADMOB_APP_ID"))
resValue("string", "ADMOB_BANNER_ID", localProperties.getProperty("ADMOB_BANNER_ID"))
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}debug 시에는 사용하지 않으나, release을 할 때 사용하게 되는 2가지가 있습니다. 코드 난독화와 압축을 이한 선택 사항입니다. playstore에 게시를 하고자 할 때 선택 해야 하는 요소들 중에 하나입니다.
이유는 앱의 크기를 조금이라도 줄여야 한다는 구글의 압박(?)이 있고, 난독화를 통해 앱을 디버깅하려는 분에게 피곤함(?)을 남겨 두기 위해서입니다.
그래도 proguard 설정을 통해서 난독화 등에서 제외시킬 수 있도록 하고 있기 때문에 꼭 필요한 부분은 제외 등록을 해서 처리를 하고 있기는 합니다.
아무튼 이번에 하고자 하는 이야기의 주된 목적은 압축을 해서 앱 크기를 줄이는 처리를 진행하는 동안에 release 된 앱이 오류를 발생시킨다는 것입니다. Instantiation of JsonResponse failed!...
https://github.com/firebase/firebase-android-sdk/issues/2124
Firebase Auth crash when R8 full mode obfuscation enabled · Issue #2124 · firebase/firebase-android-sdk
[READ] Step 1: Are you in the right place? Y [REQUIRED] Step 2: Describe your environment Android Studio version: 4.0 Firebase Component: Auth Component version: 20.0.0 [REQUIRED] Step 3: Describe ...
github.com
이미 많은 개발자들이 같은 증상에 대한 고민을 하고 있었더군요. 아무튼 그 덕분에 며칠을 허비하기는 했으나, 그 결론을 오늘에서야 내릴 수 있게 되었습니다.
조치 방안
먼저 progurad-rules.pro 파일에 아래와 같이 추가해 주었습니다.
-keep public class com.google.firebase.** {*;}
-keep class com.google.android.gms.internal.** {*;}
-keepclasseswithmembers class com.google.firebase.FirebaseException다음은 gradle.properties 파일에 추가해 주었습니다.
# firebase auth minifyEnabled = true 설정시 인증 오류 JSON 파싱 안되요...
android.enableR8.fullMode=true
그리고 project의 gradle 파일의 설정은 다음과 같이 수정해 주었습니다.
buildscript {
ext {
compose_version = '1.4.3'
raamcosta_version = '1.8.41-beta'
}
dependencies {
classpath 'com.google.gms:google-services:4.3.15'
classpath 'com.android.tools.build:gradle:8.0.1'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.5'
}
}// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '8.0.1' apply false
id 'com.android.library' version '8.0.1' apply false
id 'org.jetbrains.kotlin.android' version '1.8.10' apply false
}
gradle 버전이 8.0.1 이 현재 수준에서는 최신인 듯합니다. 그걸 사용하기 위해서 어느 날 문득 나타난 오류(?)와의 대면에서 며칠을 보냈습니다. ㅠㅠ;;
아무튼 이제 그 오류에서 해소되어 이렇게 정리된 글을 적어 두고자 합니다. 아닌가? ㅋ~

오늘도 즐~ 코딩 하세요.
오늘의 이야기
오늘은 이미지 자르기 (cropper)에 대한 이야기를 적어 보겠습니다. 해당 라이브러리의 출처는 다음과 같습니다.
https://github.com/CanHub/Android-Image-Cropper
GitHub - CanHub/Android-Image-Cropper: Image Cropping Library for Android, optimised for Camera / Gallery.
Image Cropping Library for Android, optimised for Camera / Gallery. - GitHub - CanHub/Android-Image-Cropper: Image Cropping Library for Android, optimised for Camera / Gallery.
github.com
2021년쯤에 이 방법을 java 코드에 연결해 보았던 기억이 있으나 그때 사용 되었던 github는 이제 더 이상 관리가 되지 않고 있었고 그걸 fork 해 계속 유지하고 있는 코드를 발견하게 되었습니다.
카메라 또는 갤러리에서 가져오는 이미지는 전체가 필요할 수 도 있지만, 문자 인식 등에 활용 하기 위해서는 필요한 부분만을 가져와야 하는 경우 등이 있습니다.
이 떄 유용하게 활용될 수 있습니다.
이미지 자르기 (cropper 기능)을 활용하는 방법은 먼저 gradle 파일에 다음과 같이 기술합니다.
// image cropper
implementation("com.vanniktech:android-image-cropper:4.5.0")현재는 4.5.0 이 마지막 버전입니다. 이 글을 보시는 시점에는 다시 확인해 보시는 것이 좋을 듯합니다.
이제 코드 구현을 해 보겠습니다. 먼저 callback 을 구현합니다. 호출된 이후에 결과가 돌아오면 그걸 현재는 viewModel에 있는 변수에 저장해 볼 예정입니다.
private val customCropImage = registerForActivityResult(CropImageContract()) {
if (it !is CropImage.CancelledResult) {
dataViewModel.textRecognitionUri.value = it.uriContent
Log.e("", "cropImage = ${it.uriContent}")
}
}
이 응답을 받기 위해서 아래와 같이 호출을 시도합니다.
private fun startCameraWithoutUri(includeCamera: Boolean, includeGallery: Boolean) {
customCropImage.launch(
CropImageContractOptions(
uri = null,
cropImageOptions = CropImageOptions(
imageSourceIncludeCamera = includeCamera,
imageSourceIncludeGallery = includeGallery,
),
),
)
}첫 번째 인수는 카메라 선택 가능 여부, 두 번째는 갤러리 선택 가능 여부입니다. 저는 두 개다 true로 실행을 했습니다.

이렇게 activity가 실행이 되게 되는 이때 꼭 해야 할 것은 다음과 같이 manifest.xml 파일에 해당 activite을 추가해 주어야 합니다.
<activity
android:name="com.canhub.cropper.CropImageActivity"
android:theme="@style/Base.Theme.AppCompat" />또한 갤러리와 카메라 선택 하는 문구가 영문으로 표출되는 것이 기본 값인데, 그걸 한글로 표시 되게 하기 위해서
values / strings.xml 에 다음과 같이 추가했습니다.
<string name="ic_rotate_left_24">반시계 회전</string>
<string name="ic_rotate_right_24">회전</string>
<string name="crop_image_menu_crop">자르기</string>
<string name="ic_flip_24">반전</string>
<string name="ic_flip_24_horizontally">좌우반전</string>
<string name="ic_flip_24_vertically">상하반전</string>
<string name="pick_image_chooser_title">이미지 선택</string>
<string name="pick_image_camera">카메라로 촬영하기</string>
<string name="pick_image_gallery">저장된 이미지 읽어오기</string>이런 문구들이 들어가므로 해서 화면에 한글로 표시가 진행됩니다.
이제 실행하고 그 결과를 보겠습니다.

당초에는 전체 촬영된 이미지가 보였으나, 안내선을 이용하여 조정한 후 화면 상단의 자르기 버튼을 선택하면 오른쪽의 그림과 같이 선택된 부분만 이미지로 전달을 받아 보여 줄 수 있었습니다.
위에서 선택된 결과를 viewModel에 있는 변수에 받았으니 그걸 그대로 Image에 넣어주는 것으로 그 코드를 정리가 되었습니다. 그 부분은 다음과 같습니다.
dataViewModel.textRecognitionUri.value?.let {
val source = ImageDecoder
.createSource(context.contentResolver, it)
bitmapPic.value = ImageDecoder.decodeBitmap(source)
bitmapPic.value?.let { btm ->
val quality = 20
val baos = ByteArrayOutputStream()
btm.compress(
Bitmap.CompressFormat.WEBP_LOSSLESS,
quality,
baos
)
val b: ByteArray = baos.toByteArray()
productImage = Base64.encodeToString(b, Base64.DEFAULT)
Image(
bitmap = btm.asImageBitmap(),
contentDescription = "profile",
contentScale = ContentScale.FillBounds,
modifier = Modifier
.clip(shape = RoundedCornerShape(16.dp))
.width(screenWidth * .8f)
.height(300.dp)
)
}
}uri로 받아온 응답을 활용해서 이미지로 변환해 바로 보여주는 방식으로 말입니다. Quality는 20으로 조정을 하기는 했으나, 그렇게 차이가 나는 것을 느낄 수 없었습니다.
이제 이미지 자르기는 그렇게 어렵지 않게 구현을 해 볼 수 있을 것 같습니다.
오늘의 이야기
#billcorea #운동동아리관리앱
🏸 Schneedle,羽毛球俱乐部必备应用!
👉 比洞赛 – 记录分数并寻找对手 🎉
适合任何地方,独自一人、与朋友一起或在俱乐部! 🤝
如果你喜欢羽毛球,一定要尝试一下
前往应用程序👉 https://play.google.com/store/apps/details?id=com.billcorea.matchplay
오늘의 이야기
Google Vision ML은 텍스트 인식, 이미지 레이블 지정, 개체 감지 및 얼굴 감지를 비롯한 다양한 컴퓨터 비전 기능을 제공하는 클라우드 기반 API입니다. Google Vision ML의 텍스트 인식 기능을 사용하면 이미지에서 텍스트를 감지하고 추출하여 기계가 읽을 수 있는 텍스트로 변환할 수 있습니다. 이 서비스는 다양한 언어를 지원하며 다양한 방향과 글꼴의 텍스트를 식별할 수 있습니다.
반면에 Firebase ML 텍스트 인식은 Android 및 iOS 애플리케이션을 위한 온디바이스 및 클라우드 기반 기계 학습 기능을 제공하는 모바일 SDK인 Firebase ML Kit의 일부입니다. Firebase ML Kit의 텍스트 인식 기능을 사용하면 이미지에서 텍스트를 감지 및 추출하여 기계가 읽을 수 있는 텍스트로 변환할 수 있습니다. Google Vision ML과 달리 Firebase ML Kit 텍스트 인식은 온디바이스 텍스트 인식을 제공합니다. 즉, 텍스트 인식은 네트워크 연결 없이 사용자의 디바이스에서 로컬로 수행됩니다.
Google Vision ML과 Firebase ML 텍스트 인식 모두 장점과 한계가 있습니다. Google Vision ML은 고급 텍스트 인식 기능을 제공하며 웹 및 데스크톱 애플리케이션을 비롯한 다양한 애플리케이션에서 사용할 수 있습니다. 그러나 텍스트 인식을 수행하려면 네트워크 연결이 필요하므로 경우에 따라 제한이 될 수 있습니다. 반면에 Firebase ML 텍스트 인식은 클라우드 기반 인식보다 더 빠르고 안정적인 온디바이스 텍스트 인식을 제공합니다. 그러나 언어 지원 및 글꼴 인식 측면에서 일부 제한이 있습니다.
요약하면 고급 텍스트 인식 기능이 필요하고 네트워크 연결 요구 사항에 신경 쓰지 않는다면 Google Vision ML을 사용할 수 있습니다. 온디바이스 텍스트 인식이 필요하고 고급 텍스트 인식 기능이 필요하지 않은 경우 Firebase ML 텍스트 인식을 사용할 수 있습니다.

오늘의 이야기
Exception java.lang.IllegalArgumentException: Given String is empty or null at com.google.android.gms.common.internal.Preconditions.checkNotEmpty (Preconditions.java) at com.google.firebase.auth.FirebaseAuth.signInWithEmailAndPassword (FirebaseAuth.java) at androidx.compose.foundation.gestures.TapGestureDetectorKt$detectTapAndPress$2$1.invokeSuspend (TapGestureDetector.kt) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (BaseContinuationImpl.java) at kotlinx.coroutines.DispatchedTaskKt.resume (DispatchedTask.kt) at kotlinx.coroutines.CancellableContinuationImpl.tryResume (CancellableContinuationImpl.java) at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume (CancellableContinuationImpl.java) at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl (CancellableContinuationImpl.java) at kotlinx.coroutines.CompletionStateKt.toState (CompletionState.kt) at kotlinx.coroutines.CancellableContinuationImpl.resumeWith (CancellableContinuationImpl.java) at androidx.compose.ui.input.pointer.SuspendingPointerInputFilter.dispatchPointerEvent (SuspendingPointerInputFilter.java) at androidx.compose.ui.input.pointer.SuspendingPointerInputFilter.onPointerEvent-H0pRuoY (SuspendingPointerInputFilter.java) at androidx.compose.ui.node.BackwardsCompatNode.onPointerEvent-H0pRuoY (BackwardsCompatNode.java) at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass (Node.java) at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass (Node.java) at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass (Node.java) at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass (Node.java) at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass (Node.java) at androidx.compose.ui.input.pointer.NodeParent.dispatchMainEventPass (NodeParent.java) at androidx.compose.ui.input.pointer.HitPathTracker.dispatchChanges (HitPathTracker.java) at androidx.compose.ui.input.pointer.PointerInputEventProcessor.process-BIzXfog (PointerInputEventProcessor.java) at androidx.compose.ui.platform.AndroidComposeView.sendMotionEvent-8iAsVTc (AndroidComposeView.java) at androidx.compose.ui.platform.AndroidComposeView.handleMotionEvent-8iAsVTc (AndroidComposeView.java) at androidx.compose.ui.platform.AndroidComposeView.dispatchTouchEvent (AndroidComposeView.java) at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:3030) at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2719) at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:3030) at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2719) at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:3030) at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2719) at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:3030) at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2719) at com.android.internal.policy.DecorView.superDispatchTouchEvent (DecorView.java:440) at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent (PhoneWindow.java:1830) at android.app.Activity.dispatchTouchEvent (Activity.java:3400) at com.android.internal.policy.DecorView.dispatchTouchEvent (DecorView.java:398) at android.view.View.dispatchPointerEvent (View.java:12752) at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent (ViewRootImpl.java:5106) at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess (ViewRootImpl.java:4909) at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:4426) at android.view.ViewRootImpl$InputStage.onDeliverToNext (ViewRootImpl.java:4479) at android.view.ViewRootImpl$InputStage.forward (ViewRootImpl.java:4445) at android.view.ViewRootImpl$AsyncInputStage.forward (ViewRootImpl.java:4585) at android.view.ViewRootImpl$InputStage.apply (ViewRootImpl.java:4453) at android.view.ViewRootImpl$AsyncInputStage.apply (ViewRootImpl.java:4642) at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:4426) at android.view.ViewRootImpl$InputStage.onDeliverToNext (ViewRootImpl.java:4479) at android.view.ViewRootImpl$InputStage.forward (ViewRootImpl.java:4445) at android.view.ViewRootImpl$InputStage.apply (ViewRootImpl.java:4453) at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:4426) at android.view.ViewRootImpl.deliverInputEvent (ViewRootImpl.java:7092) at android.view.ViewRootImpl.doProcessInputEvents (ViewRootImpl.java:7061) at android.view.ViewRootImpl.enqueueInputEvent (ViewRootImpl.java:7022) at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent (ViewRootImpl.java:7195) at android.view.InputEventReceiver.dispatchInputEvent (InputEventReceiver.java:186) at android.os.MessageQueue.nativePollOnce at android.os.MessageQueue.next (MessageQueue.java:326) at android.os.Looper.loop (Looper.java:160) at android.app.ActivityThread.main (ActivityThread.java:6669) at java.lang.reflect.Method.invoke at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493) at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:858)
구글 플레이 콘솔에 앱을 등록 하고 나서 받은 스택 오류 메시지... 길기도 합니다. 들여다 보는 것 마저 힘겨운 이 때
구원의 손길이 보입니다.
ㅋ~

이렇게 챗봇(GPT)과 함께 오늘도 하나의 오류를 찾아 봅니다.
끝.
오늘의 이야기
#스치니1000프로젝트 #재미 #행운기원 #Compose #Firebase 🎯 야 너 토요일마다 로또 확인하냐? 나도 맨날 "혹시나~" 하면서 봤거든 ㅋㅋ 근데 이제는 그냥 안 해 AI한테 맡겼어 🤖✨ 그것도 구글 Gemini로다...