DI는 왜 유지보수성을 높일까

유진·2025년 12월 21일

Android

목록 보기
15/17
post-thumbnail

안드로이드 개발자 JD를 보면 Hilt를 쉽게 볼 수 있다.
안드로이드 개발을 접한지 얼마 안되었을 시기에는 Hilt가 너무너무 어려웠다.
하지만 이제는 Hilt 없는 안드로이드 프로젝트를 찾아보기 어려울 정도로 안드로이드 계의 기본 소양이 되었다.

오늘은 Hilt, 그리고 DI에 대해 탐구해보려 한다!

의존성 생성 책임 분리와 개방–폐쇄 원칙(OCP)의 연결

안드로이드 개발을 하다 보면 DI(Dependency Injection)를 “편해서 쓰는 도구” 정도로 받아들이기 쉽다.
하지만 실제로 DI는 단순한 편의 기능이 아니라, 변경에 강한 구조를 만들기 위한 설계 기법이다.

이 글에서는

  • DI가 말하는 “의존성 생성 책임 분리”가 무엇인지
  • 이것이 어떻게 개방–폐쇄 원칙(OCP) 과 연결되는지
    안드로이드 ViewModel–Repository 구조를 예로 들어 정리해본다.

DI 없이 생기는 문제

DI를 사용하지 않고 ViewModel에서 Repository를 직접 생성한다고 가정해보자.

class ReviewViewModel : ViewModel() {

    private val repository = ReviewRepository(
        apiService,
        localDataSource
    )
}

이 구조에는 몇 가지 문제가 있다.

1. 생성 책임이 ViewModel에 있다

ViewModel은 본래 상태 관리와 UI 로직에 집중해야 하지만,
Repository를 어떻게 생성하는지까지 알고 있다.

2. 생성 로직이 중복된다

여러 ViewModel에서 같은 Repository를 사용한다면,
각 ViewModel마다 동일한 생성 코드가 반복된다.

3. 변경에 매우 취약하다

만약 ReviewRepository 생성자에 파라미터가 하나 추가된다면 어떻게 될까?

class ReviewRepository(
    apiService: ApiService,
    localDataSource: LocalDataSource,
    logger: Logger
)

이 경우,

  • ReviewRepository를 생성하는 모든 ViewModel을 수정해야 한다
  • 변경의 영향 범위가 ViewModel 전체로 퍼진다

이 구조는 변경에 닫혀 있지 않다.


DI의 핵심: 의존성 생성 책임 분리

DI를 적용하면 ViewModel은 더 이상 Repository를 생성하지 않는다.

@HiltViewModel
class ReviewViewModel @Inject constructor(
    private val repository: ReviewRepository
) : ViewModel()

Repository는 별도의 Module에서 생성된다.

@Module
@InstallIn(SingletonComponent::class)
object RepositoryModule {

    @Provides
    fun provideReviewRepository(
        apiService: ApiService,
        localDataSource: LocalDataSource
    ): ReviewRepository {
        return ReviewRepository(apiService, localDataSource)
    }
}

여기서 중요한 변화는 단 하나다.

ViewModel은 Repository를 “어떻게 만드는지”를 더 이상 모른다


변경의 영향을 한 곳으로 모은다

이제 ReviewRepository 생성자가 변경되더라도,

fun provideReviewRepository(
    apiService: ApiService,
    localDataSource: LocalDataSource,
    logger: Logger
): ReviewRepository
  • 수정해야 할 곳은 Module 하나뿐이다
  • ViewModel 코드는 전혀 수정하지 않는다

즉,

  • 변경은 발생하지만
  • 변경의 영향은 한 곳에만 집중된다

이 지점에서 개방–폐쇄 원칙(OCP) 과 연결된다.


개방–폐쇄 원칙(OCP)과의 연결

개방–폐쇄 원칙은 다음과 같이 정의된다.

확장에는 열려 있고, 변경에는 닫혀 있어야 한다

DI 구조에서 이를 다시 해석하면 다음과 같다.

  • Repository의 생성 방식이나 구현은 확장 가능
  • Repository를 사용하는 ViewModel은 변경되지 않음

구성(configuration)은 바뀔 수 있지만, 사용 코드는 닫혀 있다

DI는 의존성 생성 책임을 분리함으로써
변경이 필요한 지점과 변경되지 않아야 할 지점을 명확히 나눈다.

이것이 DI가 OCP를 자연스럽게 만족시키는 이유다.


싱글톤은 핵심이 아니다

DI의 장점을 설명할 때 흔히 “객체를 하나만 만들어서 재사용한다”는 이야기가 나온다.
하지만 이는 부가적인 효과일 뿐, 핵심은 아니다.

  • 싱글톤은 객체의 생명주기 관리 전략이다
  • DI는 의존성을 외부에서 주입하는 설계 방식이다

DI를 사용하면서 싱글톤을 선택할 수는 있지만,
DI의 본질은 재사용이 아니라 변경 관리에 있다.


정리

DI가 유지보수성을 높이는 이유는 단순하다.

  • 의존성 생성 책임을 분리하고
  • 변경의 영향을 한 곳으로 모으며
  • 사용하는 코드를 변경 없이 확장할 수 있게 만들기 때문이다

이 구조는 자연스럽게 개방–폐쇄 원칙을 만족시킨다.

DI는 객체를 주입하는 기술이 아니라,
변경에 강한 구조를 만드는 설계 도구다.

profile
안드로이드... 좋아하세요?

0개의 댓글