회사에서 내가 주로 맡고 있는 시스템이 Kotlin으로 만든 웹 어플리케이션의 일부인데 Google Guice 프레임워크를 사용하고 있었다. 이제까지 굳이 다 파악할 필요가 없어서 읽지 않았는데 컨테이너화를 하려고 보니 알아야겠어서 이것저것 찾아봤다.
먼저, Google이 DI용으로 만든 프레임워크다. 주스라고 읽는다. 2007년인가에 만들어진 꽤 오래된 프레임워크다.
DI가 무엇이냐 하면 Dependency Injection이라 해서 의존관계를 줄여주는 소프트웨어 패턴이다.
예를 들어 아래와 같은 구현은 Client
가 Service
에게 의존하고 있다. 내부에서 초기화하기 때문에 Service
의 변화에 맞춰 Client
를 수정해야 할 것이다.
class Service:
def do(self) -> None:
pass
class Client:
def __init__(self) -> None:
self.service = Service()
client = Client()
외부에서 전달하도록 하면 의존관계는 약해진다. Service
를 수정해야해도 Client
는 모르는 일이기 때문이다.
class Service:
def do(self) -> None:
pass
class Client:
def __init__(self, service: Service) -> None:
self.service = service
service = Service()
client = Client(service)
Guice는 Injector라는 것을 만들어 참조해야 하는 외부의 처리와의 의존관계를 약하게 만든다.
class Service {
public main() {
...
}
}
class Client {
@Inject
private service: Service
public main() {
injector = Guice.createInjector(object : AcstractModule() {
override fun configure() {...}
})
}
}
createInjector
는 아래와 같은 파라미터를 받는다. Module
로 넘겨받는 것은 AbstractModule
을 계승해서 만든 모듈이다.
public static com.google.inject.Injector createInjector(com.google.inject.Module... modules) { /* compiled code */ }
public static com.google.inject.Injector createInjector(java.lang.Iterable<? extends com.google.inject.Module> modules) { /* compiled code */ }
public static com.google.inject.Injector createInjector(com.google.inject.Stage stage, com.google.inject.Module... modules) { /* compiled code */ }
public static com.google.inject.Injector createInjector(com.google.inject.Stage stage, java.lang.Iterable<? extends com.google.inject.Module> modules) { /* compiled code */ }
다시 Injector를 만드는 코드를 보면, object :
라는 표현이 보일텐데 이건 Kotlin의 object를 사용한 것이다. Kotlin에서는 추상 클래스를 직접 초기화 하는 것이 불가능하기 때문에 object :
를 사용해 익명의 클래스로 만들어 넘기는 것이다.
(애초에 클래스를 만들 때 object를 붙인다면 그건 싱글톤 클래스로 만들겠단 뜻이다)
public main() {
injector = Guice.createInjector(object : AcstractModule() {
override fun configure() {...}
})
}
AbstractModule
의 configure
메서드를 오버라이트 해야한다. 필요한 DI 설정을 그 안에 기술한다.
예를 들어, 아래와 같이 install
을 사용해 또 다른 AbstractModule
을 주입할 수 있다.
FYI: @Provides
아노테이션을 AbstractModule
내부 메서드에 붙이면 Injector가 invoke한다.
import com.google.inject.AbstractModule
import com.google.inject.Provides
object class SomeOtherModule: AbstractModule() {
override fun configure() {
bind(...)
}
@Provides
private fun someMethod() = Unit
}
object class SomeModule: AbstractModule() {
public main() {
injector = Guice.createInjector(object : AcstractModule() {
override fun configure() {
install(SomeOtherModule)
}
})
}
bind
도 configure
내부에서 사용할 수 있는 메서드로 외부의 다른 클래스 등을 주입할 때 쓴다. 바인딩이란 어떠한 인터페이스에 외부의 다른 구현물을 연결짓는 것을 말한다.
가장 처음 얘기로 돌아가서 내가 파악해야 하는 것은 웹 어플리케이션의 일부인데 서블릿이 필요했기 때문에 ServletModule
을 사용했다.
서블릿이란 웹 어플리케이션에서 클라이언트의 리퀘스트에 응답하기 위한 기술이다. JSP도 있다.
이것도 AbstractModule
을 계승했다. 위에서 말한대로 주입할 수 있는데, 단, configure
가 아니라 configureServlets
을 오버라이트 해야한다.
init {
injector = Guice.createInjector(object : ServletModule() {
override fun configureServlets() {
install(...)
}
})
'기타 메모' 카테고리의 다른 글
이미 push한 commit 메시지를 수정하기 (2) | 2023.10.17 |
---|---|
Kibana 웹페이지에 접속이 안될 때 (0) | 2022.03.24 |
Kotlin이어도 null에러는 발생한다고.. (0) | 2021.12.09 |
아무 생각 없이 git access token지웠다가 fatal에러 나서 다시 인증해야 할 때 github cli 쓰면 편함 (0) | 2021.11.29 |
java로 만들어진 툴을 사용하려는데 Unrecognized VM option 'AggressiveOpts' 에러가 날 때 (feat.jEnv) (0) | 2021.10.11 |