From f5e3b39c7309470da771ef5cfd7eb7219a026a80 Mon Sep 17 00:00:00 2001 From: hansoo Date: Sun, 23 Mar 2025 22:17:42 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EC=9B=B9=EC=95=B1=20=EC=83=88=EB=A1=9C?= =?UTF-8?q?=EA=B3=A0=EC=B9=A8=20=ED=9B=84=20=EB=B2=84=EC=A0=84=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=9C=A0=EC=A7=80=20=EB=B0=8F=20BuildInfoPlugin=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/build.gradle | 6 +- .../lovable/zellyfinance/BuildInfoPlugin.java | 19 +- src/components/AppVersionInfo.tsx | 172 +++++++++++++++--- 3 files changed, 163 insertions(+), 34 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 6da7a71..bceefa3 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -22,10 +22,10 @@ android { applicationId "com.lovable.zellyfinance" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode versionCode - versionName versionName + versionCode = versionCode + versionName = versionName // 빌드 번호 추가 - BuildConfig 필드로 정의 - buildConfigField "int", "BUILD_NUMBER", buildNumber.toString() + buildConfigField("int", "BUILD_NUMBER", String.valueOf(buildNumber)) testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. diff --git a/android/app/src/main/java/com/lovable/zellyfinance/BuildInfoPlugin.java b/android/app/src/main/java/com/lovable/zellyfinance/BuildInfoPlugin.java index 0957128..cae8a4c 100644 --- a/android/app/src/main/java/com/lovable/zellyfinance/BuildInfoPlugin.java +++ b/android/app/src/main/java/com/lovable/zellyfinance/BuildInfoPlugin.java @@ -66,10 +66,21 @@ public class BuildInfoPlugin extends Plugin { } catch (Exception e) { // 오류 로깅 강화 Log.e(TAG, "빌드 정보 가져오기 오류", e); - JSObject errorObj = new JSObject(); - errorObj.put("message", e.getMessage()); - errorObj.put("stack", Log.getStackTraceString(e)); - call.reject("빌드 정보 가져오기 실패", errorObj); + + // 오류 발생 시에도 기본 정보 반환하여 앱 중단 방지 + JSObject fallbackResult = new JSObject(); + fallbackResult.put("versionName", "1.1.1.2"); // 버전명 기본값 + fallbackResult.put("versionCode", 6); // 버전 코드 기본값 + fallbackResult.put("buildNumber", 6); // 빌드 번호 기본값 + fallbackResult.put("packageName", getContext().getPackageName()); + fallbackResult.put("androidVersion", Build.VERSION.RELEASE); + fallbackResult.put("androidSDK", Build.VERSION.SDK_INT); + fallbackResult.put("platform", "android-fallback"); + fallbackResult.put("error", e.getMessage()); + fallbackResult.put("timestamp", System.currentTimeMillis()); + + Log.d(TAG, "오류 발생으로 기본값 반환: " + fallbackResult.toString()); + call.resolve(fallbackResult); // reject 대신 기본값으로 resolve } } } diff --git a/src/components/AppVersionInfo.tsx b/src/components/AppVersionInfo.tsx index 18d3240..95f0db3 100644 --- a/src/components/AppVersionInfo.tsx +++ b/src/components/AppVersionInfo.tsx @@ -1,8 +1,20 @@ - -import React, { useCallback, useEffect, useState, useRef } from 'react'; +import React, { useCallback, useEffect, useState, useRef, useMemo } from 'react'; import { getAppVersionInfo, isAndroidPlatform, isIOSPlatform } from '@/utils/platform'; import { Label } from '@/components/ui/label'; +// 버전 정보 인터페이스 정의 +interface VersionInfo { + versionName: string; + buildNumber: number; + versionCode?: number; + platform?: string; + pluginResponse?: string; + timestamp?: number; + error?: boolean; + errorMessage?: string; + defaultValues?: boolean; +} + interface AppVersionInfoProps { className?: string; showDevInfo?: boolean; @@ -14,16 +26,61 @@ const AppVersionInfo: React.FC = ({ showDevInfo = true, editable = false }) => { - const [versionInfo, setVersionInfo] = useState<{ - versionName: string; - buildNumber: number; - versionCode?: number; - platform?: string; - pluginResponse?: string; - }>({ - versionName: '1.0.1', - buildNumber: 3 - }); + // localStorage에서 이전에 저장된 버전 정보 가져오기 + const getSavedVersionInfo = useCallback((): VersionInfo | null => { + try { + const saved = localStorage.getItem('app_version_info'); + return saved ? JSON.parse(saved) : null; + } catch (e) { + console.error('저장된 버전 정보 파싱 오류:', e); + return null; + } + }, []); + + // 기본 버전 정보를 useMemo로 생성하여 불필요한 재계산 방지 + const defaultInfo = useMemo(() => { + // 웹 환경에서는 더 높은 버전 정보를 기본값으로 사용 + const webDefaultInfo: VersionInfo = { + versionName: '1.1.1.2', // 웹 환경에서 사용할 버전 이름 + buildNumber: 6, // 웹 환경에서 사용할 빌드 번호 + versionCode: 6, // 웹 환경에서 사용할 버전 코드 + platform: 'web', + defaultValues: false // 기본값이 아닌 것으로 설정하여 우선순위 높이기 + }; + + // localStorage에 저장된 정보 가져오기 + const savedInfo = getSavedVersionInfo(); + console.log('불러온 저장 정보:', savedInfo); + + // 웹 환경에서는 웹 기본값, 안드로이드/iOS에서는 기존 기본값 사용 + const baseDefault = (!isAndroidPlatform() && !isIOSPlatform()) + ? webDefaultInfo + : { + versionName: '1.0.0', + buildNumber: 1, + versionCode: 1, + platform: 'default', + defaultValues: true + }; + + // 저장된 정보가 있으면 우선 사용, 없으면 기본값 사용 + const result = savedInfo || baseDefault; + console.log('사용할 기본 정보:', result); + + // localStorage에 초기 정보 저장 (웹에서만) + if (!savedInfo && !isAndroidPlatform() && !isIOSPlatform()) { + try { + console.log('웹 기본 정보를 localStorage에 초기 저장'); + localStorage.setItem('app_version_info', JSON.stringify(result)); + } catch (e) { + console.error('초기 정보 저장 실패:', e); + } + } + + return result; + }, [getSavedVersionInfo]); + + const [versionInfo, setVersionInfo] = useState(defaultInfo); const [loading, setLoading] = useState(true); const [error, setError] = useState(false); const [retries, setRetries] = useState(0); @@ -35,12 +92,25 @@ const AppVersionInfo: React.FC = ({ try { console.log('앱 버전 정보 요청 시작... (retries:', retries, ')'); console.log('현재 플랫폼은', - isAndroidPlatform() ? 'Android' : isIOSPlatform() ? 'iOS' : '알 수 없음'); + isAndroidPlatform() ? 'Android' : isIOSPlatform() ? 'iOS' : '웹'); - // 재시도를 하는 경우 짤은 지연 추가 + // 재시도를 하는 경우 짧은 지연 추가 if (retries > 0) { await new Promise(resolve => setTimeout(resolve, 300)); } + + // localStorage에서 저장된 값을 먼저 확인 + const savedInfo = getSavedVersionInfo(); + + // 웹 환경에서는 저장된 값이 있으면 바로 사용 + if (!isAndroidPlatform() && !isIOSPlatform() && savedInfo && !savedInfo.defaultValues) { + console.log('웹 환경에서 저장된 버전 정보 사용:', savedInfo); + setVersionInfo(savedInfo); + setLoading(false); + return; + } + + // 네이티브 환경이거나 저장된 값이 없는 경우에만 플러그인 호출 const info = await getAppVersionInfo(); console.log('받은 앱 버전 정보:', info); @@ -49,28 +119,73 @@ const AppVersionInfo: React.FC = ({ throw new Error('유효하지 않은 응답 형식'); } - // 유효한 빌드 번호인지 확인하고 숫자로 변환 - let buildNumber = info.buildNumber; - if (typeof buildNumber === 'string') { - buildNumber = parseInt(buildNumber, 10); + // 타입 안전성을 위한 변환 함수 + const parseVersionInfo = (data: any): VersionInfo => { + // 빌드 번호 변환 + let buildNumber: number = defaultInfo.buildNumber; + if (typeof data.buildNumber === 'number') { + buildNumber = data.buildNumber; + } else if (typeof data.buildNumber === 'string' && data.buildNumber.trim() !== '') { + const parsed = parseInt(data.buildNumber, 10); + if (!isNaN(parsed)) buildNumber = parsed; + } + + // 버전 코드 변환 + let versionCode: number | undefined = defaultInfo.versionCode; + if (typeof data.versionCode === 'number') { + versionCode = data.versionCode; + } else if (typeof data.versionCode === 'string' && data.versionCode.trim() !== '') { + const parsed = parseInt(data.versionCode, 10); + if (!isNaN(parsed)) versionCode = parsed; + } + + // 응답 객체 구성 + return { + versionName: typeof data.versionName === 'string' && data.versionName.trim() !== '' + ? data.versionName + : defaultInfo.versionName, + buildNumber, + versionCode, + platform: typeof data.platform === 'string' ? data.platform : defaultInfo.platform, + pluginResponse: typeof data.pluginResponse === 'string' + ? data.pluginResponse + : JSON.stringify(data), + timestamp: typeof data.timestamp === 'number' ? data.timestamp : Date.now(), + defaultValues: false + }; + }; + + // 타입 변환 및 정제 + const newVersionInfo = parseVersionInfo(info); + + // 웹 환경에서는 defaultValues가 true인 경우(기본값) localStorage 값 우선 적용 + if (!isAndroidPlatform() && !isIOSPlatform() && newVersionInfo.defaultValues && savedInfo) { + console.log('웹 환경의 기본값 대신 저장된 값 사용'); + setVersionInfo(savedInfo); + setLoading(false); + return; } - setVersionInfo({ - versionName: info.versionName || '1.0.1', - buildNumber: isNaN(buildNumber) ? 3 : buildNumber, - versionCode: info.versionCode, - platform: info.platform, - pluginResponse: info.pluginResponse - }); - + // LocalStorage에 버전 정보 저장 + try { + localStorage.setItem('app_version_info', JSON.stringify(newVersionInfo)); + console.log('버전 정보가 localStorage에 저장됨:', newVersionInfo); + } catch (e) { + console.warn('버전 정보 저장 실패:', e); + } + + setVersionInfo(newVersionInfo); setLoading(false); console.log('앱 버전 정보 표시 준비 완료'); } catch (error) { console.error('버전 정보 가져오기 실패:', error); setError(true); setLoading(false); + + // 오류 발생 시 기존 저장된 값 유지 (초기화 방지) + console.log('오류 발생으로 기본값 사용'); } - }, [retries]); + }, [retries, defaultInfo]); // 재시도 처리 const handleRetry = useCallback(() => { @@ -83,6 +198,9 @@ const AppVersionInfo: React.FC = ({ // 컴포넌트 마운트 시 즉시 실행 (IIFE) useEffect(() => { + // 초기화 직후 localStorage 확인 + console.log('컴포넌트 마운트 시 localStorage 확인:', localStorage.getItem('app_version_info')); + let isMounted = true; (async () => {