목차
1. 스프링 빈(Spring Bean)?
2. 의존성 주입?
3. 왜 쓰는가
4. 마치며
스프링 빈(Spring Bean)?

스프링 빈(Spring Bean)은 스프링 컨테이너에 의해 관리되는 소프트웨어 컴포넌트이다. 인터넷을 찾다보면 이와 같이 스프링 빈에 대해 소개하는 글을 어렵지않게 찾을 수 있다.
정말 말그대로 스프링 빈은 개발자가 new나 delete등의 키워드를 통해 관리하는 클래스의 인스턴스가 아니라 스프링 컨테이너에서 관리되는 객체이다.
스프링 컨테이너에의해 관리되는 스프링 빈은 프로젝트내에서 하나의 객체 인스턴스만 존재할 수 있다는 특징이 있다. 근데 이 개념은 어디서 들어본 적이 있다. 디자인 패턴 중 클래스의 생성자를 제한하여 최초에 생성한 하나의 객체 인스턴스만을 가질 수 있게 하는 싱글턴 패턴과 유사하다. 다만 스프링 빈은 스프링 컨테이너에 의해 자체적으로 하나의 인스턴스만 가지게 된다는 점이 조금 다르다.
@Configuration
static class BeanConfig {
@Bean
public MemberRepository memberRepository1() {
return new MemoryMemberRepository();
}
@Bean
public MemberRepository memberRepository2() {
return new MemoryMemberRepository();
}
}
스프링 빈은 위와 같이 @Bean이라는 Annotation에 의해 등록할 수 있다. 스프링 빈의 등록같은 경우 다양한 방법이 존재하는데, 사실 위와 같이 @Bean을 선언해서 직접적으로 스프링 빈에 등록하는 경우는 잘 없다. 이에 대한 설명은 다은 목차인 의존성 주입과 함께 설명하겠다.
의존성 주입?
의존성 주입에 관해 검색하면 '스프링 컨테이너가 관리하는 스프링 빈은 의존성 주입을 통해 객체간의 의존성을 주입한다.' 라고 설명이 되어있다. 처음보면 이게 무슨말인가 싶다. 사실 객체지향에 대해 이미 어느정도 알고 있다면 어려운 말은 아니다.
의존성 주입에 관해 알기 위해서는 객체 간의 '의존 관계'라는 말의 의미를 알아야한다. 이것도 어려운 개념이 아니다.
의존 관계?
간단한 예시를 통해 객체간의 의존 관계를 설명하자면, 자동차와 엔진 객체가 있다고 가정해보자. 자동차는 엔진을 필요로 하며, 자동차가 움직이기 위해서는 엔진이 제대로 작동해야 한다. 이 경우 자동차 객체는 엔진 객체에 '의존하고 있다.' 라고 할 수 있다. 즉, 자동차 객체는 엔진 객체를 사용하여 움직이는 기능을 수행하고, 엔진 객체가 정상적으로 작동하는 것에 '의존하고 있다.' 라고 할 수 있는 것이다.
실제 프로젝트에서는 이 자동차 객체를 이용해서 작업을 수행해야하는데, 그러기 위해서는 엔진 객체를 장착(참조)해줘야 한다. 그리고 이 엔진을 장착(참조)하는 과정을 '의존성 주입'이라고 할 수 있다. 이러한 의존성 주입은 생성자를 통해 할 수도 있고 스프링 컨테이너 처럼 외부에서 처리해 줄 수도 있다.
이전 목차에서 스프링 빈(Spring Bean)에 관해 설명하였는데, 스프링에서는 스프링 빈으로 등록된 클래스들에 대해 자동으로 의존성을 주입해준다. 아래의 코드를 보자.
@RestController
@RequestMapping("/user/favorites")
public class FavoritesController {
private final FavoritesService favoritesService;
private final JwtTokenProvider jwtTokenProvider;
private final UserRepository userRepository;
@Autowired
public FavoritesController(FavoritesService favoritesService,
JwtTokenProvider jwtTokenProvider,
UserRepository userRepository) {
this.favoritesService = favoritesService;
this.jwtTokenProvider = jwtTokenProvider;
this.userRepository = userRepository;
}
/*
* 즐겨찾기 목록 조회
*/
@GetMapping
public ResponseEntity<List<FavoritesDto>> getFavoritesByUserId(@RequestHeader("Authorization") String requestAccessToken) throws Exception {
// ...
}
/*
* 즐겨찾기 등록
*/
@PostMapping
public ResponseEntity<FavoritesDto> registerFavoritesInfo(@RequestHeader("Authorization") String requestAccessToken,
@RequestBody FavoritesDto favoritesDto) throws Exception {
// ...
}
/*
* 즐겨찾기 삭제
*/
@DeleteMapping("/{favoritesId}")
public ResponseEntity<FavoritesDto> deleteFavoritesInfo(@RequestHeader("Authorization") String requestAccessToken,
@PathVariable String favoritesId) throws Exception {
// ...
}
}
프로젝트를 진행하면서 작성했던 컨트롤러 코드이다. 클래스에 '@RestController' 라는 Annotation이 붙은 것을 확인할 수 있는데, 이는 이 클래스를 스프링 빈으로 등록하겠다는 의미(실제로는 더 많은 역할을 하나 글의 주제에서 벗어나기에 제외)이다.
또한 이 컨트롤러 객체는 'favoriteService', 'jwtTokenProvider', 'userRepository'등 여러가지 객체를 의존(참조)하고 있음을 알 수 있다. 다만 앞선 목차에서는 @Bean이라는 Annotation을 통해 하나하나 등록해야 했었는데, 이 클래스에서는 @Autowired라는 Annotation을 통해 손쉽게 의존성을 주입하고 있다. 실제로 프로젝트를 진행하면서 대부분의 클래스에 @Autowired를 통해 의존성 주입을 하였다.
사실 위의 코드에서 @Autowited Annotation을 쓰지 않아도 자동으로 의존관계 주입이 된다. 이 클래스에 @RestController Annotation이 선언되어 있기 때문에 이 컨트롤러의 멤버 클래스도 스프링에서 자체적으로 의존성을 주입해주기 때문이다. 근데 개인적으로는 @Autowired 키워드 정도는 명시해주는 것을 선호한다. 이 객체가 의존성 주입을 받았다는 것이 눈에 잘 들어오기도 하고 너무 키워드를 생략해버리면 오히려 가독성이 안좋아진다고 느끼기 때문이다.
왜 쓰는가
스프링 빈에 관해 알아가다 보면 한 가지 의문이 든다. '이걸 왜 쓸까?' 라는 생각이 말이다. 나의 경우 하나만 존재해야할 객체는 싱글턴 패턴으로 생성해서 사용하면 되는게 아닐까 라는 생각을 한 적이 있었다. 물론 불가능 한것은 아니지만 그럼에도 불구하고 스프링 컨테이너가 스프링 빈을 통해 클래스를 관리하는 데에는 몇가지 이유가 있다.
의존 관계에 관해 실수할 여지가 없다.
사람이 하나하나 싱글턴 패턴을 통해 객체를 생성하고 연결하다보면 실수를 하게 될 확률이 높다. 하지만 스프링 빈으로 등록된 클래스들은 스프링 컨테이너가 직접 관리하기에 실수할 일이 없다.
객체의 재사용성과 유연성이 높다.
사람이 직접 싱글턴 패턴을 통해 객체를 사용하는 것보다 스프링 컨테이너에서 직접 등록, 관리하기에 메모리 사용이나 접근 측면에서 훨씬 효율이 좋다. 또한 외부에서 의존 객체를 주입 받기에 기존의 의존 객체를 교체해야 할 때 쉽게 가능하다는 장점이 있다.
그 이외에도 여러가지 장점이 있겠지만 실제로 프로젝트를 진행하면서 가장 와닿았던 장점들은 위와 같았다고 생각한다.
'Backend > Spring' 카테고리의 다른 글
[백엔드/Spring] - mvc 패턴과 스프링 mvc (0) | 2024.02.14 |
---|---|
[Backend/Spring] 클라이언트에서 서버로 요청 데이터 전달 (0) | 2023.11.18 |