Skip to content

Naver_Maps와_Coil에서의_Marker_이미지_로딩_이슈_해결

yujin45 edited this page Nov 18, 2024 · 1 revision

💡 문제 정의

  • Naver Maps API를 사용해 Room DB에 저장된 여러 개의 이미지를 지도에 마커로 표시하려 했으나, Coil을 사용한 이미지 로딩이 제대로 작동하지 않는 문제가 발생했다. Coil 로딩 상태에서 DeferredCoroutine{Cancelled} 메시지가 반복적으로 출력되며, 로딩이 취소되는 현상이 나타났다. 그 결과, Marker에 설정된 OverlayImage가 이미지 로딩 전 상태의 View를 사용하면서 원하는 이미지가 정상적으로 표시되지 않았다.

💡 문제 원인 파악 과정

✅ 초기 시도

  • 문제 상황 인지: 처음에는 Coil로 로딩된 이미지를 MapView에 Marker로 추가하기 위해 Coil을 사용해 ImageView에 로드한 후, 이 ImageView를 OverlayImage.fromView()를 통해 Marker에 적용했다. 그러나 Coil이 이미지 로딩을 시작하지 않고, DeferredCoroutine{Cancelled} 메시지만 출력되었다.
  • 로그 확인: Coil의 ImageRequest.Builder에 리스너를 추가하고 로그를 통해 onStartonSuccess 등의 상태가 호출되지 않는 것을 확인했다. 이를 통해 Coil 요청이 정상적으로 작동하지 않고 취소되는 것을 파악했다.

image

  • Coil load의 listener에서 onStart자체가 실행되지 않음

image

  • ▲ Log 출력 결과 job이 Cancelled 되는 것을 확인

image image

  • ▲ MapView의 Lifecycle은 정상 작동하는 것 확인

✅ 상세 원인 파악을 위한 추가 시도

  1. Composable을 사용해 이미지 로드 테스트:

    image

    • 시도: Coil로 이미지를 로드한 후 ComposeView에 마커를 설정하는 방식을 고려해, Composable에서 이미지를 표시해보고자 했다.
    • 결과: MapView를 사용하지 않고 Composable만 사용할 때는 Coil이 정상적으로 이미지를 로드했다. 그러나 MapView와 함께 사용하는 경우에는 로딩 문제가 여전히 발생했다.
    • 분석: 문제는 MapView와 함께 사용될 때 발생하는 것으로 보였으며, View 생명주기와 관련이 있을 것으로 추정되었다.
  2. MapView 대신 Fragment를 사용하여 마커 표시 시도:

    • 시도: MapView 대신 Fragment를 사용해 ImageView를 포함한 커스텀 마커 뷰를 Marker의 OverlayImage로 설정했다.

    • 결과: 커스텀 마커 뷰를 사용하여도 이미지가 정상적으로 로드되지 않고 Coil의 로딩이 취소되는 문제가 발생했다. 로그를 통해 Coil의 ImageRequest가 시작되지 않는 것을 확인했다.

    • 추가 분석: 이로 인해 Attach 상태에서 Coil 로딩이 문제를 일으킬 수 있다는 점을 의심하게 되었다. Coil 로딩을 위해 View가 Attach 상태에 있어야 함을 확인한 후, Attach 상태에서만 로딩을 시도해야 함을 알게 되었다. ⭐

          // Coil 관련
          override fun assertActive() {
              if (!target.view.isAttachedToWindow) {
                  target.view.requestManager.setRequest(this)
                  throw CancellationException("'ViewTarget.view' must be attached to a window.")
              }
          }
  3. LifecycleObserver와 DisposableEffect 활용 테스트:

    • 시도: MapView의 생명주기를 Compose의 DisposableEffectLifecycleObserver로 직접 관리하면서 ImageView를 추가하고, Marker가 올바르게 표시되는지 테스트했다.
    • 결과: MapView 자체의 생명주기는 정상적으로 관리되었지만, 여전히 ImageView에서 Coil 로딩이 취소되었다.
    • 분석: MapView는 정상적으로 동작했으나, Coil이 ImageView를 Attach된 상태에서 처리하지 못하고 취소되는 문제가 계속되었다. 이를 통해 Attach 상태가 Coil의 요청 활성화에 필수적임을 재차 확인했다.
  4. ViewTreeObserver를 통한 Attach 상태 확인 후 Marker에 추가:

    • 시도: ViewTreeObserver를 사용해 ImageView가 Window에 완전히 Attach된 후 Coil 로딩을 시작하고, Coil 로딩이 완료된 후 Marker에 추가되도록 작업 순서를 조정했다.
    • 결과: View가 Attach된 이후 Coil 로딩을 시작하면서 Coil의 이미지 로딩이 정상적으로 완료되었고, 이를 Marker의 OverlayImage로 추가할 수 있었다. 최종적으로 이미지가 정상적으로 Marker에 표시되었다.
    • 분석: ImageView가 Window에 완전히 Attach된 이후에 Coil이 활성화되어 로딩을 성공적으로 수행하는 것을 확인했다. 따라서, View의 Attach 상태가 이미지 로딩의 성패를 좌우하는 주요 원인임을 확인할 수 있었다.
  5. OverlayImage.fromView()의 비동기 처리 한계 파악:

    • 문제 분석: OverlayImage.fromView()는 호출 시점에 View의 현재 상태를 비트맵으로 변환하기 때문에, 이미지가 완전히 로딩되기 전의 View 상태가 비트맵으로 변환될 경우, 예상과 달리 빈 View가 Marker에 표시되는 현상이 발생했다.
    • 해결책: Coil 로딩이 완료된 후, 완전한 이미지 상태의 View를 Marker에 적용하는 방식으로 수정했다.

💡 문제 원인 정리

  1. ImageView의 Attach 상태: Coil의 ImageLoader는 View가 Attach된 상태에서만 활성화되어 로딩을 시작할 수 있다. Attach되지 않은 View에서는 Coil 로딩이 취소되어 DeferredCoroutine{Cancelled} 메시지가 출력되었다.
  2. OverlayImage.fromView()의 비동기 처리 한계: OverlayImage.fromView()는 호출 시점의 View 상태를 바로 비트맵으로 변환하기 때문에, View의 이미지가 완전히 로딩되기 전에 호출될 경우, 빈 View가 비트맵으로 변환되어 Marker에 표시된다.

💡 문제 해결 방법

  1. ImageView가 Attach된 후에 로딩 요청하기: ViewTreeObserver를 통해 ImageView가 Window에 Attach된 후 Coil 로딩을 수행하고, 로딩이 완료된 시점에서 Marker에 추가되도록 작업 순서를 조정했다.
  2. Coil을 사용하여 마커 이미지 로딩: Coil 로딩 과정에서 발생한 취소 문제를 해결하기 위해 ImageView가 Attach된 상태를 확인한 후에만 이미지를 로딩하게 설정했다.

💡 문제 해결 진행

ViewTreeObserver로 Attach 상태 확인 후 Coil 로딩 및 Marker 설정

imageView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
    override fun onGlobalLayout() {
        if (imageView.isAttachedToWindow) {
            val marker = Marker().apply {
                position = LatLng(photoDetail.latitude, photoDetail.longitude)
                icon = OverlayImage.fromView(imageView)
                map = naverMap
            }
            imageView.viewTreeObserver.removeOnGlobalLayoutListener(this)
        }
    }
})

💡 결과

  • 정상적인 이미지 로딩: ImageView가 Attach된 후에 Coil이 이미지를 정상적으로 로딩했고, 이를 Marker의 OverlayImage로 설정할 수 있었다.
  • Marker에 올바른 이미지 표시: Marker에 완전히 로드된 ImageView 상태를 비트맵으로 변환하여 OverlayImage로 적용할 수 있었다.

💡 결론

  • Coil의 ImageLoader는 View가 Window에 Attach된 상태여야 활성화되어 로딩을 시작한다. OverlayImage.fromView()는 호출 시점의 View를 비트맵으로 변환해 Marker에 사용하므로, 이미지가 완전히 로드된 상태의 View를 Overlay로 설정해야 한다. 이를 위해 View가 Attach된 이후 Coil 로딩을 시작하도록 작업 순서를 조정함으로써, 정상적인 이미지 표시가 가능하게 되었다.

💡 관련 PR 링크

위 문제 해결과 관련된 PR 링크는 다음과 같다: https://github.com/boostcampwm-2024/and04-Nature-Album/pull/115

Clone this wiki locally