All com.android.support 버전 충돌 경고의 근본 원인과 해결책

안드로이드 개발자라면 누구나 한 번쯤은 마주쳤을 법한 붉은색 경고 메시지, "All com.android.support libraries must use the exact same version specification". 특히 페이스북 SDK나 Firebase 같은 대규모 서드파티 라이브러리를 추가하는 순간, 이 경고는 어김없이 우리를 찾아와 빌드를 가로막곤 합니다. 이 메시지는 단순히 '버전을 맞추라'는 표면적인 의미를 넘어, 안드로이드 빌드 시스템인 Gradle의 의존성 관리 메커니즘과 깊은 관련이 있습니다.

저는 풀스택 개발자로서 이 문제를 수없이 겪으며, 단순히 코드를 복사 붙여넣기 하는 임시방편적인 해결을 넘어 그 근본 원인을 파헤치는 것이 얼마나 중요한지 깨달았습니다. 이 글은 단순히 경고를 없애는 방법을 넘어, 왜 이런 문제가 발생하는지, Gradle은 내부적으로 어떻게 동작하는지, 그리고 다시는 이런 문제로 고통받지 않기 위한 최선의 예방책은 무엇인지에 대한 깊이 있는 통찰을 공유하고자 합니다. 이 글을 끝까지 읽으신다면, 당신은 더 이상 의존성 충돌을 두려워하지 않는 한 단계 성장한 안드로이드 개발자가 될 것입니다.

이 글에서 다룰 내용:

  • 'exact same version' 경고가 발생하는 진짜 이유: Gradle 전이 의존성(Transitive Dependency) 심층 분석
  • 가장 빠른 해결책: Support Library 버전 강제 통일 방법
  • 가장 확실한 해결책: 왜 AndroidX로 마이그레이션해야 하는가?
  • 전문가를 위한 고급 해결책: Gradle 설정으로 의존성 제어하기
  • 미래를 위한 최선의 선택: Version Catalog를 이용한 똑똑한 의존성 관리 전략

경고의 실체: Gradle의 전이 의존성과 버전 충돌

문제 해결의 첫걸음은 원인을 정확히 이해하는 것입니다. 이 경고 메시지는 Gradle이 우리 프로젝트의 '의존성 트리(Dependency Tree)'를 분석하다가 모순을 발견했을 때 발생합니다. 여기서 핵심 개념은 바로 전이 의존성(Transitive Dependency)입니다.

전이 의존성이란 무엇인가?

우리가 build.gradle 파일에 implementation 'com.facebook.android:facebook-android-sdk:12.3.0' 와 같이 한 줄의 코드를 추가하면, 우리는 페이스북 SDK 라이브러리 하나만 추가한다고 생각하기 쉽습니다. 하지만 실제로는 그렇지 않습니다. 페이스북 SDK 라이브러리 역시 정상적으로 동작하기 위해 다른 여러 라이브러리를 필요로 합니다. 예를 들어, UI를 그리기 위해 cardview-v7 라이브러리가 필요할 수 있고, 크롬 커스텀 탭을 사용하기 위해 customtabs 라이브러리가 필요할 수 있습니다.

이렇게 내가 추가한 라이브러리(A)가 의존하는 또 다른 라이브러리(B), 그리고 그 라이브러리(B)가 의존하는 라이브러리(C)처럼, 꼬리에 꼬리를 물고 이어지는 의존 관계를 '전이 의존성'이라고 부릅니다. Gradle은 이 모든 관계를 파악하여 필요한 모든 라이브러리를 자동으로 다운로드하고 프로젝트에 포함시켜 줍니다. 이는 매우 편리한 기능이지만, 바로 이 지점에서 버전 충돌의 씨앗이 싹트게 됩니다.

예를 들어, 우리 프로젝트의 의존성 구조가 다음과 같다고 가정해 봅시다.


내 앱(My App)
├── implementation 'com.android.support:appcompat-v7:28.0.0'
└── implementation 'com.facebook.android:facebook-android-sdk:5.15.3'
    └── (전이 의존성) com.android.support:cardview-v7:27.1.1
    └── (전이 의존성) com.android.support:customtabs:27.1.1
        └── (전이 의존성) com.android.support:support-compat:27.1.1

위 구조에서 Gradle은 두 가지 버전의 Support Library를 발견하게 됩니다.

  1. 내 앱이 직접 요구하는 appcompat-v7의 버전: 28.0.0
  2. 페이스북 SDK를 통해 간접적으로 요구되는 cardview-v7, customtabs, support-compat 등의 버전: 27.1.1

바로 이 지점에서 "All com.android.support libraries must use the exact same version specification" 경고가 발생하는 것입니다. 구글은 com.android.support 라이브러리 패키지 그룹이 내부적으로 매우 긴밀하게 연결되어 있어, 서로 다른 버전이 섞여 사용될 경우 예측 불가능한 런타임 오류(예: NoSuchMethodError)를 일으킬 수 있기 때문에, Gradle 빌드 과정에서 이를 엄격하게 금지하도록 만든 것입니다.

Gradle의 기본 버전 해결 전략: 일반적으로 Gradle은 동일한 라이브러리의 여러 버전이 요청되면, 특별한 설정이 없는 한 가장 높은 버전을 선택하여 사용합니다. 하지만 com.android.support 라이브러리 그룹에 대해서는 이 규칙 대신 '모든 버전이 동일해야 한다'는 더 엄격한 규칙을 적용하는 것입니다.

A Full-Stack Developer's Note

이러한 내부 동작 원리를 이해했다면, 이제 해결책은 명확해집니다. 프로젝트 전체의 의존성 트리에서 사용되는 모든 com.android.support 라이브러리의 버전을 하나로 통일시켜주면 됩니다.

해결 방안 1: Support Library 버전 명시적 통일 (단기 처방)

가장 빠르고 직관적인 해결 방법은 문제가 되는 라이브러리들을 우리 프로젝트의 build.gradle (Module: app) 파일에 직접 명시하여 버전을 강제로 지정하는 것입니다. 이는 마치 Gradle에게 "네가 어떤 버전을 발견했든 상관없이, 이 라이브러리에 대해서는 무조건 내가 지정한 이 버전을 사용해!"라고 명령하는 것과 같습니다.

단계별 해결 과정

  1. 기준 버전 확인하기: 가장 먼저 우리 프로젝트의 기준이 되는 Support Library 버전을 확인해야 합니다. 보통은 com.android.support:appcompat-v7의 버전을 기준으로 삼습니다. build.gradle (Module: app) 파일을 열어 해당 라인을 찾습니다.
    
    dependencies {
        // ...
        implementation 'com.android.support:appcompat-v7:28.0.0' // 기준 버전은 28.0.0
        // ...
    }
        
  2. 충돌 라이브러리 버전 통일하기: 페이스북 SDK가 주로 의존하는 cardview-v7이나 customtabs 같은 라이브러리들을 위에서 확인한 기준 버전(28.0.0)으로 명시적으로 추가해줍니다.
    
    dependencies {
        // ... 기존 의존성들 ...
        implementation 'com.android.support:appcompat-v7:28.0.0'
    
        // 페이스북 SDK 의존성
        implementation 'com.facebook.android:facebook-android-sdk:[5,6)'
    
        // 버전 충돌 해결을 위해 명시적으로 추가 (appcompat-v7 버전과 동일하게)
        implementation 'com.android.support:cardview-v7:28.0.0'
        implementation 'com.android.support:customtabs:28.0.0'
        implementation 'com.android.support:support-v4:28.0.0' // 경우에 따라 필요할 수 있음
    
        // ... 기타 의존성들 ...
    }
        
    주의: 위 코드의 28.0.0 버전은 예시입니다. 반드시 본인 프로젝트의 appcompat-v7 버전에 맞춰서 작성해야 합니다.
  3. Gradle 동기화: 코드를 수정한 후, 안드로이드 스튜디오 우측 상단에 나타나는 "Sync Now"를 클릭하여 변경사항을 프로젝트에 적용합니다. 동기화가 성공적으로 완료되면 경고 메시지가 사라진 것을 확인할 수 있습니다.

의존성 트리 직접 확인하기

어떤 라이브러리가 정확히 어떤 버전으로 충돌하고 있는지 직접 확인하고 싶다면, Gradle의 강력한 디버깅 도구를 사용할 수 있습니다. 안드로이드 스튜디오의 터미널을 열고 다음 명령어를 입력해 보세요.


./gradlew app:dependencies

이 명령어는 app 모듈의 모든 의존성 관계를 트리 형태로 출력해줍니다. 결과가 매우 길기 때문에, 특정 라이브러리를 필터링해서 볼 수 있습니다.


./gradlew app:dependencies --configuration releaseRuntimeClasspath --scan | findstr "com.android.support"

출력된 결과를 보면 ( ... -> 27.1.1) 와 같이 버전이 강제로 변경되는 부분을 확인할 수 있으며, 이를 통해 어떤 라이브러리를 명시적으로 추가해야 할지 정확히 파악할 수 있습니다.

이 방법의 한계

이 방법은 빠르고 간단하지만, 근본적인 해결책은 아닙니다. 새로운 라이브러리를 추가할 때마다 또 다른 버전 충돌이 발생할 수 있으며, 그 때마다 수동으로 의존성을 추가하고 관리해야 합니다. 이는 프로젝트가 커질수록 유지보수를 매우 어렵게 만듭니다. 우리는 이것을 '의존성 지옥(Dependency Hell)'의 초입이라고 부릅니다.

해결 방안 2: AndroidX로의 마이그레이션 (근본적인 해결책)

앞서 설명한 com.android.support 라이브러리 버전 충돌 문제는 안드로이드 개발 생태계의 오랜 골칫거리였습니다. 이 문제를 근본적으로 해결하기 위해 구글은 AndroidX라는 새로운 개념의 라이브러리 패키지를 도입했습니다.

AndroidX란 무엇인가?

AndroidX(Android Extension Library)는 기존의 Support Library를 개선하고 대체하는 차세대 안드로이드 라이브러리입니다. AndroidX의 가장 큰 특징은 다음과 같습니다.

  • 독립적인 버전 관리: 기존 Support Library는 모든 라이브러리가 하나의 버전을 공유해야 했지만(예: 28.0.0), AndroidX에서는 각 라이브러리가 독립적인 시맨틱 버전(Semantic Versioning)을 가집니다. 예를 들어, appcompat1.6.1, recyclerview1.3.2 와 같이 각자의 개발 주기에 맞춰 버전을 올릴 수 있습니다.
  • 명확한 패키지 이름: 모든 라이브러리가 androidx.* 라는 일관된 패키지 이름으로 시작하여, 더 이상 com.android.support와 OS에 포함된 클래스를 혼동할 일이 없습니다.
  • 개선된 유지보수: 새로운 기능은 이제 Support Library가 아닌 AndroidX를 통해서만 제공됩니다. 즉, com.android.support 라이브러리는 버전 28.0.0을 끝으로 개발이 중단된 레거시(legacy)입니다.

결론적으로, 현대적인 안드로이드 앱을 개발한다면 AndroidX를 사용하는 것이 표준이자 필수입니다. 만약 아직도 프로젝트가 com.android.support를 사용하고 있다면, 이번 기회에 마이그레이션하는 것을 강력히 권장합니다.

AndroidX 마이그레이션 방법

안드로이드 스튜디오는 매우 강력한 자동 마이그레이션 도구를 제공합니다. 대부분의 경우 몇 번의 클릭만으로 마이그레이션을 완료할 수 있습니다.

  1. 프로젝트 백업: 마이그레이션은 프로젝트의 많은 파일을 변경하므로, 시작하기 전에 반드시 Git 등을 통해 프로젝트를 백업합니다.
  2. 메뉴 선택: 안드로이드 스튜디오 상단 메뉴에서 Refactor > Migrate to AndroidX... 를 클릭합니다.
  3. 백업 확인 및 시작: 백업 여부를 묻는 창이 나타납니다. 백업했다면 'Migrate' 버튼을 클릭합니다.
  4. 리팩토링 미리보기: 안드로이드 스튜디오가 프로젝트 전체를 분석하여 변경될 파일 목록을 보여줍니다. 하단의 'Do Refactor' 버튼을 클릭하여 마이그레이션을 진행합니다.

마이그레이션이 완료되면, build.gradle 파일의 의존성과 소스코드의 import 구문이 모두 androidx.* 로 변경된 것을 확인할 수 있습니다.

마이그레이션 전/후 `build.gradle` 비교

마이그레이션 전 (Before) 마이그레이션 후 (After)
implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.android.support:recyclerview-v7:28.0.0' implementation 'androidx.recyclerview:recyclerview:1.3.2'
implementation 'com.android.support.constraint:constraint-layout:2.0.4' implementation 'androidx.constraintlayout:constraintlayout:2.1.4'

Jetifier와 라이브러리 호환성

"내가 사용하는 오래된 서드파티 라이브러리가 아직 AndroidX를 지원하지 않으면 어떡하죠?" 라는 걱정을 할 수 있습니다. 걱정하지 마세요. 구글은 Jetifier라는 똑똑한 도구를 함께 제공합니다.

gradle.properties 파일에 다음 두 줄이 추가되면 Jetifier가 활성화됩니다.


android.useAndroidX=true
android.enableJetifier=true

Jetifier는 빌드 과정에서 아직 com.android.support를 사용하는 라이브러리의 바이트코드를 스캔하여, 이를 동적으로 androidx를 사용하도록 수정해줍니다. 따라서 대부분의 레거시 라이브러리도 별다른 문제 없이 AndroidX 프로젝트에서 함께 사용할 수 있습니다. 페이스북 SDK 최신 버전은 이미 AndroidX를 완벽하게 지원하므로, 마이그레이션 후에는 버전 충돌 경고가 마법처럼 사라질 것입니다.

해결 방안 3: 고급 Gradle 설정 (전문가를 위한 외과수술)

여러 라이브러리가 복잡하게 얽혀있거나, 특정 라이브러리를 제외해야 하는 등 매우 특수한 상황에서는 Gradle 설정을 직접 수정하여 의존성을 보다 정교하게 제어할 수 있습니다. 이는 강력한 기능이지만, 잘못 사용하면 프로젝트를 더 복잡하게 만들 수 있으므로 원리를 정확히 이해하고 사용해야 합니다.

`configurations.all` 로 버전 강제하기

프로젝트 전체에서 특정 라이브러리 그룹의 버전을 강제로 통일하고 싶을 때 사용할 수 있는 방법입니다. build.gradle (Module: app) 파일에 다음과 같은 코드를 추가할 수 있습니다.


configurations.all {
    resolutionStrategy {
        force "com.android.support:support-v4:28.0.0"
        force "com.android.support:appcompat-v7:28.0.0"
        // ... 필요한 다른 support 라이브러리들
    }
}

이 설정은 Gradle의 버전 해결 전략(resolutionStrategy)에 직접 개입하여, com.android.support:support-v4 라이브러리에 대한 어떤 버전 요청이 들어오든 무시하고 무조건 28.0.0 버전을 사용하도록 강제합니다. 이는 개별적으로 implementation을 추가하는 것보다 더 강력하고 포괄적인 방법입니다.

`exclude`로 특정 전이 의존성 제외하기

특정 라이브러리가 포함하는 전이 의존성이 문제를 일으키거나, 다른 라이브러리로 대체하고 싶을 때 exclude 키워드를 사용할 수 있습니다.


dependencies {
    implementation('com.some.library:library-abc:1.0.0') {
        // library-abc가 내부적으로 포함하는 support-v4를 제외함
        exclude group: 'com.android.support', module: 'support-v4'
    }
}

위 코드는 library-abc를 프로젝트에 추가하되, 이 라이브러리가 전이 의존성으로 가져오는 support-v4는 포함시키지 않도록 설정합니다. 이 방법은 매우 정밀한 제어가 필요할 때 유용하지만, 제외된 라이브러리로 인해 library-abc가 오작동할 수 있으므로 주의 깊게 사용해야 합니다.

해결 방안 비교 분석

지금까지 살펴본 세 가지 해결 방안을 표로 비교해 보겠습니다.

항목 버전 명시적 통일 AndroidX 마이그레이션 고급 Gradle 설정
구현 난이도 낮음 (코드 몇 줄 추가) 중간 (자동화 도구 사용, 간혹 수동 수정 필요) 높음 (Gradle에 대한 깊은 이해 필요)
유지보수성 나쁨 (문제가 생길 때마다 수동 대응 필요) 매우 좋음 (근본적인 문제 해결) 보통 (설정이 복잡해져 관리가 어려울 수 있음)
권장 여부 비권장 (임시방편) 강력 권장 (현대 안드로이드 개발 표준) 특수한 경우에만 제한적으로 사용
핵심 개념 의존성 덮어쓰기(Override) 생태계 전환(Ecosystem Shift) 규칙 기반 제어(Rule-based Control)

예방이 최선: 건강한 의존성 관리 전략

풀스택 개발자로서 저는 항상 '치료보다 예방'이 중요하다고 생각합니다. 의존성 충돌이 발생한 후에 해결하는 것도 중요하지만, 처음부터 충돌이 발생하기 어려운 구조로 프로젝트를 설계하는 것이 훨씬 더 효율적입니다. 최신 Gradle은 이를 위한 강력한 기능들을 제공합니다.

Version Catalog (libs.versions.toml) 사용하기

Gradle 7.0부터 공식적으로 도입된 Version Catalog는 의존성 관리를 위한 최상의 방법(Best Practice)입니다. 이는 모든 의존성 라이브러리의 버전 정보를 한 곳에서 중앙 관리할 수 있도록 해줍니다.

프로젝트 루트의 gradle 폴더 아래에 libs.versions.toml 파일을 생성하고 다음과 같이 작성합니다.


# gradle/libs.versions.toml

[versions]
# 라이브러리 그룹의 버전을 정의
androidx-appcompat = "1.6.1"
google-material = "1.11.0"
facebook-sdk = "16.3.0"

[libraries]
# 버전을 참조하여 라이브러리의 별칭을 정의
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidx-appcompat" }
google-material = { group = "com.google.android.material", name = "material", version.ref = "google-material" }
facebook-sdk = { module = "com.facebook.android:facebook-android-sdk", version.ref = "facebook-sdk" }

[bundles]
# 여러 라이브러리를 묶어서 관리
# 예: ui = ["androidx-appcompat", "google-material"]

이제 build.gradle (Module: app) 파일에서는 다음과 같이 타입-세이프(type-safe)하고 간결하게 의존성을 추가할 수 있습니다.


// build.gradle (Module: app)

dependencies {
    implementation libs.androidx.appcompat
    implementation libs.google.material
    implementation libs.facebook.sdk

    // 묶음으로 추가
    // implementation libs.bundles.ui
}

Version Catalog를 사용하면 다음과 같은 엄청난 이점을 얻을 수 있습니다.

  • 중앙 관리: 모든 버전 정보가 .toml 파일 한 곳에 모여있어 관리가 쉽고 충돌 가능성이 줄어듭니다.
  • 타입 안정성: 안드로이드 스튜디오의 자동 완성을 지원하여 오타를 방지하고 개발 생산성을 높입니다.
  • 모듈 간 공유: 멀티 모듈 프로젝트에서 모든 모듈이 동일한 버전의 라이브러리를 사용하도록 강제하기 매우 용이합니다.

지금 바로 새 프로젝트를 시작하거나 기존 프로젝트를 리팩토링한다면, Version Catalog 도입을 최우선으로 고려하세요. 이는 당신의 프로젝트를 의존성 지옥으로부터 구해줄 가장 확실한 방법입니다.

결론: 의존성 충돌을 넘어 성장으로

"All com.android.support libraries must use the exact same version specification". 이 단순해 보이는 경고 메시지 안에는 Gradle의 전이 의존성, Support Library의 역사, 그리고 AndroidX로의 패러다임 전환이라는 안드로이드 개발의 중요한 흐름이 담겨 있습니다.

우리는 이 문제를 해결하기 위해 다음의 여정을 거쳤습니다.

  1. 원인 파악: 전이 의존성으로 인해 서로 다른 버전의 Support Library가 충돌하는 것이 근본 원인임을 이해했습니다.
  2. 단기 해결: build.gradle에 직접 버전을 명시하여 급한 불을 끄는 방법을 배웠습니다.
  3. 근본 해결: 레거시 Support Library를 버리고, 현대적인 AndroidX로 마이그레이션하는 것이 최선의 해결책임을 확인했습니다.
  4. 예방 전략: Version Catalog를 통해 처음부터 건강하고 유지보수하기 쉬운 의존성 관리 구조를 만드는 방법을 습득했습니다.

이제 당신은 이 경고를 다시 마주했을 때, 단순히 인터넷에서 해결 코드를 찾아 복사하는 대신, 문제의 본질을 꿰뚫어 보고 당신의 프로젝트에 가장 적합한 최선의 해결책을 자신 있게 선택할 수 있을 것입니다. 의존성 관리 능력은 앱의 안정성과 직결되는 핵심 기술입니다. 오늘 얻은 깊이 있는 지식이 당신의 개발 여정에 튼튼한 발판이 되기를 바랍니다.

Post a Comment