[Spring boot]Dependency injection(DI)

여러개의 object(class)들 사이에는 의존적 관계가 존재한다. 이런 의존적 관계를 가지고 구현하는 것을 tight-coupled programming이라고 한다. Spring에서는 이런 관계를 좀 더 유연한 관계(loosely-coupled programming)으로 변환하는 메가니즘을 dependency injection(DI)라고 한다. 즉 object의 생성과 생명주기 관리를 spring에 넘기므로써 개발자는 로직에만 집중하여 개발 할 수 있게 만드는 것이다.

DI 주입 방식은 보통 3가지가 있다. 그것들을 소개하고 장점과 단점에 대해 논의해 보자.

Setter-based DI

set 함수를 이용하여 의존성을 부여하는 것을 말한다. 이 방식은 runtime에 주입 받는 객체가 변경될 가능성이 있을 떄 사용한다. 예를 들면 Controller의 어떤 서비스에 대하여 여러개의 구현 방식이 존재할 경우 이다.

@RestController
class Corporate(
){
    var corporateRepository: CorporateRepository? =null
    fun setCoroprateRepository(corporateRepository: CorporateRepository){
        this.corporateRepository=corporateRepository
    }
    @GetMapping("corporates")
    fun get_corporate_information():List<Corporate>?{
        return corporateRepository?.findAll() ?: null
    }

    @PostMapping("corporates")
    fun creat_corporate(@RequestBody corporate: Corporate):Corporate?{
        return corporateRepository?.save(corporate) ?: null
    }
}

위 코드를 정확히 실행하려고 하면 corporate 주입 시 setCoroprateRepository 을 호출하여 repository를 주입해야한다. 이 때문에 이를 사용하는 다른 함수 들에서는 null 체크를 무조건 해야 하기에 여러모로 불편하다. 불가피한 경우를 제외하고 사용하지 않는 것이 좋다. 이런 용법이 있다는 정도만 알면 될것 같다.

Contructor-based DI

이 방식은 생성자를 통해 주입하는 방식이다. 위의 예를 좀 수정하여 Contructor-base DI에 적합하도록 해보자.

@RestController
class Corporate(
    var corporateRepository: CorporateRepository
){

    @GetMapping("corporates")
    fun get_corporate_information():List<Corporate>?{
        return corporateRepository.findAll() 
    }

    @PostMapping("corporates")
    fun creat_corporate(@RequestBody corporate: Corporate):Corporate?{
        return corporateRepository.save(corporate) 
    }
}

코드가 엄청 깔끔해 보인다. 생성자 주입을 사용하면 Corporate 대상이 생성할때 CorporateRepository을 주입하기때문에 함수에서 사용 시 safe check를 따로 하지 않아도 되는 장점이 있다.

Field-based DI

필드 주입은 간결하지만 외부에서의 변경이 불가능하다는 단점이 있다. 또 하나의 단점은 반드시 DI프레임워크가 존재해야 사용할 수 있다. 위의 두 방식은 DI지원이 없어도 주입이 가능하다.

@Service
class SearchService() {
        @Autowired
        private val wordRepository: WordRepository
    fun search_word(word: String):String{
        val idx = wordRepository.findWord(word)
        return if(idx==-1){
            "Word is not in repo"
        } else {
            String.format("Word %s is in index of %d",word,idx)
        }
    }
}

이글에서 사용되었던 service 부분 코드를 fied-based 주입 방식으로 작성해 보았다. 사용하기에는 정말 편리한데 위에서 나열한 단점 떄문에 Contruction 주입 방식을 사용하는 것을 권장한다.

참조:

Hands-On High Performance with Spring 5 - Spring Best Practices and Bean Wiring Configurations

https://mangkyu.tistory.com/125

 

[Spring] 다양한 의존성 주입 방법과 생성자 주입을 사용해야 하는 이유 - (2/2)

Spring 프레임워크의 핵심 기술 중 하나가 바로 DI(Dependency Injection, 의존성 주입)이다. Spring 프레임워크와 같은 DI 프레임워크를 이용하면 다양한 의존성 주입을 이용하는 방법이 ..

mangkyu.tistory.com