Spring Boot에서 자주 사용하는 애노테이션은 크게 세 가지 유형으로 나눌 수 있습니다: 컨테이너에 빈을 등록하는 애노테이션, 컨테이너에서 빈을 주입받는 애노테이션, 그리고 기능을 제공하는 애노테이션입니다. 전자는 제어의 역전(IoC), 후자는 의존성 주입(DI)의 개념과 연결됩니다.
컨테이너에 빈 등록 애노테이션
이 유형의 애노테이션은 클래스나 메서드가 생성한 객체를 Spring IoC 컨테이너가 관리하도록 선언합니다.
1. @Component
클래스 레벨에 적용되어 일반 POJO 클래스를 Spring 컨테이너의 빈으로 등록합니다. @Controller, @Service, @Repository 등 많은 애노테이션이 내부적으로 @Component를 포함합니다.
2. @Bean
메서드 레벨에 적용되며, XML 설정에서 <bean>을 정의하는 것과 동일합니다. 이 메서드는 일반적으로 엔티티 객체를 반환하며, Spring은 이 메서드를 한 번만 호출하여 반환된 객체를 컨테이너에서 관리합니다. 빈의 이름은 기본적으로 메서드 이름입니다.
3. @Configuration
클래스 레벨에 적용되어 해당 클래스가 설정 클래스임을 나타냅니다. 내부에 선언된 @Bean 메서드의 반환 객체를 Spring 컨테이너에 등록하며, 이는 @Component를 기반으로 동작합니다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
String value() default "";
}
4. @ConfigurationProperties
설정 파일(예: application.properties)의 값을 객체에 매핑합니다. prefix 속성으로 접두사를 설정하고, 나머지 키 이름이 객체의 속성명과 일치하면 자동으로 주입됩니다. 이를 위해 객체는 해당 속성의 getter와 setter 메서드를 제공해야 하며, Spring은 리플렉션을 통해 setter를 호출합니다.
@ConfigurationProperties(prefix = "spring.datasource.test1")
public class Datasource1 {
private String url;
private String username;
private String password;
// getter, setter 메서드
}
# application.properties
spring.datasource.test1.url=jdbc:mysql://localhost:3307/multipledatasource1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
spring.datasource.test1.username=root
spring.datasource.test1.password=root
spring.datasource.test1.driver-class-name=com.mysql.cj.jdbc.Driver
5. @Value
설정 파일의 단일 값을 변수에 주입합니다. @ConfigurationProperties와 달리 필드 레벨에 적용되며, 변수 이름이 설정 파일의 키와 일치할 필요는 없지만 SpEL 표현식(${...}) 내의 키는 일치해야 합니다.
@Value("${myapp.config.value}")
private String customVariable;
# application.properties
myapp.config.value=someValue
6. @RestController, @Controller, @Service, @Repository
이 네 가지 애노테이션은 모두 @Component를 기반으로 하며, 기능적으로는 유사합니다. @RestController는 @Controller와 @ResponseBody의 조합입니다. Spring 5.x 이전에는 구조적 계층 구분(웹, 서비스, 데이터 액세스)을 위해 사용되었지만, 내부 구현은 동일합니다. 예를 들어 Controller 계층에 @Service를 사용해도 Spring은 정상적으로 동작하며, 이는 계층 구분을 위한 관례입니다.
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
String value() default "";
}
컨테이너에서 빈 주입 애노테이션
이 유형의 애노테이션은 의존성 주입(DI)을 구현하며, 컨테이너가 관리하는 빈을 필요한 곳에 주입합니다.
1. @Resource
기본적으로 이름을 기준으로 빈을 주입하며, 일치하는 이름이 없으면 타입을 기준으로 주입합니다. name과 type 속성을 모두 지정할 수 있으며, 둘 다 생략하면 byName 방식으로 동작합니다.
@Resource(type = ShiroService.class, name = "shiroService")
private ShiroService shiroService;
주입 순서: name과 type 모두 지정 → 정확히 일치하는 빈 검색, name만 지정 → 이름 일치 검색, type만 지정 → 타입 일치 검색(유일해야 함), 모두 미지정 → byName 자동 주입.
2. @Autowired
기본적으로 타입을 기준으로 빈을 주입합니다. required 속성을 false로 설정하면 일치하는 빈이 없을 때 null을 허용하고 예외를 방지합니다.
@Autowired(required = false)
private ShiroService shiroService;
3. @Qualifier
@Autowired와 함께 사용하여 특정 이름의 빈을 지정합니다. 동일한 타입의 빈이 여러 개 있을 때 모호성을 해결합니다. @Resource의 name 속성 역할과 유사합니다.
@Autowired
@Qualifier("adminDAO")
private AdminDAO adminDAO;
기능 제공 애노테이션
이 애노테이션들은 특정 기능(예: 자동 설정, 비동기, 스케줄링)을 활성화하거나 구성합니다.
1. @SpringBootApplication
메인 클래스에 적용되며, @SpringBootConfiguration, @EnableAutoConfiguration, @ComponentScan을 통합합니다. @SpringBootConfiguration은 설정 클래스임을, @EnableAutoConfiguration은 Spring Boot의 자동 설정을 활성화하고, @ComponentScan은 동일 패키지의 컴포넌트를 자동 스캔합니다.
2. @EnableConfigurationProperties
@ConfigurationProperties가 적용된 클래스를 Spring 컨테이너의 빈으로 등록합니다. @ConfigurationProperties만 사용하면 동작하지 않으며, 이 애노테이션을 메인 클래스나 설정 클래스에 추가해야 합니다.
3. @Async와 @EnableAsync
@Async는 메서드를 비동기로 실행하도록 지정하며, @EnableAsync는 애플리케이션에서 비동기 기능을 활성화합니다(주로 메인 클래스에 추가). @Async를 사용하면 메서드가 별도의 스레드에서 실행됩니다.
@RestController
public class AsyncController {
@Autowired
private AsyncService asyncService;
@RequestMapping("/syncTest")
public String syncTest() {
System.out.println("start sync");
asyncService.syncMethod();
System.out.println("end sync");
return "done";
}
@RequestMapping("/asyncTest")
public String asyncTest() {
System.out.println("start async");
asyncService.asyncMethod();
System.out.println("end async");
return "done";
}
}
@Service
public class AsyncService {
public void syncMethod() {
System.out.println("begin sync work");
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
System.out.println("end sync work");
}
@Async
public void asyncMethod() {
System.out.println("begin async work");
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
System.out.println("end async work");
}
}
실행 결과: syncTest는 순차적으로 출력되지만, asyncTest는 start async와 end async가 먼저 출력되고 begin async work가 나중에 출력됩니다(비동기 실행). 이는 @Async가 새로운 스레드를 생성하는 것과 유사합니다.
4. @Scheduled와 @EnableScheduling
@Scheduled는 메서드를 정기적으로 실행하도록 예약하며, @EnableScheduling은 스케줄링 기능을 활성화합니다(메인 클래스에 추가). cron 표현식을 사용하여 실행 주기를 정의합니다.
@Scheduled(cron = "0/5 * * * * ?")
public void periodicTask() {
System.out.println(new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").format(new Date()));
}