【 개발 이야기 】

코틀린에서 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를 해주는 과정을 알아보자.

  1. @Service가 붙은 AService 객체를 스프링 컨테이너가 자동으로 생성한다.
  2. @RestController가 붙은 컨트롤러를 생성할 때
    1. 컨트롤러의 생성자에서 AService가 필요하다고 선언되어 있다.
    2. 스프링이 자동으로 AService 객체를 찾아서 컨트롤러의 생성자에 넣어준다.
  3. 그 결과 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)