Flutter와 Android를 연동해 Firebase Realtime Database를 사용할 때, 종종 데이터 구조와 관련된 오류가 발생한다. 특히 리스트(List) 데이터를 저장하거나 불러올 때 DatabaseException이 발생하는 경우가 많다.
문제 상황
Flutter 앱에서 Firebase로 데이터를 저장하고, Android 네이티브 코드(MainActivity.kt)에서 MethodChannel을 통해 데이터를 처리할 때 다음과 같은 오류가 발생했다.
com.google.firebase.database.DatabaseException: Expected a Map while deserializing, but got a class java.util.ArrayList
이 오류는 Firebase에서 데이터를 Map 형태로 기대했지만, 실제 데이터는 List였기 때문에 발생한 것이다.
원인 분석
Firebase의 DataSnapshot.getValue() 메서드는 내부적으로 데이터를 특정 타입으로 역직렬화(deserialize)한다. 이때 기대하는 타입과 실제 Firebase에 저장된 타입이 다르면 예외가 발생한다.
예를 들어, Firebase에 아래와 같은 데이터가 저장되어 있다고 가정하자.
"2025-04-22": [
{ "diary": "첫 번째 일기" },
{ "diary": "두 번째 일기" }
]
이 데이터는 List<Map<String, Any>> 형식이다. 그러나 기존 Android 코드에서는 이를 Map<String, Map<String, Any>> 형식으로 읽으려고 했기 때문에 오류가 발생했다.
원래 작성했던 방식 (오류 발생)
저장할 때
val diaryMap = mutableMapOf<String, Map<String, Any>>()
diaries.forEachIndexed { index, item ->
if (item is Map<*, *>) {
val diaryEntry = mutableMapOf<String, Any>()
item.forEach { (key, value) ->
if (key is String) {
diaryEntry[key] = value as Any
}
}
diaryMap[index.toString()] = diaryEntry
}
}
diariesRef.child(date).setValue(diaryMap)
불러올 때
val indicator = object : GenericTypeIndicator<Map<String, Map<String, Any>>>() {}
val diaryMap = snapshot.getValue(indicator) ?: emptyMap()
val diaryList = diaryMap.toSortedMap().values.map {
it["diary"] as? String ?: ""
}
이 구조는 Firebase에 Map 구조로 저장하고, 그에 맞게 읽으려는 방식이다. 그러나 실제 Flutter 쪽에서는 List 형태로 전달하고 있었기 때문에 충돌이 발생했다.
🎈이렇게 바꿨다 (정상 동작)
저장할 때
val diaryList = mutableListOf<Map<String, Any>>()
diaries.forEach { item ->
if (item is Map<*, *>) {
val diaryEntry = mutableMapOf<String, Any>()
item.forEach { (key, value) ->
if (key is String) {
diaryEntry[key] = value as Any
}
}
diaryList.add(diaryEntry)
}
}
diariesRef.child(date).setValue(diaryList)
불러올 때
val indicator = object : GenericTypeIndicator<List<Map<String, Any>>>() {}
val diaryList = snapshot.getValue(indicator) ?: emptyList()
val resultList = diaryList.map {
it["diary"] as? String ?: ""
}
이제 Firebase에는 리스트로 저장되고, 리스트로 정확하게 읽어오기 때문에 오류 없이 작동한다.
결론
Firebase Realtime Database에서 데이터를 리스트로 저장하고 불러오려면 다음을 지켜야 한다.
- 저장 시 List<Map<String, Any>> 형식으로 Firebase에 저장한다.
- 조회 시 GenericTypeIndicator<List<Map<String, Any>>>를 사용해 타입을 정확하게 지정한다.
- Flutter에서 전달하는 데이터 구조와 Android에서 처리하는 구조를 일치시켜야 한다.
✅ 요약: Before & After
항목 기존 방식 (오류 발생) 변경된 방식 (정상 동작)
저장 형식 | Map<String, Map<String, Any>> | List<Map<String, Any>> |
조회 형식 | GenericTypeIndicator<Map<String, Map<String, Any>>> | GenericTypeIndicator<List<Map<String, Any>>> |
Firebase 저장 구조 | 중첩된 Map | 리스트 |
Flutter 구조와의 일치 여부 | ❌ | ✅ |
이 글을 참고해서 비슷한 구조의 데이터를 Firebase와 안전하게 주고받을 수 있기를..!
Flutter와 Android 간 데이터 구조를 맞추는 것이 핵심이다.
'개발 > Android' 카테고리의 다른 글
[Flutter] Flutter에서 ref.invalidate() 사용 시 새로고침 오류 해결하기 (0) | 2025.04.21 |
---|---|
[Flutter] 영상 플레이어 — VideoPlayer에서 BetterPlayer로, 그리고 비율 문제 해결 (0) | 2025.04.17 |
[Flutter 오류 해결] Gradle & Java 버전 호환 이슈 해결 방법 (feat. Java 21 → 17 다운그레이드) (1) | 2025.04.17 |
[Flutter] Riverpod 상태관리 - ConsumerStatefulWidget 가이드 (0) | 2025.04.02 |
[Flutter] Riverpod 상태관리 - watch vs read 차이 (0) | 2025.04.02 |