IoC(제어의 역전)는 Spring 프레임워크의 가장 근본적인 설계 원리입니다.
전통적인 방식: 개발자가 직접 객체를 생성하고, 의존성을 연결하며 프로그램의 흐름을 제어합니다.
public class MyController {
// 개발자가 직접 서비스 객체를 생성 (제어권이 개발자에게 있음)
private MyService myService = new MyService();
}
IoC 방식: 객체의 생성과 생명주기 관리, 의존성 연결 등 모든 제어권이 개발자로부터 프레임워크(Spring 컨테이너)로 넘어간(역전된) 것을 의미합니다.
DI(의존성 주입)는 IoC를 구현하는 구체적인 방법 중 하나입니다.
개념: 클래스가 의존하는(필요로 하는) 다른 객체를 내부에서 직접 생성하는 것이 아니라, 외부(Spring 컨테이너)에서 생성하여 주입(Injection)받는 방식입니다.
왜 사용하는가? (DI의 장점)
생성자 주입 (Constructor Injection) - 가장 권장되는 방법
final 키워드를 사용할 수 있어, 객체가 생성된 후 의존성이 변경될 위험이 없습니다.NullPointerException(NPE)을 원천적으로 방지할 수 있습니다.@Service
public class MyService {
private final MyRepository myRepository;
// 생성자가 하나만 있을 경우 @Autowired 생략 가능
public MyService(MyRepository myRepository) {
this.myRepository = myRepository;
}
}
필드 주입 (Field Injection)
@Autowired를 직접 붙여 주입합니다.수정자 주입 (Setter Injection)
NullPointerException이 발생할 수 있습니다.Spring 컨테이너 (IoC 컨테이너, DI 컨테이너):
ApplicationContext가 있습니다.Bean:
@Component 어노테이션(또는 이를 포함하는 @Service, @Repository, @Controller 등)을 클래스에 붙여, 해당 클래스의 객체를 Bean으로 등록해달라고 Spring에 요청합니다.AOP는 OOP를 보완하는 프로그래밍 패러다임입니다.
문제점 (횡단 관심사의 분리): 애플리케이션의 여러 비즈니스 로직(핵심 관심사)에 걸쳐 공통적으로 나타나는 부가 기능(e.g., 로깅, 트랜잭션, 보안)들이 있습니다. OOP만으로는 이러한 "횡단 관심사(Cross-cutting Concerns)"를 분리하기 어려워, 모든 비즈니스 메서드에 중복된 코드가 흩어지게 됩니다.
AOP의 해결책: 횡단 관심사를 "Aspect"라는 별도의 모듈로 분리하여, 핵심 비즈니스 로직에는 영향을 주지 않으면서 필요한 곳에 동적으로 적용(Weaving)하는 방식입니다.
Aspect: 횡단 관심사를 모듈화한 것. (e.g., LoggingAspect)
Advice: Aspect가 실제로 수행하는 부가 기능 로직. (e.g., "메서드 시작 전에 로그를 남긴다.")
@Before, @After, @Around 등Pointcut: Advice를 어디에 적용할지 결정하는 표현식. (e.g., "모든 서비스 계층의 create로 시작하는 메서드에 적용")
Join Point: Advice가 적용될 수 있는 모든 위치. (e.g., 메서드 호출, 필드 값 변경 등)
Spring의 @Transactional: Spring AOP의 가장 대표적인 활용 사례입니다. 개발자는 비즈니스 로직에만 집중하고, @Transactional 어노테이션만 붙이면, Spring이 AOP를 통해 해당 메서드의 시작과 끝에 트랜잭션 시작/커밋/롤백이라는 부가 기능(Aspect)을 동적으로 적용해줍니다.