1. 의존성(Dependency)이란 대체 뭔가 - 레고 블록의 악몽
의존성을 가장 쉽게 설명하자면 "내 앱이 작동하기 위해 필요한 다른 사람이 만든 코드 조각들"입니다. 마치 레고 블록처럼 여러 조각을 조립해서 앱을 만드는데, 문제는 이 블록들이 서로 맞지 않는 경우가 너무 많다는 것이죠.
예를 들어, 카메라 기능을 구현하려면:
이렇게 꼬리에 꼬리를 무는 것이 의존성입니다.
- 직접 의존성 vs 간접 의존성 내가 직접 추가한 라이브러리는 '직접 의존성'입니다. 하지만 그 라이브러리가 또 다른 라이브러리를 필요로 하는 '간접 의존성'이 진짜 문제입니다.
- 의존성 충돌의 시작 A 라이브러리는 C 라이브러리 버전 1.0을 요구하고, B 라이브러리는 C 라이브러리 버전 2.0을 요구한다면? 빌드는 실패하고 여러분은 머리를 쥐어뜯게 됩니다.
- 왜 이런 시스템을 만들었나 아이러니하게도 의존성 시스템은 개발을 '쉽게' 하려고 만들어졌습니다. 모든 코드를 처음부터 짜는 대신, 남이 만든 검증된 코드를 가져다 쓰자는 좋은 의도였죠. 하지만 현실은...
2. 안드로이드 버전 파편화 - 지옥의 시작
안드로이드의 가장 큰 저주는 '파편화(Fragmentation)'입니다. iOS와 달리 안드로이드는 수천 개의 제조사, 수만 개의 기기 모델, 그리고 Android 4.4부터 14까지 모든 버전이 여전히 사용되고 있습니다.
- API 레벨의 악몽 Android 5.0(API 21)에서 추가된 기능을 쓰고 싶은데, 아직도 4.4를 쓰는 사용자가 있다면? 조건문으로 버전을 체크하고 다른 코드를 작성해야 합니다.
- 제조사별 커스터마이징 삼성, LG, 샤오미... 각 제조사가 안드로이드를 마음대로 수정합니다. 같은 Android 12라도 제조사마다 동작이 다를 수 있습니다.
- 하위 호환성의 딜레마 구글은 하위 호환성을 포기할 수 없습니다. 10년 전 기기도 지원해야 하니, 새로운 기능을 추가할 때마다 복잡도가 기하급수적으로 증가합니다.
3. Gradle과 빌드 시스템 - 필요악의 대명사
Gradle은 안드로이드의 빌드 시스템입니다. 의존성을 관리하고, 코드를 컴파일하고, APK를 만드는 모든 과정을 담당하죠. 하지만 이게 또 골칫거리입니다.
Gradle이 하는 일들:
- Gradle 버전 자체의 문제 Gradle도 버전이 있고, Android Gradle Plugin도 버전이 있습니다. 이 둘의 호환성도 맞춰야 합니다. Gradle 7.0은 AGP 7.0과 맞지만, Gradle 6.0과는 안 맞습니다.
- 빌드 캐시의 저주 "Clean Project 하고 다시 빌드하세요"라는 조언을 들어보셨나요? Gradle 캐시가 꼬이면 이상한 오류가 발생합니다. 캐시를 지우면 해결되지만, 다시 빌드하는데 30분...
- 의존성 해결 알고리즘 Gradle은 복잡한 알고리즘으로 버전 충돌을 해결하려 합니다. 하지만 때로는 잘못된 선택을 하고, 런타임에 크래시가 발생합니다.
4. 실제 발생하는 버전 충돌 사례들 - 현실의 고통
실제로 안드로이드 개발하면서 마주치는 대표적인 버전 충돌 사례들을 살펴봅시다.
사례 1: AndroidX 마이그레이션 지옥
Error: Manifest merger failed : Attribute application@appComponentFactory value=(android.support.v4.app.CoreComponentFactory)구버전 Support Library와 새로운 AndroidX가 충돌하는 전형적인 예입니다.
사례 2: Kotlin 버전 충돌
Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.8.0, expected version is 1.6.0라이브러리가 더 높은 Kotlin 버전으로 컴파일되어 있을 때 발생합니다.
사례 3: Firebase의 악몽
Please fix the version conflict either by updating the version of the google-services plugin or updating the version of com.google.android.gmsFirebase와 Google Play Services 버전이 맞지 않을 때의 고통입니다.
- Jetpack Compose 도입의 함정 Compose를 쓰려면 Kotlin 버전, Gradle 버전, 심지어 Android Studio 버전까지 모두 맞춰야 합니다. 하나라도 틀리면 빌드 실패.
- 네이티브 라이브러리 충돌.so 파일이 중복되거나 아키텍처가 맞지 않으면 런타임 크래시. 디버깅도 어렵습니다.
- ProGuard/R8 규칙 충돌 각 라이브러리마다 난독화 규칙이 있는데, 이게 충돌하면 릴리즈 빌드만 크래시가 발생합니다.
5. 왜 이런 시스템이 생겼나 - 역사적 배경
이 모든 고통의 역사적 배경을 이해하면 조금은 납득이 될 수도 있습니다(화는 여전히 나지만).
- 오픈소스 생태계의 성장 안드로이드는 오픈소스를 기반으로 빠르게 성장했습니다. 누구나 라이브러리를 만들고 공유할 수 있었죠. 하지만 통제되지 않은 성장은 혼돈을 가져왔습니다.
- 구글의 전략 변경 Support Library → AndroidX로의 전환, Eclipse → Android Studio로의 이동 등 구글의 큰 방향 전환마다 개발자들은 고통받았습니다.
- 자바에서 코틀린으로 코틀린 도입은 좋았지만, 또 다른 버전 관리 지옥을 만들었습니다. Java 8, 11, 17... Kotlin 1.4, 1.5, 1.6, 1.7, 1.8, 1.9...
6. 버전 맞추기 실전 가이드 - 생존 전략
이제 실제로 버전 충돌을 해결하는 방법을 알아봅시다. 이건 정말 경험에서 나온 피눈물의 노하우입니다.
- 버전 카탈로그 사용하기
- 의존성 트리 분석
- 강제 버전 지정
- exclude 전략
충돌하는 의존성을 제외시킬 수 있습니다.
7. 개발 환경 세팅의 베스트 프랙티스
버전 지옥을 최소화하는 개발 환경 세팅 방법입니다.
- 버전 고정 원칙
- 정기적인 업데이트 전략
- 로컬 환경 격리
- CI/CD 활용 GitHub Actions나 Jenkins에서 클린 환경 빌드를 돌려보세요. 로컬에서만 되는 빌드를 방지할 수 있습니다.
8. 미래는 나아질까? - 희망과 절망
구글도 이 문제를 인지하고 개선하려 노력하고 있습니다.
- Version Catalog의 도입 Gradle 7.0부터 도입된 버전 카탈로그는 의존성 관리를 훨씬 쉽게 만들었습니다.
- Gradle의 개선 빌드 속도 향상, 더 나은 캐싱, 명확한 에러 메시지 등 계속 개선되고 있습니다.
- BOM(Bill of Materials) 도입
BOM을 사용하면 호환되는 버전을 자동으로 선택합니다.
하지만 여전히 갈 길은 멉니다. 안드로이드의 파편화는 계속될 것이고, 새로운 기술(Compose Multiplatform 등)은 또 다른 복잡성을 추가할 것입니다.