
✅ Spring에서 @Component와 의존성 주입이 동작하는 원리
- "Spring에서 객체 생성과 관리, 그리고 의존성 주입은 어떻게 이루어질까?"
- "왜 @Component를 붙이면 자동으로 빈이 등록될까?"
- "Spring AOP 프록시는 언제 적용될까?"
Spring을 사용하다 보면 @Component, @Service, @Repository 등의 어노테이션을 붙이면 자동으로 빈(Bean)으로 등록되고 주입되는 것을 볼 수 있습니다.
먼저 들어가기 앞서
Spring 컨테이너(ApplicationContext)란?
Spring에서 객체의 생성과 생명주기를 관리를 담당하는 핵심 요소가 Spring 컨테이너입니다.
이 컨테이너는 빈(Bean)을 관리하고, 필요할 때 의존성을 주입하는 역할을 합니다.
✔️ Spring에서 컨테이너는 크게 두 가지로 구분할 수 있습니다.
| 컨테이너 | 설명 |
| IoC컨테이너 | 객체(Bean)의 생성과 의존성 주입을 담당 |
| ApplicationContext | IoC 컨테이너의 확장 개념, 빈 관리 뿐만 아니라 AOP, 이벤트 리스너, 메시지 소스 처리 등을 포함 |
✔️ 즉 ApplicationContext는 IoC 컨테이너의 기능을 포함하면서 더 많은 기능을 제공합니다.
SpringBoot는 일반적으로 ApplicationContext를 사용하여 빈을 관리합니다.
✅ 1. Spring에서 @Component는 어떻게 동작할까?
Spring이 @Component를 인식하는 과정
Spring에서는 @Component를 사용하여 특정 클래스를 빈으로 등록할 수 있습니다.
- Spring 애플리케이션이 실행되면서 ApplicationContext가 생성됩니다.
- @ComponentScan을 수행하면서 해당 패키지 및 하위 패키지에서 @Component가 붙은 클래스를 검색합니다.
- @Component가 붙은 클래스를 Spring 컨테이너에서 관리하는 빈(Bean)으로 등록합니다.
- 이후, @Autowired 또는 생성자 주입을 통해 자동으로 의존성을 주입할 수 있습니다.
✅ 2. @Component 예제 코드로 이해하기
1️⃣ @Component를 사용하여 빈 등록
import org.springframework.stereotype.Component;
@Component // Spring이 자동으로 스캔하여 빈으로 등록
public class MyComponent {
public void doSomething() {
System.out.println("MyComponent 동작 중...");
}
}
@Component를 사용하면 Spring이 자동으로 이 클래스를 스캔하고 빈으로 등록합니다.
2️⃣ @Autowired를 사용하여 빈 주입
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service // Service도 내부적으로 @Component 역할을 함
public class MyService {
private final MyComponent myComponent;
// @RequiredArgsConstructor 사용하면 생성자 만들 필요 없음
@Autowired // Spring이 MyComponent 빈을 찾아 자동 주입
public MyService(MyComponent myComponent) {
this.myComponent = myComponent;
}
public void execute() {
myComponent.doSomething(); // 자동 주입된 빈 사용
}
}
@Service도 @Component와 동일한 역할을 수행하며, Spring이 자동으로 MyComponent 빈을 찾아 MyService에 주입합니다.
📌 참고) @RequiredArgsConstructor를 클래스에 달아주면 생성자를 따로 만들 필요가 없습니다!
3️⃣ 빈이 등록되었는지 확인
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class BeanChecker {
private final ApplicationContext applicationContext;
public BeanChecker(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public void checkBean() {
boolean isBeanPresent = applicationContext.containsBean("myComponent");
System.out.println("MyComponent 빈 등록 여부: " + isBeanPresent);
}
}
MyComponent 빈 등록 여부: true
applicationContext.containsBean("myComponent")를 사용하면 Spring 컨테이너에 빈이 등록되었는지 확인할 수 있습니다.
4️⃣ 실행하면 자동으로 빈이 등록되고 주입됨
MyService에서 MyComponent의 doSomething() 메서드를 호출하면, 자동 주입된 빈을 통해 실행됩니다.
✅ 3. Spring의 의존성 주입(DI)은 누가 해주는가?
Spring에서 의존성을 주입하는 역할을 하는 것은 바로 Spring IOC 컨테이너입니다.
의존성 주입(DI)의 과정
1️⃣ Spring이 애플리케이션을 시작하면, IOC 컨테이너가 생성됨
- @ComponentScan을 수행하여 @Component, @Service, @Repository, @Controller 등의 어노테이션이 붙은 클래스를 자동으로 스캔
- 스캔된 클래스들은 Spring 컨테이너에서 관리되는 빈(Bean)으로 등록됨
2️⃣ 필요한 의존성을 자동으로 주입
- @Autowired, 생성자 주입을 사용하면 Spring 컨테이너가 적절한 빈을 찾아 주입
- 기본적으로 Spring은 모든 빈을 싱글톤(Singleton)으로 관리하여, 한 번 생성된 빈을 재사용함
✅ 4. Spring 컨테이너와 AOP 프록시의 흐름
AOP 프록시 적용 과정
Spring은 AOP를 적용하기 위해 프록시 객체를 생성합니다.
이 프록시는 트랜잭션 관리(@Transactional), 재시도 로직(@Retryable), 로깅 등 다양한 부가 기능을 제공할 수 있습니다.
Spring 컨테이너(AOP 프록시 포함)의 동작 흐름
1️⃣ Spring 컨테이너(ApplicationContext) 생성
- @ComponentScan을 수행하여 @Component, @Service, @Repository, @Controller가 붙은 클래스를 스캔
- 스캔된 클래스는 Spring 컨테이너에서 관리하는 빈(Bean)으로 등록
2️⃣ AOP 프록시 적용 여부 확인
- @Transactional, @Retryable과 같은 AOP 어노테이션이 붙어 있으면 Spring이 해당 빈을 AOP 프록시 객체로 감쌈
- 프록시는 메서드 호출을 가로채서 트랜잭션 관리나 재시도 로직을 적용
3️⃣ 빈을 다른 클래스에서 주입받아 사용
- @Autowired 또는 생성자 주입을 통해 자동으로 의존성을 주입
- 프록시 객체가 주입되면, 실제 메서드 실행 전에 AOP 기능이 동작
❌ 하지만, 같은 클래스 내부에서 메서드를 직접 호출(Self-Invocation) 하면 프록시를 거치지 않기 때문에 AOP 기능이 적용되지 않습니다.
Self-Invocation 문제와 해결 방법
❌ 잘못된 예제 : 자기 자신을 직접 호출(self-Invocation)
@Service
public class MyService {
@Transactional
public void outerMethod() {
System.out.println("외부 메서드 실행");
innerMethod(); // 같은 클래스 내부에서 직접 호출 → 프록시 미적용
}
@Transactional
public void innerMethod() {
System.out.println("내부 메서드 실행 (트랜잭션 적용 안됨)");
}
}
문제: innerMethod()가 같은 클래스 내부에서 직접 호출되므로, 프록시를 거치지 않고 실행되어 @Transactional이 적용되지 않습니다.
✅ 해결 방법 : 프록시 객체를 통해 호출
@Service
public class MyService {
private final MyService self;
public MyService(@Lazy MyService self) { // ApplicationContext를 통해 주입받아 프록시 적용
this.self = self;
}
public void outerMethod() {
System.out.println("외부 메서드 실행");
self.innerMethod(); // 프록시 객체를 통한 호출 → @Transactional 적용됨
}
@Transactional
public void innerMethod() {
System.out.println("내부 메서드 실행 (트랜잭션 적용)");
}
}
해결: self.innerMethod()는 ApplicationContext에서 주입받은 프록시 객체를 통해 실행되므로, @Transactional이 정상 동작합니다.
✅ 결론
“Spring 컨테이너(ApplicationContext)는 빈을 생성하고 관리하며, 필요할 때 자동으로 주입하는 역할을 한다.”
“AOP 프록시는 Spring 컨테이너가 관리하는 빈을 감싸서 트랜잭션이나 재시도 기능을 적용할 수 있도록 한다.”
'Back-End > Spring' 카테고리의 다른 글
| Spring의 @Configuration (0) | 2025.04.15 |
|---|---|
| Spring AOP Proxy - SelfInvocation 문제 (0) | 2025.03.17 |
| Self-Invocation(자기호출) 문제 (0) | 2025.02.25 |
| SpringSecurity Authentication 객체와 Principal 객체 (0) | 2025.01.24 |
| SpringSecurity + JWT를 활용한 로그인 (0) | 2025.01.23 |