[Android] SSAID 는 debug 환경에서 달라질 수 있다!

easyhooon·2025년 4월 22일
0
post-thumbnail

TL;DR

다른 노트북으로 개발할때도 SSAID 를 같게 유지하려면 명시적으로 debug.keystore 서명을 해줘야 한다.

서론

두 개의 노트북을 오가면서 개발을 하면서 동일한 앱, Build Variant 환경에서 SSAID 가 다르게 얻어지는 이슈를 겪어, 이를 해결한 방법을 공유하고자 한다.

본론

현재 개발중인 학교 축제 관련 서비스앱은 로그인을 별도로 하지 않고, device 고유 ID인 SSAID 를 통해 사용자를 구분하고 있다.

앱이라는 플랫폼 자체가 웹과 비교했을 때, 스토어에서 직접 검색하여 설치해야 하는 높은 진입 장벽이 있기에, 접근 편의성을 보완하기 위해 로그인을 하지 않아도, 학교 축제 정보들을 확인할 수 있도록 기획 하였다.

다행히 SSAID 는 디바이스를 초기화하지 않는다면, 앱을 삭제하고 다시 설치해도 그 값이 유지가 되기 때문에, 로그인이 굳이 필요 없는 앱에서는 사용자를 식별하는 고유 값으로 쓸 수 있어 유용하다.

Using getString to get device identifiers is not recommended Toggle info (⌘F1)
Inspection info: Using these device identifiers is not recommended other than for high value fraud prevention and advanced telephony use-cases. For advertising use-cases, use AdvertisingIdClient$Info#getId and for analytics, use InstanceId#getId.

구글에서는 개인정보 보호, 보안 관련 문제로, 기기 고유 식별자를 앱에서 사용하는 것을 권장하진 않지만, 서버에 힘을 빌리지 않는 경우, 앱을 삭제하고 다시 설치했을 때 그 값을 유지하는 것은 유일하게 SSAID 로 알고 있기 때문에, 아직 더 나은 대체제를 발견하지 못했다. 아시는 분 있으시면 공유 부탁드립니다...

@SuppressLint("HardwareIds")
fun getDeviceId(context: Context): String {
    return Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID)
}

참고로 터미널에 adb shell settings get secure android_id 명령어를 입력하여 앱별 SSAID 가 아닌, 기기 고유 SSAID 를 확인해볼 수 있다.
앱 내에서 getDeviceId 함수를 통해 얻은 앱별 SSAID 와, 쉘 명령어를 통해 얻은 SSAID 가 다르게 나오는 것을 알 수 있다.

문제 발생

노트북을 바꿔서 개발 및 QA 를 진행 하던 도중에, 전날 같은 핸드폰, 다른 노트북에서 웨이팅을 걸어두었던 부스가, 웨이팅 화면내에 웨이팅 부스 목록에서 조회되지 않는 문제가 발생하였다.

웨이팅 취소를 했던 기억이 없어서, 서버 개발자님께 문의를 하였는데, 웨이팅이 된 기록이 DB에 있는데, 다른 deviceId 로 예약이 되어있었다!

// 웨이팅 추가
@POST("waiting")
suspend fun requestBoothWaiting(
    @Body boothWaitingRequest: BoothWaitingRequest,
): WaitingResponse
    
@Serializable
data class BoothWaitingRequest(
    @SerialName("boothId")
    val boothId: Long,
    @SerialName("tel")
    val tel: String,
    // SSAID
    @SerialName("deviceId")
    val deviceId: String,
    @SerialName("partySize")
    val partySize: Long,
    @SerialName("pinNumber")
    val pinNumber: String,
    @SerialName("fcmToken")
    val fcmToken: String,
)

// 내 예약된 웨이팅 조회(deviceID 기준)
@GET("waiting/me/{deviceId}")
suspend fun getMyWaitingList(
    @Path("deviceId") deviceId: String,
): MyWaitingResponse

뭣?

같은 핸드폰을 연결해서 빌드한 것이기 때문에 deviceId가 달라지진 않았을 것이라 생각했다...

같은 앱이기도 하고, 의심이 가는 부분은 Build Variant 별로 앱의 packageName 이 변경되도록 설정한 것인데(debug 빌드시 pacakgeName suffix에 .dev를 추가) 같은 debug 빌드였다.

흠...

문제 해결

Android Studio 에서 debug 빌드를 진행하는 경우, 기본적으로 각 개발 머신(노트북)마다 서로 다른 debug keystore 가 사용된다.

앱별 SSAID 는 앱의 서명 키를 구성 요소 중 하나로 사용하기 때문에, 서명 키가 다르면 SSAID 도 달라지게 된다!

이는 같은 개발 머신(노트북)일지라도, signingConfigs 의 debug 설정을 변경하면, SSAID 가 달라진다는 의미이다.

따라서, SSAID 를 일관되게 유지하기 위해, 프로젝트내에 debug.keystore 서명을 명시적으로 추가해주었다.

    signingConfigs {
        // 추가! 
        getByName("debug") {
            storeFile = file("$rootDir/debug.keystore")
            storePassword = "android"
            keyAlias = "androiddebugkey"
            keyPassword = "android"
        }
		
        create("release") {
            val propertiesFile = rootProject.file("keystore.properties")
            val properties = Properties()
            properties.load(propertiesFile.inputStream())
            storeFile = file(properties["STORE_FILE"] as String)
            storePassword = properties["STORE_PASSWORD"] as String
            keyAlias = properties["KEY_ALIAS"] as String
            keyPassword = properties["KEY_PASSWORD"] as String
        }
    }

왜 release 는 create 인데, debug 는 getByName 인가요?

위에서 언급했든, 노트북에서 debug 환경에서 build 를 할 경우, 기본적으로 debug keystore 를 생성한다.

따라서 getByName("debug")를 사용하여 이미 존재하는 debug signingConfig 를 찾아 수정(update)하도록 설정하였다.

create("debug")로 선언할 경우, 아래와 같은 에러가 발생하는 것을 확인할 수 있다.

Cannot add a SigningConfig with name 'debug' as a SigningConfig with that name already exists.

설정을 완료하면, sync now 를 한 다음에, 설치된 앱을 지우거나, clean build 및 캐시를 제거해준 뒤 다시 빌드하면 SSAID 가 변경된 것을 확인할 수 있을 것이다.

그리고 다른 개발 환경(노트북)에서 마찬가지로 빌드를 할 경우, 설정 후 변경된 SSAID 와 같은 값이 조회되는 것을 확인할 수 있다.

결론

debug 환경에서 SSAID 를 일관되게 유지하는 방법에 대해 알아보았다.

debug.keystore 라는 프로젝트 구성파일을 추가하였기 때문에, 이를 어떻게 관리할지 고민되는 부분인데,
아무리 debug 환경이라 할지라도 keystore 이기 때문에, git 에 추적되지 않도록 gitignore 에 추가하였다.

CI/CD 를 구축하였다면 이렇게 추가된 debug.keystore 때문에 build 관련 workflow 에 문제가 생길 수 있기 때문에, github secrets 등에 추가하여, remote 환경에서 debug.keystore 를 불러올 수 있도록 step 을 추가해줄 필요가 있다.

참고로 release 빌드에서는 일반적으로 모든 개발 환경에서 동일한 release keystore 를 사용하므로, 해당 문제가 발생하지 않는다. 실제 앱 환경에선 문제가 되지 않는다는 의미이다. 다행

reference)
https://developer.android.com/privacy-and-security/about?hl=ko
https://developer.android.com/identity/user-data-ids?hl=ko
https://developer.android.com/studio/publish/app-signing?hl=ko
https://roleplayerrhea.tistory.com/309

profile
실력은 고통의 총합이다. Android Developer

0개의 댓글