백엔드 면접 용어 사전
Spring·JPA제어의 역전

IoC

객체 생성·생명주기 제어를 프레임워크가 맡는 설계 원칙. 스프링에서는 컨테이너가 빈을 관리한다.

객체의 생성과 생명주기 관리를 개발자가 아닌 컨테이너가 가져가는 것 (Inversion of Control, 제어의 역전).

무엇이 "역전"되었나

// 전통적 방식 — 내가 제어한다
class OrderService {
    private PaymentClient client = new TossPaymentClient();   // 내가 만든다
}

// IoC — 컨테이너가 제어한다
class OrderService {
    private final PaymentClient client;
    OrderService(PaymentClient client) { this.client = client; }   // 받는다
}

"필요한 것을 내가 만든다" → "누군가 만들어서 준다" 로 방향이 뒤집혔다.

왜 이것이 중요한가

첫 번째 코드의 문제를 보자.

  • ① 구현체가 코드에 박혀 있다
    • 카카오페이로 바꾸려면 OrderService 를 수정해야 한다
  • ② 테스트할 수 없다
    • 실제 결제가 호출된다. 가짜로 바꿔 끼울 방법이 없다
  • ③ 생명주기를 제어할 수 없다
    • 매번 새로 만든다. 공유·풀링을 적용하려면 코드를 고쳐야 한다

두 번째는 셋 다 해결된다. 의존 대상을 밖에서 결정할 수 있기 때문이다.

프레임워크와 라이브러리의 차이 — 같은 개념

라이브러리: 내 코드가 라이브러리를 부른다(내가 제어)
프레임워크: 프레임워크가 내 코드를 부른다(제어 역전)
  • "Don't call us, we'll call you" (할리우드 원칙)

스프링에서 컨트롤러 메서드를 만들면 내가 부르지 않는다. 디스패처 서블릿이 요청이 오면 대신 불러 준다. 이것도 IoC다.

스프링 컨테이너

ApplicationContext
├─ 빈 정의를 읽는다 (@Component, @Bean, XML)
├─ 객체를 생성한다
├─ 의존 관계를 주입한다
├─ 초기화 콜백 (@PostConstruct)
├─ 사용
└─ 소멸 콜백 (@PreDestroy)

빈(Bean) 은 컨테이너가 관리하는 객체다. 등록되지 않은 객체는 @Transactional@Cacheable도 동작하지 않는다 — AOP 프록시를 컨테이너가 씌워 주기 때문이다. new로 만든 객체에 어노테이션이 안 먹는 이유가 이것이다.

DI와의 관계 — 자주 혼동된다

IoC : 제어를 넘긴다는 넓은 개념 (원칙)

  • DI — : 그 구현 방법 중 하나 — 의존성을 주입한다 (기법)

IoC ⊃ DI

IoC를 구현하는 다른 방법으로 서비스 로케이터가 있다 (필요할 때 컨테이너에 물어보는 방식). DI가 더 나은 이유는 의존 관계가 생성자 시그니처에 드러나 감춰지지 않기 때문이다.

얻는 것

이점설명
결합도 감소인터페이스에만 의존. 구현체 교체가 설정 문제가 된다
테스트 용이가짜 구현을 주입해 단위 테스트 가능
관심사 분리비즈니스 로직이 객체 생성·조립을 신경 쓰지 않는다
횡단 관심사컨테이너가 프록시를 씌워 트랜잭션·로깅을 얹는다

면접 답변 골격

"객체 생성과 조립의 책임을 컨테이너에 넘기는 것입니다. 그 결과 구현체 교체가 코드 수정이 아니라 설정 변경이 되고, 테스트에서 가짜 구현을 끼울 수 있게 됩니다. DI는 IoC를 구현하는 방법 중 하나입니다."

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — Spring·JPA — IoC/DI·AOP·영속성 컨텍스트·N+1