[트러블슈팅] BOM 쓰면 버전 끝나냐

Thirfir·2025년 4월 24일
post-thumbnail

💥 TL;DR:

Compose BOM을 쓰더라도, 최신 버전이 우선적으로 채택된다.

프로젝트에 build-logic 모듈을 적용하여 이전의 복잡한 의존성 관리 체계를 갈아엎었다. 그런데 팀원으로부터 제보가 들어왔다.
런타임 에러가 발생했다는 것.
java.lang.NoSuchMethodError: No static method FlowRow...

NoSuchMethod…? 메서드가 없다고..? 코딩할 때 문제 없었는데 뭔소리??

https://community.intercom.com/mobile-sdks-24/java-lang-nosuchmethoderror-no-static-method-flowrow-9891
다행히 같은 에러를 겪은 사람들이 많았다.
간단히 요약하면 1.8.0 이상의 최신 Compose 버전 사용 시 위 에러가 발생한다는 것...

따라서 해결책은 그저 FlowRow가 포함된 foundation-layout 라이브러리의 버전을 1.8.0 아래 버전으로 낮추면 되는 것이었다.

NoSuchMethodError?

우선 이게 무엇인지 알아볼 필요가 있었다. 코딩할 때 문제없고, 컴파일도 문제없었는데, 뭐가 문제였을까?

NoSuchMethodError는 말 그대로 "존재하지 않는 메서드를 호출하려고 했을 때" 발생하는 런타임 에러다.
보통 아래의 상황에 발생한다.

컴파일 타임에는 해당 메서드가 있는 줄 알고 성공함. 하지만 실제 앱 실행 시 로딩된 클래스에는 해당 메서드가 없음

즉, 코딩할 때도, 빌드할 때도 문제없이 넘어가지만, 실제 앱이 실행되면서 그 메서드를 찾다가 못 찾으면 터진다.

간단히 생각해서, 내가 코딩할 때 사용한 메서드 (내 경우엔 FlowRow)와 런타임에 채택된 메서드의 형태가 다르다는 것. (또는 메서드가 아예 없어졌거나)

다시 말해, 코딩할 때는 1.8.0 아래 버전의 라이브러리가 사용되다가, 런타임에는 1.8.0 이상의 라이브러리가 사용되었다는 것이다.

난 BOM을 쓰고 있는데...?

아직 뭔지 잘 모르겠어서 확실히 버전을 낮추기 위해 버전 카탈로그를 다시 봤더니 뭔가 이상하다.

build-logic을 적용하면서, 복잡하게 흩어진 버전들을 깔끔하게 관리하기 위해 BOM(Bill Of Materials)을 적용했었다.

[versions]
...
composeBom = "2025.03.01"

[libraries]
...
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" } // BOM
androidx-compose-animation = { group = "androidx.compose.animation", name = "animation" }
androidx-compose-animation-core = { group = "androidx.compose.animation", name = "animation-core" }
androidx-compose-animation-graphics = { group = "androidx.compose.animation", name = "animation-graphics" }
androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation" }
androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime-android" }
compose-material3 = { group = "androidx.compose.material3", name = "material3" }
BOM을 사용하면 버전을 직접 명시하지 않아도 적절한 버전의 조합을 알아서 적용해준다.

처음엔 생각했다. "2025.03.01 BOM 버전이 foundation-layout을 1.8.0 이상으로 제공하는구나!"
그래서 확인해봤더니...?

https://developer.android.com/develop/ui/compose/bom/bom-mapping?hl=ko

BOM-2025.03.01foundation-layout 버전을 1.7.8로 제공하고 있었다.
이상하다. 분명 1.8.0 이상에서 발생하는 버그라고 했는데...?

잘 모르겠다. 일단 간단한 거 부터 해보자.

  1. foundation-layout만을 1.7.8 버전으로 고정시키기
androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout", version = "1.7.8" }
  1. compose BOM 버전 더 낮추기
composeBom = 2024.02.00

하지만 결과는 같았다.
java.lang.NoSuchMethodError: No static method FlowRow

버전

프로젝트를 한 번 살펴보다가 이런 걸 발견했다.

foundation-layout에 1.8.0-alpha04 버전이 들어가 있었다.

뭐야... 1.8.0이 왜 있어...

1.8.0 이상 버전이 들어가 있는건지 없는건지... 하던 의심이 확신으로 바뀌는 순간이었다.

너 뭐니

왜 내가 넣지도 않은 1.8.0-alpha04 버전이 프로젝트에 포함되어 있는가?

./gradlew :app:dependencies --configuration releaseRuntimeClasspath --info
안드로이드 스튜디오 터미널에서 위 명령어를 실행하면 프로젝트 내의 라이브러리들이 어떤 의존 관계를 가지고 있는지 확인할 수 있다. 그러면 터미널 상에서 아래와 같은 의존성 트리를 보여준다.

라이브러리들의 의존성들이 보인다.

나의 목적이었던 foundation-layout을 찾아보면, 다양한 라이브러리들에서 foundation-layout을 의존하고 있다.

위의 이미지 상에서 2개의 foundation-layout을 찾을 수 있었는데, 각각 아래의 사실을 나타냈다.
1. animation-graphics-android(버전 1.8.0-alpha04)foundation-layout(버전 1.8.0-alpha04) 를 의존함
2. compose-bom:2025.03.01에 의해 foundation-layout의 버전이 1.7.8로 명시되었으나 실제로는 1.8.0-alpha04 버전이 채택됨

정리하면, foundation-layout은 내가 직접 명시한 것 외에도 다른 라이브러리들에서 의존하고 있고, 최종 버전은 1.8.0-alpha04라는 것이었다. (->기호는 최종적으로 채택된 버전을 나타낸다)


Gradle이 버전을 선택하는 방법

어쩌다 1.8.0-alpha04 버전으로 결정되었을까?

Gradle은 같은 라이브러리가 여러 번 선언되었을 때, 가장 최신 버전을 채택한다고 한다.

즉, 나는 프로젝트 상에서 BOM으로 버전을 결정하고자 하였으나, 다른 라이브러리에 의해 가장 최신 버전인 1.8.0-alpha04가 채택되었던 것이다.

너 어디서 왔니

이제 다음 단계는 어떤 라이브러리에 의해 1.8.0-alpha04가 채택되었는지 찾는 것이었다.
위에서 활용한 의존성 트리를 더 살펴보았다.

Compose 라이브러리들을 위주로 의존 관계를 살펴보았고, 최종적으로 이것을 발견할 수 있었다.

네이버 지도를 사용하기 위해 포함시켰던 naver-map-compose 라이브러리가 foundation:1.8.0-alpha04를 의존하고 있었다! (foundationfoundation-layout을 의존하는데, 뒤에 있는 (*) 기호는 앞에 내용과 중복되어 하위 트리 내용이 생략되었음을 의미한다.)

해결

naver-map-compose의 버전을 확인해보고자 깃허브에서 확인해보았다.

https://github.com/fornewid/naver-map-compose?tab=readme-ov-file

naver-map-compose:1.8.0Compose 1.8.0 버전을 사용한다고 한다.

결국 naver-map-compose의 버전을 1.8.0으로 명시했던 것이 최종 원인이었다.
build-logic을 적용하면서 네이버 지도 라이브러리 버전을 아무 생각없이 최신 버전으로 올린 것이 문제였던 것이다.

[versions]
...
naverMapCompose="1.7.0"

따라서 네이버 지도의 버전을 낮춰주는 것으로 문제를 해결하였다.

🧑‍💻 또 다른 방법

androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout", version = { strictly = "1.7.8" } }

strictly 라는 키워드를 이용해서 버전을 강제할 수도 있다고 한다. 이렇게 하면 더 최신 버전이 있더라도 foundation-layout은 1.7.8 버전으로 강제된다.
하지만 다른 라이브러리들과의 관계를 생각했을 때 비교적 덜 안정적인 방식인 것 같다.

BOM은 만능이 아니다!!

BOM만 적용하면 버전은 신경쓸 필요 없다고 생각했는데.... 아니었다.
BOM은 이 프로젝트에서 전혀 활용되지 않고 있었던 것이다.

역시 버전 관리는 중요하다... 버전 막 건드리지 말아야지..

profile
안드로이드 개발자입니다.

0개의 댓글