티스토리 뷰

제어의 역전(IoC, Inversion of Control)

일반 자바 프로그램은 객체를 직접 생성하여 메소드를 호출한다. 만약 A object 에서 B object 의 메소드를 사용하고 싶으면, A object 에서 B object 를 직접 생성한 다음 메소드를 호출한다.

 

하지만 Spring 은 구현한 객체가 아니라 Spring 이 객체의 생명주기를 관리한다. 개발자가 Service 객체의 동작을 구현하더라도 해당 객체가 언제 호출되는지, 메소드를 언제 실행하는지는 Spring 이 관리한다. 프로그램의 제어권이 역전된 것이다.

 

예를 들어 JUnit 프레임워크에는 유닛 테스트를 도와주는 annotation 이 존재한다.  @Test  나   @BeforeEach ,  @AfterEach  같은 annotation 을 붙이면 JUnit 프레임워크가 해당 메소드의 호출 시점을 알아서 정한다.

 

따라서 IoC 개념을 도입하면 다음과 같은 장점을 얻을 수 있다.

  • 프로그램의 진행 흐름과 구체적인 구현을 분리시킬 수 있다.
  • 개발자는 비즈니스 로직에 집중할 수 있다.
  • 구현체 사이의 변경이 용이하다.
  • 객체 간 의존성이 낮아진다.

의존성 주입(DI, Dependency Injection)

앞서 든 예시처럼 'A object 에서 B object 를 직접 생성한 다음 B object 의 메소드를 호출한다.' 와 같은 상황이라면 A 가 B 에 의존한다 라고 말할 수 있다. 이와 달리 외부에서 B 를 생성한 다음 A 에게 넘기는 행위를 의존성 주입이라고 한다. 여기서 B 를 인터페이스로 만들면 A 에게 다양한 구현체를 주입할 수 있다.

의존성 주입 방법

1. Field Injection

변수 선언부에  @Autowired  annotation 을 사용한다.

@Controller
public class SampleController {
	@Autowired
	private SampleService sampleService;
}

Field Injection 은 여러모로 위험한 방법이다.

 

우선 annotation 을 붙이는데 제한이 없다. 극단적인 예시로 한 클래스에 변수가 1,000개가 있고 모든 변수에 @Autowired annotation 을 붙이면 그만큼 다른 클래스와 의존성이 높아진다.

 

순환 참조가 일어날 수도 있다. A class 가 B class 를 참조, B class 가 C class 를 참조, C class 가 A class 를 참조.

다시 말해 A → B → C → A 가 일어날 수 있다는 뜻이다. 이렇게 되면 서로가 서로를 호출하다가 결국 StackOverFlowError 가 발생하고 만다.

 

2. Setter Injection

setter 에 @Autowired annotation 을 선언한다.

@Controller
public class SampleController {
	private SampleService sampleService;
    
    @Autowired
    public void setSampleService(SampleService sampleService) {
    	this.sampleService = sampleService;
    }
}

상황에 따라 의존성을 선택적으로 주입할 수 있다.

 

Setter Injection 도 위험한 상황이 발생할 수 있다. 대표적으로 주입이 필요한 객체가 주입되지 않았음에도 생성될 수 있다. 예를 들어 Service 구현체를 주입해야 하는 Controller 를 Service 주입 없이 생성한 다음 Service 내 메소드를 호출하면 NullPointerException 이 발생한다.

 

3. Constructor Injection

constructor 에 @Autowired annotation 을 선언한다.

@Service
public class SampleService {
	private SampleDAO sampleDAO;
    
    @Autowired
    public SampleService(SampleDAO sampleDAO) {
    	this.sampleDAO = sampleDAO;
    }
}

@Controller
public class SampleController {
	private final SampleService sampleService = new SampleService(new SampleDAO());
}

Spring 에서는 Constructor Injection 을 권장한다.

Constructor Injection 은 Field Injection 에서 발생할 수 있는 순환 참조와 Setter Injection 에서 발생할 수 있는 NullPointerException 을 방지한다.

주입받을 필드를 final 로 선언하여 불변성을 유지할 수도 있다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함