Files
sodalive-android/docs/20260309_DataStore전환및SharedPreferences마이그레이션.md

6.8 KiB

DataStore 전환 및 SharedPreferences 마이그레이션

  • 공식 문서의 DataStore 권고 문구 확인 및 근거 정리
  • 프로젝트 내 SharedPreferences 사용 지점 식별
  • SharedPreferenceManager를 DataStore 기반으로 전환
  • LanguageManager를 DataStore 기반 읽기/쓰기로 전환
  • 채팅방 chat_room_prefs를 DataStore + 마이그레이션으로 전환
  • 앱 초기화 경로에 DataStore 초기화 반영
  • 진단/테스트/빌드 검증 결과 기록
  • 핵심 런타임 회귀 자동 테스트(androidTest) 추가

검증 기록

2026-03-09

  • 무엇/왜/어떻게: Android 공식 문구를 확인한 뒤, 기본 설정 저장소(SharedPreferenceManager)와 채팅방 전용 저장소(chat_room_prefs)를 각각 DataStore로 전환하고 SharedPreferencesMigration으로 기존 설치 기기의 데이터가 손실 없이 이관되도록 구현했다. 기존 호출부 대량 수정을 피하기 위해 기존 매니저 API를 유지하고 내부 저장소만 교체했다.
  • 공식 문서 근거: "If you're using SharedPreferences to store data, consider migrating to DataStore instead." (App Architecture: Data Layer - DataStore - Android Developers, https://developer.android.com/topic/libraries/architecture/datastore)
  • 실행 명령: lsp_diagnostics (변경 Kotlin 파일 8개)
  • 결과: 현재 실행 환경에 Kotlin LSP 서버가 없어 진단 불가(No LSP server configured for extension: .kt).
  • 실행 명령: ./gradlew :app:testDebugUnitTest :app:assembleDebug
  • 결과: BUILD SUCCESSFUL (단위 테스트/디버그 빌드 성공).
  • 실행 명령: Oracle 리뷰(bg_0bc7de61)
  • 결과: 리스너 콜백 쓰레드 호환성 리스크(백그라운드 스레드에서 UI 리스너 호출 가능)와 읽기 전용 SharedPreferences 뷰의 no-op edit() 리스크를 확인했고, SharedPreferenceManager에서 메인 스레드 디스패치 및 fail-fast edit() 예외 처리로 반영했다.
  • 실행 명령: ./gradlew :app:testDebugUnitTest :app:assembleDebug (Oracle 피드백 반영 후 재검증)
  • 결과: BUILD SUCCESSFUL.
  • 실행 명령: ./gradlew :app:lintDebug
  • 결과: 실패. 기존 프로젝트 이슈(AndroidManifest.xmlMissingClass(com.facebook.FacebookActivity) 포함 18 errors, 577 warnings)로 중단되었고, 재실행에서도 동일 결과다.
  • 실행 명령: ./gradlew :app:ktlintCheck
  • 결과: 실패. 기존 코드베이스 전반의 스타일 이슈(다수 파일)로 :app:ktlintMainSourceSetCheck 실패.
  • 무엇/왜/어떻게: 요청에 따라 "첫 접근 시 이관" 지점 주석을 추가했다. AppPreferencesDataStoreProviderSharedPreferencesMigration 등록부와 SharedPreferenceManager/ChatRoomPreferenceManagerdataStore.data.first() 트리거 지점에 주석을 배치해, 언제 이관이 실행되는지 코드를 읽는 즉시 파악할 수 있게 했다.
  • 실행 명령: ./gradlew :app:assembleDebug :app:testDebugUnitTest
  • 결과: BUILD SUCCESSFUL.
  • 무엇/왜/어떻게: 사용자 요청에 맞춰 "전체 일괄 치환" 대신 값 변화 반응 지점과 즉시 사용 안정성이 중요한 지점을 우선 리팩터링했다. SharedPreferenceManager/ChatRoomPreferenceManager 내부 runBlocking을 제거하고 비동기 수집 + 메모리 캐시 기반으로 변경했으며, 기존 OnSharedPreferenceChangeListener 의존 화면 4개를 Flow 수집으로 전환했다.
  • 반응형 전환 파일: MainActivity, HomeFragment, LiveFragment, AudioContentPlaylistDetailActivity에서 isPlayerServiceRunningFlow/roleFlowrepeatOnLifecycle로 수집해 UI를 갱신하도록 변경.
  • 실행 명령: ./gradlew :app:assembleDebug :app:testDebugUnitTest (비동기 리팩터링 반영 후)
  • 결과: BUILD SUCCESSFUL.
  • 실행 명령: ./gradlew :app:lintDebug (최종)
  • 결과: 실패. 기존 프로젝트 이슈(AndroidManifest.xmlMissingClass(com.facebook.FacebookActivity) 포함 18 errors, 577 warnings)로 동일하게 중단.

2026-03-11

  • 무엇/왜/어떻게: 핵심 런타임 회귀 2건을 수정했다. (1) SharedPreferenceManager/ChatRoomPreferenceManager가 초기 캐시 반영 전에 기본값을 반환하던 문제를 막기 위해 init()에서 dataStore.data.first()로 초기 스냅샷을 먼저 로딩한 뒤 initialized를 설정하도록 조정했다. (2) repeatOnLifecycle(STARTED) 재수집 시 미니플레이어가 중복 연결되던 문제를 막기 위해 MainActivity/AudioContentPlaylistDetailActivitymediaControllerFuture 가드와 지연 실행 runnable 정리(removeCallbacks)를 추가했다.
  • 반영 파일: SharedPreferenceManager.kt, ChatRoomPreferenceManager.kt, MainActivity.kt, AudioContentPlaylistDetailActivity.kt.
  • 실행 명령: lsp_diagnostics (변경 Kotlin 파일 4개)
  • 결과: 현재 실행 환경에 Kotlin LSP 서버가 없어 진단 불가(No LSP server configured for extension: .kt).
  • 실행 명령: ./gradlew --stop && ./gradlew :app:testDebugUnitTest :app:assembleDebug
  • 결과: BUILD SUCCESSFUL.
  • 실행 명령: ./gradlew :app:lintDebug
  • 결과: 실패. 기존 프로젝트 이슈(AndroidManifest.xmlMissingClass(com.facebook.FacebookActivity) 포함 18 errors, 577 warnings)로 동일하게 중단.
  • 무엇/왜/어떻게: 수동 재현에 의존하지 않도록 런타임 회귀 자동 검증을 위한 계측 테스트를 추가했다. DataStoreRuntimeRegressionTest에서 초기 스냅샷 재초기화 경로를 검증하고, MiniPlayerConnectionGuardTest에서 mediaControllerFuture가 이미 존재할 때 connectPlayerService()가 조기 반환되는 가드를 검증한다. 이를 위해 androidTest 실행 환경(testInstrumentationRunner, androidTestImplementation)을 설정했고, 테스트 재초기화를 위해 SharedPreferenceManager/ChatRoomPreferenceManager/AppPreferencesDataStoreProviderresetForTest() 훅을 추가했다.
  • 반영 파일: app/build.gradle, AppPreferencesDataStoreProvider.kt, SharedPreferenceManager.kt, ChatRoomPreferenceManager.kt, app/src/androidTest/java/kr/co/vividnext/sodalive/runtime/DataStoreRuntimeRegressionTest.kt, app/src/androidTest/java/kr/co/vividnext/sodalive/runtime/MiniPlayerConnectionGuardTest.kt.
  • 실행 명령: ./gradlew :app:testDebugUnitTest :app:assembleDebug :app:assembleDebugAndroidTest
  • 결과: BUILD SUCCESSFUL.
  • 실행 명령: ./gradlew :app:connectedDebugAndroidTest
  • 결과: BUILD SUCCESSFUL (SM-G960N - 10, 4 tests passed).
  • 실행 명령: ./gradlew :app:testDebugUnitTest :app:assembleDebug
  • 결과: BUILD SUCCESSFUL.
  • 실행 명령: ./gradlew :app:lintDebug
  • 결과: 실패. 기존 프로젝트 이슈(AndroidManifest.xmlMissingClass(com.facebook.FacebookActivity) 포함 18 errors, 577 warnings)로 동일하게 중단.