【 개발 이야기 】
코틀린에서 new 없이 의존성 주입(DI)을 통해 객체를 생성하는 방식
홍호나
2025. 3. 14. 10:49
코틀린 스프링부트에서 아래와 같은 예제가 있었다.
@Service
class AService {
suspend fun checkBalance(request: exRequest): ResponseEntity<*> {
return ResponseEntity.ok("Balance checked")
}
}
@RestController
class AController(private val aService: AService) {
suspend fun checkBalance(@RequestBody request: exRequest): ResponseEntity<*> {
return aService.checkBalance(request)
}
}
위 예제의 컨트롤러는 어떻게 AService를 호출하고 있는걸까? new를 한 적이 없는데 어떻게 .을 사용하여 메서드를 호출하고 있는걸까?
정답은... 어딘가에서 미리 생성되어, 컨트롤러의 생성자에 전달이 되었기 때문이다.
그렇다면 AService 객체는 어디서 오고 있는걸까.
스프링의 자동 객체 관리 with 어노테이션
스프링에서는 @Service와 @RestController 같은 어노테이션을 사용하면 스프링 컨테이너가 알아서 객체를 생성하고 주입해준다.
@Service // 스프링이 자동으로 객체를 관리함
class AService {
suspend fun checkBalance(request: exRequest): ResponseEntity<*> {
return ResponseEntity.ok("Balance checked")
}
}
@RestController
class AController(private val aService: AService) { // 여기서 자동 주입됨!
suspend fun checkBalance(@RequestBody request: exRequest): ResponseEntity<*> {
return aService.checkBalance(request)
}
}
컨트롤러는 생성자로 서비스를 받고 있지만 스프링이 알아서 AService 객체를 넣어준다.
>>> 이거에 대해서는 https://github.com/waveaway77/KOTLIN2/blob/master/src/code/C_22_Constructor/Constructor.kt 코드를 참고 (35line)
new 없이도 객체가 생성되는 과정
스프링이 대신 new를 해주는 과정을 알아보자.
- @Service가 붙은 AService 객체를 스프링 컨테이너가 자동으로 생성한다.
- @RestController가 붙은 컨트롤러를 생성할 때
- 컨트롤러의 생성자에서 AService가 필요하다고 선언되어 있다.
- 스프링이 자동으로 AService 객체를 찾아서 컨트롤러의 생성자에 넣어준다.
- 그 결과 AController에서는 new AService()를 하지 않아도 aService를 사용할 수 있다.
코틀린에서 기본 생성자와 자동 호출
코틀린에서 클래스는 기본 생성자를 가질 수 있다.
ClassName()을 호출하면 기본값이 설정된 인자는 자동으로 채워지고, 기본값이 없는 인자는 직접 제공해야 한다.
class Person(val name: String, val age: Int = 20)
fun main() {
val p1 = Person("Alice") // 기본값 20을 사용한다
val p2 = Person("Bob", 25) // 기본값을 사용하지 않는다.
}
기본값이 없는 인자는 반드시 직접 전달해야 한다.
class Car(val brand: String, val model: String)
fun main() {
val c1 = Car("Tesla") // error
}
자바에서는 new 키워드를 사용해서 객체를 생성했다.
Person p = new Person("Alice", 20);
하지만 코틀린에서는 클래스 이름 뒤에 ()를 붙이면 생성자가 자동으로 호출된다.
val p = Person("Alice", 20)