Skip to content

[#245] 버전 업데이트 파싱 에러 수정#246

Merged
hyuns66 merged 1 commit intodevelopfrom
fix/update-version-parsing-#245
Nov 30, 2025
Merged

[#245] 버전 업데이트 파싱 에러 수정#246
hyuns66 merged 1 commit intodevelopfrom
fix/update-version-parsing-#245

Conversation

@t1nm1ksun
Copy link
Member

@t1nm1ksun t1nm1ksun commented Nov 10, 2025

#️⃣연관된 이슈

📝작업 내용

앱 삭제 후 재설치시 토큰을 받아오기 전에 실행되어 빈 헤더로 요청하는 문제가 있었습니다.

  • 설치버전, 최신 버전 비교 로직을 메인에서 홈으로 이동했습니다.

PR 발행 전 체크 리스트

안해!

  • 발행자 확인
  • 프로젝트 설정 확인
  • 라벨 확인
  • 코드 린트 확인

스크린샷 (선택)

없어!

💬리뷰 요구사항(선택)

  • 리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요
    • @hyuns66 우리 위치권한 팝업 로직도 수정해야될 것 같은데요?

Summary by CodeRabbit

새 기능

  • 앱 업데이트 알림: 필수 업데이트와 선택 업데이트 알림이 홈 화면에 표시됩니다.
  • Play Store 연동: 알림에서 Play Store로 직접 이동하여 앱을 업데이트할 수 있습니다.

@t1nm1ksun t1nm1ksun self-assigned this Nov 10, 2025
@t1nm1ksun t1nm1ksun added the fix label Nov 10, 2025
@coderabbitai
Copy link

coderabbitai bot commented Nov 10, 2025

Walkthrough

앱 업데이트 체크 및 다이얼로그 표시 로직을 MainActivity/MainViewModel에서 HomeScreen/HomeViewModel으로 이관합니다. 새로운 업데이트 사이드 이펙트 두 개를 Home으로 옮기고, Main에서는 관련 로직을 제거하며, Play Store 연동 기능을 HomeScreen에 구현합니다.

Changes

Cohort / File(s) Summary
업데이트 다이얼로그 사이드 이펙트 추가
app/src/main/java/com/depromeet/team6/presentation/ui/home/HomeContract.kt
HomeSideEffect sealed interface에 ShowUpdateRequiredDialog, ShowUpdateOptionalDialog 두 개의 data object 추가
Home UI에 업데이트 기능 통합
app/src/main/java/com/depromeet/team6/presentation/ui/home/HomeScreen.kt
Import 추가 (Intent, DialogController, AppConstants 등); HomeRoute 함수 시그니처에 afterOnboarding 파라미터 추가 및 파라미터 순서 조정; DialogController 인스턴스 추가; 두 개의 업데이트 사이드 이펙트 처리 로직 추가; openPlayStoreForUpdate() 함수 구현
Home ViewModel에 버전 체크 로직 추가
app/src/main/java/com/depromeet/team6/presentation/ui/home/HomeViewModel.kt
GetAppVersionUseCase 의존성 주입 추가; 초기화 시점에 checkAppVersion() 호출; 버전 비교 유틸 및 VersionResult enum 추가; 버전에 따라 적절한 사이드 이펙트 발생
Main에서 업데이트 로직 제거
app/src/main/java/com/depromeet/team6/presentation/ui/main/MainActivity.kt, app/src/main/java/com/depromeet/team6/presentation/ui/main/MainContract.kt, app/src/main/java/com/depromeet/team6/presentation/ui/main/MainViewModel.kt
MainActivity에서 openPlayStoreForUpdate() 및 사이드 이펙트 처리 코드 제거; MainSideEffect에서 업데이트 관련 data object 제거; MainViewModel에서 GetAppVersionUseCase 의존성 및 checkAppVersion() 로직 제거

Sequence Diagram(s)

sequenceDiagram
    participant HomeScreen
    participant HomeViewModel
    participant GetAppVersionUseCase
    participant DialogController
    participant PlayStore

    HomeViewModel->>GetAppVersionUseCase: init → checkAppVersion()
    GetAppVersionUseCase->>GetAppVersionUseCase: 설치된 버전 vs 최신 버전 비교
    
    alt 필수 업데이트 필요
        GetAppVersionUseCase->>HomeViewModel: ShowUpdateRequiredDialog
    else 선택 업데이트 필요
        GetAppVersionUseCase->>HomeViewModel: ShowUpdateOptionalDialog
    end
    
    HomeViewModel->>HomeScreen: sideEffect 발행
    HomeScreen->>DialogController: 다이얼로그 표시
    DialogController-->>HomeScreen: 사용자 입력 수신
    HomeScreen->>HomeScreen: openPlayStoreForUpdate(context)
    HomeScreen->>PlayStore: Intent로 Play Store 앱 실행
    alt Play Store 앱 미설치
        HomeScreen->>PlayStore: 웹 브라우저로 URL 오픈 (fallback)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

세심한 검토가 필요한 영역:

  • 버전 비교 로직: compareVersions() 함수와 cleanVersion() 확장 함수의 정확성을 확인하세요. 버전 문자열 파싱 및 비교 로직이 모든 버전 형식(예: "1.0.0", "1.0.0-beta" 등)을 올바르게 처리하는지 검증이 중요합니다.
  • 라이프사이클 관리: MainActivity에서 lifecycle-aware LaunchedEffect를 제거했는데, HomeScreen에서 LaunchedEffect의 라이프사이클 안전성이 보장되는지 확인하세요. 특히 afterOnboarding 플래그와의 상호작용을 검토하세요.
  • Play Store 오픈 로직: openPlayStoreForUpdate()에서 Intent 실패 시 fallback 처리가 모든 예외 상황을 커버하는지 확인하세요.
  • 사이드 이펙트 이관의 완전성: Main에서 Home으로 이관된 로직이 누락되거나 중복되지 않았는지 확인하세요.

Possibly related PRs

  • PR #243: 앱 버전 확인 기능의 핵심 로직(VersionService, Repository, GetAppVersionUseCase)을 구현한 PR입니다. 이 PR은 그 로직을 Home 화면에서 소비하면서 직접 의존성을 갖습니다.
  • PR #229: DialogController와 OneButton/TwoButton 다이얼로그 API를 도입한 PR입니다. 이 PR에서 새로 추가된 업데이트 다이얼로그 표시 기능이 그 API에 의존합니다.
  • PR #235: 동일한 UI 모듈(HomeContract, HomeViewModel, 메인 관련 파일)을 수정하는 PR으로, 파일 레벨의 변경이 겹칩니다.

Suggested reviewers

  • hyuns66
  • jieeeunnn

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Out of Scope Changes check ❓ Inconclusive 모든 변경사항이 버전 업데이트 파싱 에러 수정이라는 목표와 관련되어 있으나, MainActivity에서 권한 요청 로직의 포맷팅 변경은 핵심 수정사항과 직접적인 연관이 낮습니다. MainActivity의 권한 요청 로직 포맷팅 변경이 버전 파싱 에러 수정과의 필요성이 명확하지 않으므로, 해당 변경사항이 필수인지 리뷰어와 확인이 필요합니다.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 주요 변경사항인 '버전 업데이트 파싱 에러 수정'을 명확하게 요약하고 있으며, 체인지셋의 핵심 변경(버전 비교 로직을 Main에서 Home으로 이동)과 직접적으로 연관되어 있습니다.
Linked Issues check ✅ Passed 버전 업데이트 파싱 이슈를 해결하기 위해 버전 비교 로직을 Main에서 Home으로 이동하고, GetAppVersionUseCase를 HomeViewModel에 주입하여 초기화 시 버전 검사를 수행하도록 구현했으며, 토큰 수신 이전 실행 문제를 해결했습니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/update-version-parsing-#245

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot]
coderabbitai bot previously requested changes Nov 10, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce2a876 and d15a315.

📒 Files selected for processing (6)
  • app/src/main/java/com/depromeet/team6/presentation/ui/home/HomeContract.kt (1 hunks)
  • app/src/main/java/com/depromeet/team6/presentation/ui/home/HomeScreen.kt (11 hunks)
  • app/src/main/java/com/depromeet/team6/presentation/ui/home/HomeViewModel.kt (9 hunks)
  • app/src/main/java/com/depromeet/team6/presentation/ui/main/MainActivity.kt (1 hunks)
  • app/src/main/java/com/depromeet/team6/presentation/ui/main/MainContract.kt (1 hunks)
  • app/src/main/java/com/depromeet/team6/presentation/ui/main/MainViewModel.kt (0 hunks)
💤 Files with no reviewable changes (1)
  • app/src/main/java/com/depromeet/team6/presentation/ui/main/MainViewModel.kt
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-10-18T12:10:51.782Z
Learnt from: CR
Repo: depromeet/16th-team6-Android PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-10-18T12:10:51.782Z
Learning: Adopt Clean Architecture with MVI and unidirectional data flow (Intent -> ViewModel -> State -> UI)

Applied to files:

  • app/src/main/java/com/depromeet/team6/presentation/ui/home/HomeViewModel.kt
📚 Learning: 2025-10-18T12:10:51.782Z
Learnt from: CR
Repo: depromeet/16th-team6-Android PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-10-18T12:10:51.782Z
Learning: Separate layers: UI with Jetpack Compose subscribing to ViewModel state; Domain as pure Kotlin with UseCases; Data using repositories abstracting sources

Applied to files:

  • app/src/main/java/com/depromeet/team6/presentation/ui/home/HomeViewModel.kt
  • app/src/main/java/com/depromeet/team6/presentation/ui/main/MainActivity.kt
  • app/src/main/java/com/depromeet/team6/presentation/ui/home/HomeScreen.kt
📚 Learning: 2025-10-18T12:10:51.782Z
Learnt from: CR
Repo: depromeet/16th-team6-Android PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-10-18T12:10:51.782Z
Learning: Use AAC ViewModel and Navigation for lifecycle-aware state and app navigation

Applied to files:

  • app/src/main/java/com/depromeet/team6/presentation/ui/main/MainActivity.kt
  • app/src/main/java/com/depromeet/team6/presentation/ui/home/HomeScreen.kt
🧬 Code graph analysis (1)
app/src/main/java/com/depromeet/team6/presentation/ui/home/HomeViewModel.kt (1)
app/src/main/java/com/depromeet/team6/presentation/util/base/BaseViewModel.kt (3)
  • setState (39-41)
  • setEvent (43-45)
  • setSideEffect (103-105)
🪛 detekt (1.23.8)
app/src/main/java/com/depromeet/team6/presentation/ui/home/HomeViewModel.kt

[warning] 769-769: The caught exception is too generic. Prefer catching specific exceptions to the case that is currently handled.

(detekt.exceptions.TooGenericExceptionCaught)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CI
🔇 Additional comments (5)
app/src/main/java/com/depromeet/team6/presentation/ui/main/MainContract.kt (1)

18-18: 업데이트 관련 사이드 이펙트가 Home으로 올바르게 이동되었습니다.

MainSideEffect를 비어있는 sealed interface로 유지하는 것은 깔끔한 리팩토링입니다. 업데이트 다이얼로그 책임이 이제 HomeSideEffect에 있으므로 Main 레이어의 관심사가 명확해졌습니다.

app/src/main/java/com/depromeet/team6/presentation/ui/main/MainActivity.kt (1)

131-160: 리뷰 요청사항이 이미 정상 구현되었습니다. 마이그레이션 완료 확인됨.

검증 결과, MainActivity에서 제거된 업데이트 다이얼로그 로직이 Home 모듈에 완전히 구현되어 있음을 확인했습니다:

  • HomeContract.kt: ShowUpdateRequiredDialog, ShowUpdateOptionalDialog 사이드 이펙트 정의됨
  • HomeScreen.kt: 두 다이얼로그 모두 적절히 처리 중 (openPlayStoreForUpdate() 함수 포함)
  • HomeViewModel.kt: GetAppVersionUseCase 임포트되어 버전 체크 기능 보유

아키텍처 개선(Main 시작 시점→ Home 진입 시점)이 정상 완료되었으므로 코드는 안정적인 상태입니다.

app/src/main/java/com/depromeet/team6/presentation/ui/home/HomeContract.kt (1)

86-87: LGTM!

업데이트 다이얼로그를 위한 사이드 이펙트 추가가 MVI 패턴에 맞게 잘 구현되었습니다.

app/src/main/java/com/depromeet/team6/presentation/ui/home/HomeScreen.kt (1)

1052-1073: 상수 정의 확인 완료 - 구현 정상

검증 결과, AppConstants.PLAY_STORE_PACKAGE_NAMEAppConstants.PLAY_STORE_URL 모두 올바르게 정의되어 있습니다:

  • PLAY_STORE_PACKAGE_NAME = "com.depromeet.team6" (Constraints.kt:28)
  • PLAY_STORE_URL = "https://play.google.com/store/apps/details?id=$PLAY_STORE_PACKAGE_NAME&hl=ko" (Constraints.kt:29-30)

함수 구현도 적절합니다. Play Store 앱 우선 시도 후 웹 폴백이 정상 동작하며, 에러 처리와 플래그 설정도 올바릅니다.

app/src/main/java/com/depromeet/team6/presentation/ui/home/HomeViewModel.kt (1)

74-74: 버전 체크 호출 시점은 적절함 - 추가 검증 불필요

분석 결과, HomeViewModel의 init 블록에서 checkAppVersion() 호출하는 것은 안전합니다:

  1. 엔드포인트 특성: /app/version 엔드포인트는 public 엔드포인트로 인증이 필요하지 않으므로, 빈 헤더로 요청해도 API가 응답합니다.
  2. 토큰 가용성: HomeViewModel은 로그인 후 메인 화면으로 표시되므로, 이 시점에서 토큰은 이미 초기화되어 있습니다.
  3. 비동기 실행: viewModelScope.launch를 통한 비동기 실행은 defensive programming으로서 적절합니다.

PR의 변경사항(MainActivity init → HomeViewModel init)은 앱 시작 시 불필요한 작업을 지연시키는 좋은 아키텍처 개선입니다.

Comment on lines +192 to +207
is HomeContract.HomeSideEffect.ShowUpdateRequiredDialog -> {
dialogController.showAtchaOneButtonAlert(
message = "더 좋아진 앗차를 사용하기 위해\n업데이트가 필요해요",
onConfirm = { openPlayStoreForUpdate(context = context) },
confirmButtonText = "업데이트 하기"
)
}

is HomeContract.HomeSideEffect.ShowUpdateOptionalDialog -> {
dialogController.showAtchaTwoButtonAlert(
message = "더 좋아진 앗차를 사용하기 위해\n업데이트가 필요해요",
onConfirm = { openPlayStoreForUpdate(context = context) },
confirmButtonText = "업데이트 하기",
closeButtonText = "닫기"
)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

다이얼로그 메시지를 string resources로 관리

Lines 194, 202에서 다이얼로그 메시지가 하드코딩되어 있습니다. 다국어 지원 및 유지보수를 위해 strings.xml로 이동하는 것을 권장합니다.

strings.xml에 추가:

<string name="update_dialog_message">더 좋아진 앗차를 사용하기 위해\n업데이트가 필요해요</string>
<string name="update_dialog_confirm">업데이트 하기</string>
<string name="update_dialog_close">닫기</string>

코드 수정:

 is HomeContract.HomeSideEffect.ShowUpdateRequiredDialog -> {
     dialogController.showAtchaOneButtonAlert(
-        message = "더 좋아진 앗차를 사용하기 위해\n업데이트가 필요해요",
+        message = stringResource(R.string.update_dialog_message),
         onConfirm = { openPlayStoreForUpdate(context = context) },
-        confirmButtonText = "업데이트 하기"
+        confirmButtonText = stringResource(R.string.update_dialog_confirm)
     )
 }
🤖 Prompt for AI Agents
In app/src/main/java/com/depromeet/team6/presentation/ui/home/HomeScreen.kt
around lines 192 to 207, the dialog strings are hardcoded; move them to string
resources and replace the literal text with resource lookups. Add the three
strings to res/values/strings.xml as suggested (update_dialog_message,
update_dialog_confirm, update_dialog_close), then update the calls to
dialogController.showAtchaOneButtonAlert and showAtchaTwoButtonAlert to use
context.getString(R.string.update_dialog_message),
context.getString(R.string.update_dialog_confirm), and
context.getString(R.string.update_dialog_close) respectively (ensure the Android
R import is available).

Comment on lines +711 to +739
private fun checkAppVersion() {
val installedVersion = getCurrentVersionName()?.cleanVersion() ?: "0.0.0"
viewModelScope.launch {
getAppVersionUseCase().onSuccess { appVersion ->
val latestVersion = appVersion.removePrefix("Success(")
.removePrefix("v")
.removeSuffix(")")
.cleanVersion()

Timber.d("latestVersion: $latestVersion, installedVersion: $installedVersion")

when (compareVersions(installedVersion, latestVersion)) {
VersionResult.UPDATE_REQUIRED -> {
setSideEffect(HomeContract.HomeSideEffect.ShowUpdateRequiredDialog)
}

VersionResult.UPDATE_OPTIONAL -> {
setSideEffect(HomeContract.HomeSideEffect.ShowUpdateOptionalDialog)
}

VersionResult.UP_TO_DATE -> {
Timber.d("최신 버전입니다")
}
}
}.onFailure {
Timber.e(it, "앱 버전 확인 실패")
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

응답 문자열 파싱이 취약함

Lines 715-718에서 응답을 removePrefix("Success(") 등으로 파싱하는 방식은 매우 취약합니다. UseCase가 이미 Result<String>을 반환한다면, 성공 시 문자열 내용에 "Success("가 포함될 이유가 없습니다.

GetAppVersionUseCase의 반환 타입을 확인하고, 정확한 응답 형식에 맞게 파싱 로직을 수정하세요. DTO를 사용하거나 최소한 응답 형식에 대한 가정을 검증하는 로직이 필요합니다.


🏁 Script executed:

#!/bin/bash
# GetAppVersionUseCase의 반환 타입과 구현 확인
ast-grep --pattern $'class GetAppVersionUseCase {
  $$$
  operator fun invoke($$$): $RETURN_TYPE {
    $$$
  }
  $$$
}'

# 실제 API 응답 형식 확인
rg -n "appVersion|getAppVersion" --type=kotlin -C5

Length of output: 10513



---

🏁 Script executed:

```shell
#!/bin/bash
# Find GetAppVersionUseCase implementation
find . -name "*GetAppVersionUseCase*" -type f

# Search for class definition
rg -n "class GetAppVersionUseCase|fun GetAppVersionUseCase" --type=kotlin -A20

Length of output: 2554



---

🏁 Script executed:

```shell
#!/bin/bash
# Find VersionRepository interface and implementations
rg -n "interface VersionRepository|class.*VersionRepository" --type=kotlin -A15

# Look for getVersion implementation
rg -n "fun getVersion|getVersion\(\)" --type=kotlin -B3 -A10 | head -80

Length of output: 5497


VersionRepositoryImpl에서 Result 타입을 문자열로 변환하는 심각한 버그

근본 원인은 HomeViewModel이 아니라 VersionRepositoryImpl.kt 라인 12에 있습니다.

// 현재 (잘못됨)
versionRemoteDataSource.getVersion().toString()

versionRemoteDataSource.getVersion()Result<String>을 반환하는데, 여기서 .toString()을 호출하면 "Success(v1.0.0)" 같은 문자열 표현이 되어버립니다. 그 결과 HomeViewModel의 onSuccess 블록에서 받는 appVersion은 이미 "Success(v1.0.0)" 형태의 문자열이 되어있고, 이를 파싱하기 위해 removePrefix("Success(") 같은 우회책을 쓰고 있는 것입니다.

해결책: VersionRepositoryImpl.kt 라인 11-13을 다음과 같이 수정하세요:

override suspend fun getVersion(): Result<String> = 
    versionRemoteDataSource.getVersion()

Result 타입 시맨틱을 제대로 존중하고, HomeViewModel의 파싱 로직도 제거할 수 있습니다.

Comment on lines +765 to +773
private fun getCurrentVersionName(): String? {
return try {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
packageInfo.versionName
} catch (e: Exception) {
Timber.e(e, "Failed to get app version name")
"0.0.0"
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

구체적인 예외 타입 사용 필요

PackageManager.getPackageInfo()PackageManager.NameNotFoundException을 던지므로, 일반적인 Exception 대신 구체적인 예외 타입을 catch하는 것이 좋습니다.

As per static analysis hints.

다음과 같이 수정하세요:

 private fun getCurrentVersionName(): String? {
     return try {
         val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
         packageInfo.versionName
-    } catch (e: Exception) {
+    } catch (e: PackageManager.NameNotFoundException) {
         Timber.e(e, "Failed to get app version name")
         "0.0.0"
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private fun getCurrentVersionName(): String? {
return try {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
packageInfo.versionName
} catch (e: Exception) {
Timber.e(e, "Failed to get app version name")
"0.0.0"
}
}
private fun getCurrentVersionName(): String? {
return try {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
packageInfo.versionName
} catch (e: PackageManager.NameNotFoundException) {
Timber.e(e, "Failed to get app version name")
"0.0.0"
}
}
🧰 Tools
🪛 detekt (1.23.8)

[warning] 769-769: The caught exception is too generic. Prefer catching specific exceptions to the case that is currently handled.

(detekt.exceptions.TooGenericExceptionCaught)

🤖 Prompt for AI Agents
In app/src/main/java/com/depromeet/team6/presentation/ui/home/HomeViewModel.kt
around lines 765 to 773, the catch currently swallows all Exceptions; change it
to catch the specific PackageManager.NameNotFoundException instead of Exception,
keep the Timber.e logging and the "0.0.0" fallback return, and add the necessary
import for android.content.pm.PackageManager.NameNotFoundException if missing.

@hyuns66 hyuns66 merged commit a6f2c52 into develop Nov 30, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Fix] 버전 업데이트 파싱 이슈

3 participants