스프링 빈 (Spring Bean)

__〆( ̄ー ̄ ) ·2025년 1월 23일

스프링 부트

목록 보기
2/9

빈(Spring Bean)이란?

빈은 이전의 IoC/DI에서 했던 스프링에 의해 생성되고 관리되는 객체를 의미해요. 이전 장에서의 예시를 다시 볼까요?

// IoC가 적용된 프로그램
public class Car {
    private final Engine engine;

    public Car(Engine engine) { // Car는 Engine을 외부에서 주입받음
        this.engine = engine;
    }

    public void run() {
        engine.start();
    }
}

여기서 자동차(Car)와 엔진(Engine)은 서로 의존 관계에 있어요. 이 코드에서는 생성자를 통해 주입되는 engine 객체가 바로 빈입니다.

빈은 스프링 프레임워크에 의해 생성되어 IoC 컨테이너에 보관되어 있다가 필요로 하는 곳에 주입돼요. 스프링 부트에서 빈은 어노테이션을 통해 정의되고 스프링 부트는 @ComponentScan 어노테이션을 통해 지정된 빈들을 찾아 생성해요.

빈(Bean) 지정

빈을 지정하는 방법에는 크게 두 가지로 @Component@Bean 어노테이션을 사용해요.

먼저 Engine 클래스를 @Component 어노테이션으로 빈을 지정하는 방법을 볼게요.

@Component
public class Engine {
    public void start() {
       ...
    }
}

위와 같이 일반적인 Java 클래스에 @Component 어노테이션만 지정해주면 스프링에서 빈으로 생성해요. 대부분 위와 같은 방법으로 빈을 지정하고 스프링 부트에서 많이 사용하는 @Service, @Controller, @Repository, @Configuration 어노테이션들도 모두 @Component 어노테이션을 포함하고 있어요.

다음은 @Bean 어노테이션을 사용한 방법이에요.

@Configuration
public class AppConfig {

    @Bean
    public Engine engine() {
        return new Engine();
    }    
}

@Configuration 어노테이션을 적용한 클래스에 @Bean 어노테이션을 적용해 빈을 지정할 수 있어요. 이런 방식은 라이브러리가 팩토리나 빌더 패턴을 사용하도록 하는 경우나 레거시 모듈의 객체를 빈으로 사용하고 싶을때 주로 사용해요.

스프링 부트는 @ConponentScan 어노테이션이 지정된 클래스 패키지를 포함한 하위 패키지에서 빈들을 찾아 생성해요 따로 패키지 경로를 지정할 수도 있고 포함시키지 않을 수도 있어요.. @SpringBootApplication 어노테이션이 @ComponentScan 어노테이션을 포함하고 있어요.

빈(Bean)의 주입 (DI)

빈의 의존 관계를 설정하고 주입하는 방식은 필드 주입, 세터 주입, 생성자 주입 3가지가 있어요.

필드 주입

@Service
public class MyService {
	@Autowired
    private MyRepository myRepository;
    ...
}

필드에 @Autowired 어노테이션을 사용하여 의존성을 주입하는 방식으로 가장 코드가 간결하지만 빈들 간의 순환 참조 문제를 컴파일 시점에 발견하기 어려워 권장하지 않는 방식이에요.

세터 주입

@Service
public class MyService {	
    private MyRepository myRepository;
    
    @Autowired
    public void setMyRepository(MyRepository myRepository) {
    	this.myRepository = myRepository;
    }
    ...
}

@Autowired 어노테이션을 세터에 적용하는 방식이에요. 선택적으로 의존성을 주입할 수 있고 중간에 의존성을 변경할 수 있는 장점이 있어요. 다만 이에 따라 별도의 Null 처리가 필요하고 의존성을 변경할 수 있기 때문에 의존성이 명확하지 않는 단점이 있어요.

생성자 주입

@Service
public class MyService {	
    private final MyRepository myRepository;
    
    public MyRepository(MyRepository myRepository) {
    	this.myRepository = myRepository;
    }
    ...
}

별도의 어노테이션 없이 의존성 객체를 final로 선언하고 생성자를 통해 주입받는 방식이에요. 의존성에 대한 불변성을 확보할 수 있고 순환 의존성을 미연에 방지할 수 있는 등 장점이 많은 방식으로 가장 권장되는 방식입니다.

다만 세터 주입과 마찬가지로 코드가 길어지는 단점이 있는데 Lombok@RequiredArgsConstructor 어노테이션을 통해 생성자 선언을 대체할 수 있어요.

같은 타입의 빈이 여러개 인 경우
스프링은 같은 타입의 빈이 여러개 인 경우 적합한 빈을 지정해주지 않으면 오류가 발생해요.
@Primary 또는 @Qualifier과 같은 어노테이션을 통해 빈을 지정해주는 것으로 해결할 수 있어요.

빈(Bean)의 스코프 (Scope)

빈은 기본적으로 싱글톤으로 애플리케이션이 실행될 때 객체가 모두 생성되어 IoC 컨테이너에 보관돼요.

싱글톤 (Singleton) 패턴
싱글톤 패턴은 객체 지향 프로그래밍에서 특정 클래스의 인스턴스를 하나만 생성하고, 해당 인스턴스에 대한 전역 접근 지점을 제공하는 디자인 패턴이에요.

따라서 객체들 간의 빈을 공유하거나 멀티 스레드 환경에서 빈을 사용할 때 주의가 필요해요. 일반적으로 빈은 상태를 나타내는 속성을 가지고 있지 않는 것이 바람직해요.

필요에 따라 여러 개의 독립적인 빈을 필요로 하는 경우 스코프를 별도로 지정해줄 수 있어요. 빈의 스코프는 singleton (기본), prototype (매번), request (요청 시), session (한 세션에서), webSocket (소켓 연결 시) 등이 있어요. 적용하는 예시는 다음과 같습니다.

@Component
@Scope("prototype")
public class MyPrototypeBean {
    // ...
}

싱글톤 외에 다른 스코프를 적용하고자 하는 경우 메모리 누수나 자원 관리에 신경써야 하기 때문에 일반적으로는 잘 사용하지 않아요.

profile
뭐라도 적자

0개의 댓글