[Ktor] 의존성 주입 라이브러리 Koin 설정
Ktor에는 스프링과 다르게 기본적으로 의존성 주입을 지원해주지 않는다.
(경량 프레임워크라 그런가... 스프링이 무겁고 러닝커브가 높지만 참 많은 것을 지원해 주는 것 같다.)
여하튼 그래서 이번에는 Koin이라는 코틀린 의존성 주입 라이브러리를 통해서 의존성 주입을 설정해 보자.
소스코드
https://github.com/mopil/ktor-server
Koin
https://github.com/InsertKoinIO/koin
(이름을 참 잘 지은 것 같다)
implementation("io.insert-koin:koin-ktor:$koinVersion")
implementation("io.insert-koin:koin-logger-slf4j:$koinVersion")
gradle에 다음을 추가해 주자. 버전은 가장 최신버전을 사용하면 된다.
# 의존성 주입 설정
Koin을 사용하려면 우선 Ktor 어플리케이션에 install 함수를 통해서 적용해줘야 한다.
그전에 어떤 대상들을 의존성 주입으로 관리할지 먼저 module이라는 고차함수를 통해 지정한다.
val dependencyInjectionModule = module {
single { UserService(get()) }
single<UserRepository> { UserRepositoryImpl() }
single { ProductService(get()) }
single<ProductRepository> { ProductRepositoryImpl() }
}
fun Application.configureDependencyInjection() {
install(Koin) {
SLF4JLogger()
modules(dependencyInjectionModule)
}
}
(Application.module 고차함수에서 configureDependencyInjection 함수를 호출해주어야 등록되는 것을 잊지말자!)
Repository는 인터페이스로, 모두 구현체를 직접 명시하면 된다.
의존성 주입이 설정되지 않으면 아예 서버가 부트 업되지 않으므로 누락되는 것 없이 안전하게 설정할 수 있다.
스프링과는 별개로 서비스나 컴포넌트를 제작할 때마다 명시적으로 이렇게 모듈로 등록해야 하는 게 좀 불편하다.
# 의존성 주입받아서 사용하기
클래스 레벨은 코프링의 생성자 주입과 동일하게 사용하면 된다.
class ProductService(
private val productRepository: ProductRepository
) {
suspend fun createProduct(request: CreateProductRequest): IdResponse {
val product = productRepository.save(request)
return product.id.value.toResponse()
}
suspend fun getAllProducts(request: GetProductRequest): PageResponse<GetProductResponse> {
return productRepository.findAllByCondition(request)
}
}
Ktor 라우터(컨트롤러)는 클래스가 아니라 확장함수로 정의되기 때문에 이런 식으로 inject() 함수를 통해 주입받아서 사용하면 된다.
fun Route.productRouter() {
val productService: ProductService by inject()
get(Uris.Product.GET_ALL_PRODUCTS) {
val params = GetProductRequest(call.parameters)
call.respond(productService.getAllProducts(params))
}
}