개발/소프트웨어 디자인 패턴
설계 패턴에서 Interface와 implementation을 쓰는 이유
뿌꾸 빵
2025. 2. 18. 17:16
728x90
반응형
아키텍처 설계를 하는데 굳이 Interface와 구현 클래스를 나누는 이유는 무엇일까 궁금해졌다.
Kotlin에서는 이것이 일반적인 방식의 설계 패턴인데, 유연한 구조를 만들고 유지보수가 편해진다.
1️⃣ 유지보수 & 확장성
예제 1: 인터페이스 없이 직접 구현한 경우
class LoginRepository @Inject constructor(
private val api: LoginNetworkApi
) {
suspend fun getLoginData(): LoginClientModel {
return api.getLoginData()
}
}
이렇게 하면 LoginRepository 안에서 직접 LoginNetworkApi를 사용하니까 나중에 구현을 바꾸기 어려움
예제 2: interface를 사용한 경우 (좋은 예!)
interface LoginRepository {
suspend fun getLoginData(): LoginClientModel
}
class LoginRepositoryImpl @Inject constructor(
private val api: LoginNetworkApi
) : LoginRepository {
override suspend fun getLoginData(): LoginClientModel {
return api.getLoginData()
}
}
이렇게 interface를 두면:
- API 방식이 바뀌더라도 LoginRepositoryImpl만 수정하면 됨
- 테스트할 때도 FakeLoginRepository를 쉽게 만들어서 대체 가능
- DB에서 가져오는 LocalLoginRepositoryImpl을 만들어도 구조 변경 없이 추가 가능
2️⃣ 테스트하기 쉬워짐 (Mocking 가능)
인터페이스 없이 테스트하면?
val repository = LoginRepository(LoginNetworkApi()) // 의존성이 직접 결합됨
- 여기서 LoginRepository는 LoginNetworkApi를 직접 호출하니까,
- 테스트할 때 진짜 네트워크 요청이 나감! 😱
👉 네트워크가 없으면 테스트가 실패함. 속도도 느림.
인터페이스를 활용하면?
class FakeLoginRepository : LoginRepository {
override suspend fun getLoginData(): LoginClientModel {
return LoginClientModel(name = "Test User", token = "fake_token")
}
}
val repository: LoginRepository = FakeLoginRepository() // 네트워크 없이 테스트 가능!
- FakeLoginRepository를 만들어서 네트워크 없이 테스트 가능
- 테스트 속도가 빨라짐 & 안정적
3️⃣ 의존성 주입 (DI, Dependency Injection) 활용
Hilt 같은 DI를 사용할 때 인터페이스를 쓰면 쉽게 구현체를 교체할 수 있다.
Hilt에서 인터페이스를 주입하는 방식
@Module
@InstallIn(SingletonComponent::class)
object RepositoryModule {
@Provides
fun provideLoginRepository(api: LoginNetworkApi): LoginRepository {
return LoginRepositoryImpl(api)
}
}
이렇게 하면
- LoginRepositoryImpl을 다른 구현체로 쉽게 교체 가능
- Mock Repository를 주입해서 테스트 가능
728x90
반응형