[Spring] 의존성 주입

의존성 주입

의존성 주입(Dependency Injection)의 의미를 알아보고 스프링 부트로 의존성 주입을 해보자.

예제

의존성의 의미는 무엇일까?
간단한 Test 용 Repository와 Controller가 있다고 해보자.

package com.vividswan.spring.web.domain.test;

import org.springframework.stereotype.Component;

public class TestRepository {
    public String findStrig(){
        return "This Is Test!!";
    };
}
package com.vividswan.spring.web;

import com.vividswan.spring.web.domain.test.TestRepository;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
    TestRepository testRepository = new TestRepository();
    //생성자 사용

    @GetMapping("/test")
    public String test(){
        return testRepository.findStrig();
    }

}

/test 경로로 매핑을 하면 “This Is Test!!”를 반환하는 간단한 컨트롤러다.
이때, 이 컨트롤러에서 TestRepository라는 객체가 필요하다면 위의 코드처럼 생성자를 이용해야 한다.

의존이란?

위의 상황이 TestController 객체가 TestRepository에 의존하는 상황이다.

A라는 객체가 B에 의존한다는 것은, A가 B를 사용한다는 뜻이고, 그 말은 B가 바뀌면 A에게도 영향을 끼친다는 의미다.
위의 예제는 간단한 소스였지만, Repository가 인터페이스를 구현한 객체였고, 다른 것으로 Repository가 구현된다면, TestController의 내용도 수정해야 한다.
작은 규모가 아닌 큰 프로젝트에선 이러한 의존성이 있는 객체들끼리의 유지 보수성이 어려워진다.
스프링 프레임워크에서는 이것을 스프링이 직접 의존성 관리를 하는 방법으로 이 문제를 해결한다.

Spring IoC

스프링에선, Spring IoC 컨테이너에 객체들을 등록해 주면, 스프링이 그 안에서 객체들의 의존성 주입을 관리해 준다.
여러 가지 방법이 있지만, 스프링이 객체를 인식하고 컨테이너에 등록할 수 있게 어노테이션을 이용해서 의존성 주입을 관리하게 해주자.

어노테이션 사용

package com.vividswan.spring.web.domain.test;

import org.springframework.stereotype.Component;

@Component
public class TestRepository {
    public String findStrig(){
        return "This Is Test!!";
    };
}
package com.vividswan.spring.web;

import com.vividswan.spring.web.domain.test.TestRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
    @Autowired
    TestRepository testRepository;

    @GetMapping("/test")
    public String test(){
        return testRepository.findStrig();
    }

}

Repository에는 스프링 컨테이너가 인식할 수 있게 @Component를 사용했다.
Controller에선 생성자를 이용했던 방식을 없애고, @Autowired를 통해 Repository를 자동으로 의존성 주입하게 했다.
이렇게 해결하면 Repository의 구현체가 변한다고 해도, TestController의 유지 보수가 필요하지 않다.

예를 들어, TestRepository를 인터페이스로 바꾸고, 그에 대한 구현체를 따로 만드는 것으로 Repository가 변하는 상황이라고 하자.

package com.vividswan.spring.web.domain.test;

public interface TestRepository {
    public String findStrig();
}
package com.vividswan.spring.web.domain.test;

import org.springframework.stereotype.Component;

@Component
public class TestRepositoryImp implements TestRepository {
    public String findStrig(){
        return "This Is Test!!";
    };
}

이런 식으로 기존의 TestRepository는 인터페이스를 구현하는 TestRepositoryImp로 변경한다.
또 기존의 TestRepository의 이름으로 인터페이스를 만든다.

편의성

위와 같은 상황이 돼도 TestController는 따로 유지 보수할 필요가 없다.

    @Autowired
    TestRepository testRepository;

이 소스만으로 @Component 어노테이션이 있는 TestRepositoryImp 구현 객체를 의존성 주입해 준다.
이렇게 Repository의 구현체가 바뀌어도 스프링 IoC는 그에 맞는 구현체를 주입해 주는 편의성이 있다.