IoC(Inversion of Control) 컨테이너는 객체의 생성, 초기화, 생명주기 관리 등의 제어 권한을 개발자가 아닌 프레임워크(Spring)에게 위임하는 구조를 의미한다. Spring에서는 IoC 컨테이너가 Bean 객체들을 생성하고, 설정 파일이나 어노테이션을 기반으로 필요한 의존성들을 주입해준다.
의존성 주입(Dependency Injection)은 IoC의 핵심 개념 중 하나로 객체가 자신이 사용할 의존 객체를 직접 생성하지 않고 외부에서 주입받는 방식이다. Spring에서는 의존성 주입을 생성자, 필드, 또는 setter 메서드를 통해 수행할 수 있다. 그 중에서 생성자 주입이 권장되는 방식이다. (순환참조 문제 해결, 테스트 코드 용이)
이렇게 함으로써 코드의 결합도를 낮추고, 테스트와 유지보수가 용이해지는 장점이 있다.
AOP (Aspect Oriented Programming)는 쉽게 말해서 공통 기능(로깅, 보안, 트랜잭션 관리 등)을 비즈니스 로직과 분리해서 필요한 시점에 그 기능을 끼워 넣어주는 프로그래밍 방식이야.
즉, Spring이 실제 비즈니스 로직 코드를 감싸는 껍데기 객체(프록시 객체)를 만들어서 그 껍데기 안에서 먼저 Advice(부가기능, 예: 로그 찍기)를 실행하고, 그 다음에 비즈니스 로직 코드를 호출한다.
이때 사용하는 프록시 생성 방식은 두 가지가 있으며, 스프링은 상황에 따라 적절한 방식을 선택한다.
JDK 동적 프록시 (JDK Dynamic Proxy)
인터페이스를 기반으로 프록시를 생성한다.
대상 객체가 하나 이상의 인터페이스를 구현하고 있으면, 스프링은 기본적으로 JDK 동적 프록시를 사용한다.
java.lang.reflect.Proxy를 통해 프록시 객체를 생성하며, 인터페이스를 통해서만 접근 가능하다.
CGLIB (Code Generation Library)
클래스 기반의 프록시다.
대상 클래스에 인터페이스가 없거나, 강제로 클래스 기반 프록시를 원할 경우 사용된다.
CGLIB는 대상 클래스를 상속받아 프록시 클래스를 생성하므로, final 클래스나 final 메서드는 프록싱할 수 없다. 스프링에서 CGLIB를 사용하도록 강제하려면 @EnableAspectJAutoProxy(proxyTargetClass = true) 또는 proxyTargetClass=true 설정을 명시해야 한다.
👉 정리하자면 인터페이스가 존재하면 JDK 동적 프록시, 인터페이스가 없거나 명시적으로 설정된 경우 CGLIB를 사용하며, 두 방식 모두 프록시 기반의 AOP를 구현한다는 공통점이 있다.
Spring에서 빈(Bean)은 컨테이너에 의해 생성되고 관리되며, 생명주기 동안 다양한 이벤트가 발생한다. 이 과정을 순서대로 나열하면 다음과 같다.
new
키워드로 인스턴스화)@Autowired
, @Value
, @Inject
등을 통해 필요한 의존성을 주입한다.BeanNameAware
, BeanFactoryAware
, ApplicationContextAware
등의 Aware 인터페이스들이 순서대로 호출됩니다.
목적: Bean이 생성되고, 의존성 주입이 끝난 후에 컨테이너의 정보를 받아서 추가적인 설정을 할 수 있도록 하기 위해
예를 들어:
BeanNameAware
→ 내 Bean의 이름을 알아야 무언가 로깅하거나 조건 처리할 때 사용ApplicationContextAware
→ 전체 스프링 컨텍스트(ApplicationContext)에 접근해야 다른 빈을 꺼내 쓰는 경우InitializingBean
인터페이스의 afterPropertiesSet()
메서드@PostConstruct
어노테이션 (외부 API 연결 설정)init-method
속성으로 지정한 사용자 정의 메서드