
마이크로서비스 아키텍처(MSA)에서 스프링 빈은 의존성 주입을 통해 애플리케이션의 구성 요소를 관리하는 중요한 역할을 합니다. 이번 포스트에서는 스프링 빈의 세부 정의 중 @Primary와 @Lazy 어노테이션에 대해 자세히 살펴보겠습니다.
@Primary 어노테이션
개요
@Primary 어노테이션은 같은 타입의 빈이 여러 개 존재할 때, 어떤 빈을 우선적으로 의존성 주입할 것인지를 지정하는 데 사용됩니다. 이는 스프링이 빈을 주입할 때 혼란을 방지하고, 특정 빈을 기본적으로 선택할 수 있도록 합니다.
사용 예
예를 들어, MyService라는 클래스의 두 개의 구현체가 있을 때, 하나의 구현체에 @Primary 어노테이션을 추가하여 우선적으로 주입하도록 설정할 수 있습니다.
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
@Service
public class MyServiceImpl1 implements MyService {
@Override
public String processData(String data) {
return "Processed by Implementation 1: " + data;
}
}
@Service
@Primary
public class MyServiceImpl2 implements MyService {
@Override
public String processData(String data) {
return "Processed by Implementation 2: " + data;
}
}이 경우, MyService 타입의 빈이 주입될 때 MyServiceImpl2가 기본적으로 선택됩니다.
Configuration 클래스에서의 사용
@Configuration
public class AppConfig {
@Bean
public Employee johnEmployee() {
return new Employee("John");
}
@Bean
@Primary
public Employee tonyEmployee() {
return new Employee("Tony");
}
}중복 이름 오류 처리
같은 이름을 가진 빈이 존재할 경우 오류가 발생할 수 있습니다. 이를 방지하기 위해 application.properties 파일에서 다음과 같이 설정할 수 있습니다:
spring.main.allow-bean-definition-overriding=false이 설정을 통해 빈 정의가 중복될 경우 오류를 체크할 수 있습니다.
@Primary vs @Qualifier 비교
// @Primary 사용 - 기본 빈 지정
@Service
@Primary
public class DatabaseEmailService implements EmailService {
// 기본 이메일 서비스
}
// @Qualifier 사용 - 명시적 빈 선택
@Autowired
@Qualifier("smsNotificationService")
private NotificationService notificationService;@Lazy 어노테이션
개요
@Lazy 어노테이션은 스프링 빈 컨테이너가 설정을 로딩할 때 빈 객체를 즉시 생성하는 것이 아니라, 의존성 주입이 실제로 발생하는 시점에 빈 객체를 생성하도록 지시합니다. 이를 통해 애플리케이션의 성능을 최적화하고, 불필요한 리소스 소모를 방지할 수 있습니다.
사용 예
다음은 @Lazy 어노테이션을 사용하여 빈 객체의 생성을 지연시키는 예시입니다:
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Component
@Lazy
public class ExpensiveService {
public ExpensiveService() {
System.out.println("ExpensiveService 생성 - 리소스 집약적 작업 수행");
// 무거운 초기화 작업
}
public void performTask() {
System.out.println("작업 수행 중...");
}
}
@Component
public class MyComponent {
private final ExpensiveService expensiveService;
@Autowired
public MyComponent(@Lazy ExpensiveService expensiveService) {
this.expensiveService = expensiveService; // 실제 사용 시점에 생성
}
public void useService() {
expensiveService.performTask(); // 이 시점에 ExpensiveService 생성
}
}Configuration 클래스에서의 사용
@Configuration
public class AppConfig {
@Bean
@Lazy
public DatabaseConnection databaseConnection() {
System.out.println("데이터베이스 연결 생성 중...");
return new DatabaseConnection();
}
@Bean
public DataService dataService(@Lazy DatabaseConnection connection) {
return new DataService(connection);
}
}전역 Lazy 초기화 설정 (Spring Boot 2.2+)
# application.yml
spring:
main:
lazy-initialization: true# application.properties
spring.main.lazy-initialization=trueprototype 스코프와의 차이
@Lazy와 prototype 스코프는 비슷한 점이 있지만, 중요한 차이점이 있습니다:
- prototype 스코프: 의존성 주입이 발생할 때마다 새로운 객체를 생성
- @Lazy: 설정 시점에는 생성하지 않고, 실제 필요할 때만 생성하므로 메모리 사용을 줄이는 데 유리
// Prototype 스코프 - 매번 새 인스턴스
@Component
@Scope("prototype")
public class PrototypeService {
// 매번 새로운 인스턴스 생성
}
// Lazy 초기화 - 지연 생성, 이후 재사용
@Component
@Lazy
public class LazyService {
// 첫 사용 시에만 생성, 이후 재사용
}실무 활용 패턴
조건부 @Primary 사용
@Configuration
public class DatabaseConfig {
@Bean
@Primary
@ConditionalOnProperty(name = "database.type", havingValue = "mysql")
public DataSource mysqlDataSource() {
return new MySQLDataSource();
}
@Bean
@ConditionalOnProperty(name = "database.type", havingValue = "postgresql")
public DataSource postgresDataSource() {
return new PostgreSQLDataSource();
}
}@Lazy와 순환 의존성 해결
@Service
public class ServiceA {
private final ServiceB serviceB;
public ServiceA(@Lazy ServiceB serviceB) {
this.serviceB = serviceB; // 순환 의존성 해결
}
}
@Service
public class ServiceB {
private final ServiceA serviceA;
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
}성능 최적화를 위한 @Lazy 활용
@Service
public class ReportService {
@Autowired
@Lazy
private EmailService emailService; // 이메일 발송이 필요할 때만 초기화
@Autowired
@Lazy
private PdfGeneratorService pdfService; // PDF 생성이 필요할 때만 초기화
public void generateReport(ReportType type) {
if (type == ReportType.EMAIL) {
emailService.sendReport(); // 이 시점에 EmailService 초기화
} else if (type == ReportType.PDF) {
pdfService.generatePdf(); // 이 시점에 PdfGeneratorService 초기화
}
}
}결론
@Primary와 @Lazy 어노테이션은 MSA에서 스프링 빈을 보다 유연하고 효율적으로 관리하는 데 중요한 역할을 합니다.
@Primary는 빈의 우선순위를 설정하여 의존성 주입의 혼란을 줄이고, @Lazy는 성능 최적화를 통해 불필요한 리소스 소모를 방지합니다.
핵심 활용 가이드라인:
- @Primary: 기본 구현체가 명확한 경우 사용, 테스트 시 @Qualifier와 함께 활용
- @Lazy: 리소스 집약적이거나 선택적으로 사용되는 빈에 적용
- 순환 의존성 해결: @Lazy를 활용하여 의존성 순환 문제 해결
- 성능 최적화: 애플리케이션 시작 시간 단축을 위한 전략적 @Lazy 적용
이러한 어노테이션을 적절하게 활용하여 스프링 애플리케이션의 구조를 더욱 견고하게 만들어 보시기 바랍니다.
'IT기술 > MSA (with. springboot)' 카테고리의 다른 글
| Spring Boot DTO 패턴 완벽 가이드: MSA 환경에서의 효율적인 데이터 전송 객체 활용 (2) | 2025.07.07 |
|---|---|
| Spring Bean, Java Bean, DTO, VO와 불변 클래스 설계 완벽 가이드: MSA 환경에서의 객체 관리 전략 (0) | 2025.07.07 |
| Spring Bean 생명주기와 BeanPostProcessor 완벽 가이드: MSA 환경에서의 효율적인 빈 관리 (0) | 2025.07.05 |
| Spring Bean Scope 완벽 가이드: MSA 환경에서의 객체 생명주기 관리 (0) | 2025.07.04 |
| Spring Framework ApplicationContext 완벽 가이드: MSA 환경에서의 핵심 컨테이너 활용법 (0) | 2025.07.04 |