Programming/Spring

[Spring] 스프링 빈(Spring Bean), Spring Core 어노테이션

soeun2537 2025. 4. 23. 15:45
우아한테크코스 레벨 2에서 학습한 내용을 정리한 글입니다.
이 글은 IoC 컨테이너와 의존성 주입(DI)에 대한 기본 이해를 바탕으로 작성되었습니다. 관련 내용은 여기에 정리해 두었으니, 참고해 주시면 감사하겠습니다.

 

💭 들어가며

이전 글에서 IoC 컨테이너와 의존성 주입(DI)에 대해 정리했다. 이번에는 그 내용을 바탕으로 스프링 빈(Spring Bean)에 대해 정리하고, 스프링 빈을 보다 편리하게 관리할 수 있도록 도와주는 Spring Core 어노테이션들도 함께 살펴보고자 한다.

 

 

✅ 스프링 빈(Spring Bean)이란

스프링 빈(Spring Bean)은 스프링 IoC 컨테이너가 생성하고 관리하는 객체를 의미한다. 이 객체들은 컨테이너에 의해 생명 주기가 관리되며, 의존성 주입(DI)을 통해 서로 필요한 객체들을 연결해 준다.

스프링 빈을 등록하는 방법은 크게 두 가지가 있다.

  • @Component, @Service, @Repository, @Controller 등을 통한 자동 등록
  • @Bean을 통한 수동 등록

 

1️⃣ @Component 계열 어노테이션을 통한 자동 등록

스프링은 클래스패스를 스캔하여 @Component 계열 어노테이션이 붙은 클래스를 자동으로 스프링 빈으로 등록한다. 이 어노테이션들은 스테레오타입 어노테이션이라고 불리며, 모두 @Component를 메타 어노테이션으로 가지고 있다. 기능적으로는 동일하지만, 개발자의 의도를 명확하게 표현해 주는 역할을 한다.

🔽 @Component

  • 가장 기본적인 컴포넌트 등록 어노테이션이다.
  • 특정 계층에 속하지 않는 일반적인 빈 등록에 적합하다.
  • 나머지 세 어노테이션(@Controller, @Service, @Repository)의 기반이 되는 어노테이션이다.
@Component
public class NotificationSender {
    public void send(String message) {
        System.out.println("Sending: " + message);
    }
}

🔽 @Controller

  • 웹 요청을 처리하는 프레젠테이션 계층에서 사용하는 어노테이션이다.
  • @Component처럼 빈으로 등록되며, 추가로 DispatcherServlet이 요청을 매핑할 수 있는 핸들러로 인식된다.
@Controller
public class UserController {

    @GetMapping("/users")
    public String listUsers(Model model) {
        model.addAttribute("users", List.of("미소", "소은"));
        return "userList"; // View 이름 반환
    }
}

🔽 @Service

  • 비즈니스 로직을 담당하는 서비스 계층에서 사용하는 어노테이션이다.
  • 계층 분리를 명확히 하여 코드의 가독성을 높인다.
  • 추후 AOP 적용이나 도구에서 서비스 레이어 인식이 필요할 때 유리하다.
@Service
public class UserService {

    public List<String> findAllUsers() {
        return List.of("미소", "소은");
    }
}

🔽 @Repository

  • 데이터 접근 계층(DAO)에서 사용하는 어노테이션이다.
  • 스프링이 제공하는 예외 변환 기능을 통해, JDBC 예외(SQLException 등)를 스프링 예외(DataAccessException 등)로 변환해 준다.
  • JPA, MyBatis에서도 자주 활용된다.
@Repository
public class UserRepository {

    public List<String> findAll() {
        return List.of("Alice", "Bob");
    }
}

 

2️⃣ @Configuration과 @Bean을 통한 수동 등록

자동 등록이 어려운 경우, 예를 들어 외부 라이브러리 객체를 빈으로 등록하거나 설정 값이 필요한 객체의 경우에는 수동 등록을 사용하는 것이 더 유리하다. 이때 사용하는 어노테이션이 @Configuration과 @Bean이다.

🔽 @Configuration

  • 해당 클래스가 스프링 설정 클래스임을 명시하는 어노테이션이다.
  • 내부에 정의된 @Bean 메서드를 통해 필요한 객체를 직접 생성하고 빈으로 등록한다.
@Configuration
public class AppConfig {
	// 설정
}

🔽 @Bean

  • 메서드가 반환하는 객체를 스프링 컨테이너에 빈으로 등록하도록 지시하는 어노테이션이다.
  • 메서드의 반환 객체를 스프링 빈으로 등록한다.
  • 메서드의 파라미터로 다른 빈을 주입받을 수 있어, 생성자 주입과 유사한 방식으로 의존성을 주입할 수 있다.
@Configuration
public class AppConfig {

    @Bean
    public PaymentService paymentService() {
        return new PaymentService();
    }
	
    @Bean
    public OrderService orderService(MemberRepository repository) {
        return new OrderService(repository); // repository는 이미 빈으로 등록된 상태
    }
}
@Bean 메서드는 기본적으로 호출될 때마다 새로운 객체를 생성하지 않는다. 스프링 컨테이너가 미리 생성한 싱글톤 빈을 반환한다.

 

 

✅ @ComponentScan

그렇다면 @Component 계열 어노테이션을 통해 빈을 등록할 때, 별도로 등록하지 않아도 되는 이유는 무엇일까? 그 비밀은 바로 @ComponentScan 어노테이션에 있다.

 

▶ 기본 동작 방식

@ComponentScan은 스프링이 지정된 패키지를 기준으로 하위 패키지를 탐색하며, @Component 계열 어노테이션이 붙은 클래스를 자동으로 빈으로 등록한다.

@ComponentScan(basePackages = "com.example.myapp")
  • basePackages에 지정된 패키지를 기준으로 하위 패키지를 스캔한다.
  • 기본값은 @ComponentScan이 선언된 클래스가 속한 패키지를 기준으로 동작한다.

 

▶ 빈 스캔 구조 시각화

@ComponentScan
└── com.example.myapp
     ├── controller   → @RestController MyController
     ├── service      → @Service MyService
     ├── repository   → @Repository MyRepository
     └── config       → @Configuration MyConfig

@ComponentScan은 com.example.myapp 패키지를 기준으로 하위 패키지를 탐색하여, 각 계층에서 적절히 사용된 스테레오타입 어노테이션을 통해 의미 기반으로 빈을 등록한다.

@Configuration 클래스는 @ComponentScan의 스캔 대상이 되지만, 그 안의 @Bean 메서드는 실행 시점에 빈으로 등록된다.

 

 

✅ @SpringBootApplication

@SpringBootApplication은 스프링 부트에서 가장 자주 사용되는 어노테이션이자 애플리케이션의 진입점이다. 우리가 별다른 설정 없이도 애플리케이션을 실행할 수 있는 이유는, 이 어노테이션이 위에서 배운 핵심 어노테이션을 포함하고 있기 때문이다.

  • @SpringBootConfiguration: @Configuration의 특수한 형태로, 스프링 부트 애플리케이션의 기본 설정 클래스임을 나타낸다. ApplicationContext를 구성하는 데 사용되며, 특히 통합 테스트 시 애플리케이션 설정을 식별하는 기준점으로 활용된다.
  • @EnableAutoConfiguration: 스프링 부트의 핵심 기능 중 하나로, 설정을 자동으로 구성해 주는 다양한 클래스를 조건에 맞게 로딩한다.
  • @ComponentScan: 현재 클래스가 위치한 패키지를 기준으로 하위 패키지의 컴포넌트들을 자동으로 스캔하여 빈으로 등록한다.

따라서 애플리케이션의 시작 클래스에 @SpringBootApplication 하나만 선언해도, 스프링 부트는 자동으로 컴포넌트 스캔과 의존성 주입을 설정해 주어 개발자는 훨씬 편리하게 애플리케이션을 구성할 수 있다.

 

 

📍 참고 자료