파이어베이스 스토리지를 사용하면서
글 작성할 때, 이미지를 업로드하고 완료버튼을 누르면
작성한 글 화면이 나오면서 바로 이미지를 불러오는 경우
아래와 같은 에러가 발생하고 앱이 꺼지는 일이 발생한다.
에러의 내용을 보아하니 찾으려는 이미지 파일이 실제 파이어베이스 스토리지에 없어서라고 뜨는데...
해당 파일은 이미 스토리지에 잘 저장이 되어있는 상태이고...
검색을 해봐도.... 나와 동일한 에러를 겪은 케이스가 존재하지만 전부 그들이 파일 이름을 잘못작성해서 발생한 에러라는 답변 뿐이었다.
내가 작성한 코드도 그렇게 오타가 있어서 그런건가 싶어서 로그를 찍어봐도.. 업로드하는 시점의 파일명과 이미지 URL을 얻는 시점의 파일명이 동일한걸로 확인되어서 경로를 잘못 작성하여 발생하는 에러는 아닌 것 같았다.
강사님의 경우에도 처음에는 같은 코드로 실패하다가 다시 시도하니 에러 없이 성공했는데
정말로 바뀐 코드가 없어서 왜 실패하고 성공한건지 의문이었다...
강사님은 에러가 안나니까 그냥 넘어가셨는데...
강사님이 작성하신 코드로 아예 복붙을 해보아도 내가 시도할 때는 그래도 에러가 발생했다.
파이어베이스를 사용하는 부분은 비동기처리를 하다보니 파일이 서버에 업로드되는 동안 그 짧은 찰나에 바로 파일을 찾으려고 하다보니 비어있어서 에러를 발생시킨걸로 의심을 했는데
역시나...
이미지 데이터 불러오는 메서드 앞에 delay를 1초 정도 주니까 에러없이 사진을 잘 갖고 오는 것이었다....
아래의 코드는 원래 작성한 코드이고
class ContentDao {
companion object{
// 이미지 데이터를 firebase storage에 업로드하는 메서드
suspend fun uploadImage(context:Context, fileName:String, uploadFileName:String){
// 외부저장소 까지의 경로를 가져온다.
val filePath = context.getExternalFilesDir(null).toString()
// 서버로 업로드할 파일의 경로
val file = File("${filePath}/${fileName}")
val uri = Uri.fromFile(file)
val job1 = CoroutineScope(Dispatchers.IO).launch {
// Storage에 접근할 수 있는 객체를 가져온다.(폴더의 이름과 파일이름을 저장해준다.
val storageRef = Firebase.storage.reference.child("image/$uploadFileName")
// 업로드한다.
storageRef.putFile(uri)
}
job1.join()
}
// 이미지 데이터를 받아오는 메서드
suspend fun gettingContentImage(context:Context, imageFileName:String, imageView: ImageView){
CoroutineScope(Dispatchers.IO).launch {
// 이미지에 접근할 수 있는 객체를 가져온다.
val storageRef = Firebase.storage.reference.child("image/$imageFileName")
// 이미지의 주소를 가지고 있는 Uri 객체를 받아온다.
val imageUri = storageRef.downloadUrl.await()
// 이미지 데이터를 받아와 이미지 뷰에 보여준다.
CoroutineScope(Dispatchers.Main).launch {
Glide.with(context).load(imageUri).into(imageView)
// 이미지 뷰가 나타나게 한다.
imageView.visibility = View.VISIBLE
}
}
}
// 입력 요소에 값을 넣어준다.
fun settingInputForm(){
// 이미지뷰를 안보이게 한다.
fragmentReadContentBinding.imageViewReadContent.visibility = View.INVISIBLE
// 입력 요소에 띄어쓰기를 넣어준다.
readContentViewModel.textFieldReadContentSubject.value = " "
readContentViewModel.textFieldReadContentText.value = " "
readContentViewModel.textFieldReadContentDate.value = " "
readContentViewModel.textFieldReadContentType.value = " "
readContentViewModel.textFieldReadContentNickName.value = " "
CoroutineScope(Dispatchers.Main).launch {
// 현재 글 번호에 해당하는 글 데이터를 가져온다.
val contentModel = ContentDao.selectContentData(contentIdx)
// 글을 작성한 사용자의 번호를 통해 사용자 정보를 가져온다.
val userModel = UserDao.gettingUserInfoByUserIdx(contentModel?.contentWriterIdx!!)
// 가져온 데이터를 보여준다.
readContentViewModel.textFieldReadContentSubject.value = contentModel?.contentSubject
readContentViewModel.textFieldReadContentType.value = when(contentModel?.contentType){
ContentType.TYPE_FREE.number -> ContentType.TYPE_FREE.str
ContentType.TYPE_HUMOR.number -> ContentType.TYPE_HUMOR.str
ContentType.TYPE_SOCIETY.number -> ContentType.TYPE_SOCIETY.str
ContentType.TYPE_SPORTS.number -> ContentType.TYPE_SPORTS.str
else -> ""
}
readContentViewModel.textFieldReadContentNickName.value = userModel?.userNickName
readContentViewModel.textFieldReadContentDate.value = contentModel?.contentWriteDate
readContentViewModel.textFieldReadContentText.value = contentModel?.contentText
// 이미지 데이터를 불러온다.
if(contentModel?.contentImage != null) {
ContentDao.gettingContentImage(contentActivity, contentModel.contentImage!!, fragmentReadContentBinding.imageViewReadContent)
}
}
}
아래의 코드는 이미지 데이터를 불러오는 메서드를 호출하기 전에 delay를 붙인 코드인데
// 입력 요소에 값을 넣어준다.
fun settingInputForm(){
// 이미지뷰를 안보이게 한다.
fragmentReadContentBinding.imageViewReadContent.visibility = View.INVISIBLE
// 입력 요소에 띄어쓰기를 넣어준다.
readContentViewModel.textFieldReadContentSubject.value = " "
readContentViewModel.textFieldReadContentText.value = " "
readContentViewModel.textFieldReadContentDate.value = " "
readContentViewModel.textFieldReadContentType.value = " "
readContentViewModel.textFieldReadContentNickName.value = " "
CoroutineScope(Dispatchers.Main).launch {
// 현재 글 번호에 해당하는 글 데이터를 가져온다.
val contentModel = ContentDao.selectContentData(contentIdx)
// 글을 작성한 사용자의 번호를 통해 사용자 정보를 가져온다.
val userModel = UserDao.gettingUserInfoByUserIdx(contentModel?.contentWriterIdx!!)
// 가져온 데이터를 보여준다.
readContentViewModel.textFieldReadContentSubject.value = contentModel?.contentSubject
readContentViewModel.textFieldReadContentType.value = when(contentModel?.contentType){
ContentType.TYPE_FREE.number -> ContentType.TYPE_FREE.str
ContentType.TYPE_HUMOR.number -> ContentType.TYPE_HUMOR.str
ContentType.TYPE_SOCIETY.number -> ContentType.TYPE_SOCIETY.str
ContentType.TYPE_SPORTS.number -> ContentType.TYPE_SPORTS.str
else -> ""
}
readContentViewModel.textFieldReadContentNickName.value = userModel?.userNickName
readContentViewModel.textFieldReadContentDate.value = contentModel?.contentWriteDate
readContentViewModel.textFieldReadContentText.value = contentModel?.contentText
// 이미지 데이터를 불러온다.
if(contentModel?.contentImage != null) {
delay(1000L)
ContentDao.gettingContentImage(contentActivity, contentModel.contentImage!!, fragmentReadContentBinding.imageViewReadContent)
}
}
}
이렇게 delay를 주는게 맞는건지(또는 업로드 메서드에서 딜레이를 줘야하는건지?) 아니면 원래의 코드에서 뭔가 빼먹었거나 실수한 부분이 있었는지
혹시 아시는 분이 있으시다면 댓글주시면 감사하겠습니다 ㅠㅠ
(try-catch 예외처리는 논외로 하겠습니다;;)