의존성 주입(Dependency Injection)은 객체 간의 의존 관계를 외부에서 결정하고 주입해주는 디자인 패턴입니다. 이를 통해 코드의 유연성과 재사용성을 높일 수 있습니다. Kotlin에서는 인터페이스를 통해 의존성 주입을 구현할 수 있습니다. 이번 포스트에서는 코틀린에서 인터페이스를 통한 의존성 주입의 장점에 대해 설명하겠습니다.
1. 유연성
인터페이스를 사용하면 구체적인 구현체에 의존하지 않고 인터페이스 타입으로 선언할 수 있습니다. 이렇게 되면 하나의 인터페이스를 여러 개의 구현체로 대체할 수 있으며, 이를 통해 코드의 유연성을 높일 수 있습니다. 즉, 의존성 주입을 통해 언제든지 다른 구현체로 쉽게 교체할 수 있기 때문에, 코드의 변경 없이도 다양한 기능을 추가하거나 수정할 수 있습니다.
interface PaymentProcessor {
fun processPayment()
}
class CreditCardPaymentProcessor : PaymentProcessor {
override fun processPayment() {
// Credit card payment logic
}
}
class PaypalPaymentProcessor : PaymentProcessor {
override fun processPayment() {
// Paypal payment logic
}
}
class OrderService(private val paymentProcessor: PaymentProcessor) {
fun processOrder() {
paymentProcessor.processPayment()
// Order processing logic
}
}
fun main() {
val creditCardPaymentProcessor = CreditCardPaymentProcessor()
val orderService = OrderService(creditCardPaymentProcessor)
orderService.processOrder()
}
위의 예시 코드에서는 PaymentProcessor
인터페이스를 통해 의존성 주입을 구현하였습니다. OrderService
클래스는 PaymentProcessor
를 의존하며, 실제로 주입되는 구현체는 CreditCardPaymentProcessor
입니다. 이렇게 인터페이스를 통해 의존성을 주입함으로써 OrderService
는 결제 처리 로직을 수정하지 않고도 다른 결제 수단을 추가하거나 변경할 수 있는 유연성을 갖게 됩니다.
2. 테스트 용이성
인터페이스를 통한 의존성 주입은 테스트 용이성을 높일 수 있는 장점이 있습니다. 테스트를 작성할 때, 목(mock) 객체를 사용하여 의존성을 제어하고 예상된 동작을 검증할 수 있습니다. 즉, 테스트에서는 실제 구현체 대신에 테스트에 필요한 동작을 가지는 목 객체를 주입하여 테스트 코드를 작성할 수 있습니다.
class OrderServiceTest {
private lateinit var paymentProcessor: PaymentProcessor
private lateinit var orderService: OrderService
@Before
fun setup() {
paymentProcessor = mock()
orderService = OrderService(paymentProcessor)
}
@Test
fun testProcessOrder() {
// 주문 처리 로직 테스트 코드 작성
}
}
위의 예시 코드에서는 mock()
을 사용하여 PaymentProcessor
의 목 객체를 생성하고, OrderService
의 생성자에 주입하고 있습니다. 이렇게 하면 테스트에서는 실제 결제 처리 로직이 실행되지 않으며, 목 객체의 동작을 검증할 수 있습니다.