leelee.log

[Spring] Bean Scope 본문

개발/Backend

[Spring] Bean Scope

leeleelee3264 2020. 3. 31. 23:15

spring framework

 Scope라는 단어를 처음 봤을 때는 javascript에서 함수를 배우고 있었다. 사전에서 Scope라는 뜻을 찾아보면 '범위'라고 나온다. 오늘 다루는 주제는 Bean의 Scope, 즉 범위이다.


 Bean은 일반 자바 객체와는 조금 다른 형태의 스코프를 가지고 있다. Bean을 스프링에서 사용하는 자바 객체라고 말을 하고는 했었는데 특별한 설정을 하지 않았을 때 Bean은 singleton 형태로 만들어진다. spring을 동작 시킬 때 딱 한 번 객체가 만들어지고 계속 사용한다는 뜻이다. Spring에서 annotation을 사용하지 않는 일반 자바 객체들은 사용을 할 때 마다 생성자로 만드는 prototype 형태로 만들어지는데 알고보니 Bean또한 prototype 형태로 만들 수 있었다. 즉, 빈을 호출할 때 마다 빈을 새로 만들어 오는 것이다!

 그런데 빈을 싱글톤 형태 말고 다른 형태로 사용하는 일은 거의 없다고 한다. 성능 상으로 볼때도 객체를 매번 만드는 것 보다는 한 번 만들고 계속 재활용을 하는게 좋다. 그러니 이번 포스팅에서는 아주 기본적인 개념만 다뤄보도록 하겠다. (예전에 xml로 bean을 만들던 때에는 scope가 네 종류가 있었다. 이미 알고 있는 singleton, prototype, 그리고 http 세션마다 객체를 만드는 session, request마다 객체를 만드는 request가 있다.)

 

 

 빈을 프로토타입으로 만들기 위해서는 빈을 만들기 위해 필수로 사용해야하는 @Component를 써주고, 그 옆에 scope를 지정해주는 @Scope를 사용하면 된다. scope에 들어갈 수 있는 인자는 singleton, prototype, session, request, globalsession이 있다. globalsession은 서블릿과 비슷한 개념인 포틀릿에서 사용한다는데 자세히 살펴보지는 않았다. 빈을 만드는 또 하나의 방법인 @Bean 옆에도 @Scope()를 추가해서 스코프를 바꿔줄 수 있다.

 

 

 Single 은 빈에 아무런 설정을 하지 않아 싱글톤 형태로 만들었고 Proto는 Scope를 주어 프로토타입으로 만들었다. 예제에서는 IoC에서 빈을 꺼내오는 객체 ApplicationContext를 이용하여 각자의 빈을 꺼내보았다.

 

 

  결과는 위와 같다. Single 빈은 호출 할 때 마다 같은 빈을 돌려주기 때문에 같은 값을 가지고 있지만 Proto는 빈을 호출할 때 마다 새로 만들어서 반환을 해주기 때문에 값이 다 다르다.

 

 Spring에서 코딩을 하다보면 자주 있는 일이지만 다른 객체를 참조해서 사용해야한다. 심지어 내가 자주 사용하는 생성자 주입 법은 객체를 만들 때 생성자의 파라메터로 참조하는 객체들을 넣어주기 까지 해야 한다. 모든 빈을 통일성 있게 싱글톤으로 만들었으면 좋았겠지만 상황에 따르다 보면 빈을 프로토타입으로 만들어야 할 때가 있다. 이런 상황에서 싱글톤 빈을 프로토타입 빈이 참조하거나, 반대의 상황이 있다면 무슨 일이 일어날까?

 프로토타입 빈에서 싱글톤 빈을 참조하는 것에는 아무런 문제가 없다. 싱글톤 빈은 스프링 실행 시작시 딱 한 번 만 만들어지기 때문에 프로토타입 빈에서 참조하는 싱글톤 빈도 같은 빈이기 때문이다. 문제는 싱글톤 빈이 프로토타입 빈을 참조할 때 일어난다. 방금 말 한 것 처럼 싱글톤 빈은 서버 시작시 딱 한 번 만들어진다. 그래서 싱글톤 빈이 참조하는 프로토타입 빈의 경우에도 스프링을 막 시작 했을 때 만들어진 것이 계속 사용이 된다. 즉, 싱글톤 안에서 사용이 되는 프로토타입 빈은 몇 번 호출이 되어도 다 똑같은 빈이라는 소리다.

 이러면 프로토타입 빈을 쓰는 이유가 없게 되어버린다. 어떻게 해야 프로토타입의 빈 특성에 맞추면서도 싱글톤 빈에 참조가 될 수 있을까? 프로토타입 빈을 만들어줄 때 @Scope의 또 다른 파라메터 proxyMode 를 사용하면 된다. 예시를 보도록 하겠다.

 

 

 

  Proto 빈을 만들어 줄 때 Scope를 prototype으로 지정해주고 ProxyMode를 사용해준다. Proxy라는 개념을 ProxyPattern을 배웠기 때문에 알고 있다. 분명 중요한 알맹이를 Proxy가 감싸고 있는 형태의 무엇인게 분명하다. 실제로 ProxyMode를 알아보면 결국 prototype 빈을 proxy 객체가 한 번 감싸고 있고, 싱글톤 형태의 빈은 자기는 프로토타입의 빈을 직접 참조하고 있다고 믿고 있지만 싱글톤 빈은 proxy 객체를 직접 참조하고 있는 모양이 된다. 그래서 싱글톤이 참조한 프로토타입의 빈을 호출 할 때 마다 Proxy가 받아와서 새로운 프로토타입을 만들어준다. Proxy객체는 유령과 같아서 프로토타입의 빈을 감쌌지만 return타입 등은 그대로 프로토타입의 빈을 사용한다. 매우 편리한 기능이다.  proxyMode에 대한 더 자세한 설명은 밑의 블로그에서 볼 수 있다.

https://renuevo.github.io/spring/scope/spring-scope/

 

[Spring] Spring의 Scope와 ProxyMode 알아보기

Spring Scope Spring Bean을 생성시 적용되는 Scope에 대해 알아보려 합니다 아래 모든 소스의 제 Github에서 확인 하실 수 있습니다 해당 포스팅은 내부적 구현 요소의 대해 설명에 집중하며 Scope의 일반적인 사용법을 보시려면 👉 baeldung - spring-bean-scopes Spring Bean의 Scope는 크게 2가지가 존재합니다 Spring의 내부 를 보면 과…

renuevo.github.io

 

 

 코드가 좀 생략이 되었는데 proxyMode를 사용하여 Proto빈을 만들어주고 Single 에서 참조하도록 하였다. single에서 proto빈을 호출 할 수 있도록 getProto()를 만들었고, Runner에서 single 빈이 proto빈을 거듭 호출하고 있다. 우리가 원하는, 싱글톤 형태의 빈이 프로토타입 빈을 호출 할 때마다 프로토타입 특성에 맞게 매번 빈을 새로 만들어서 반환을 해했다면 결과로 proto 객체의 값이 달라야 한다.

 

 

 결과 화면이다. getProto로 가져온 Proto 빈의 정보가 다 다르다! 우리가 원하는 형태로 구현이 된 것이다. 빈의 Scope에 대해서는 여기까지 다루도록 하겠다. 사실 프로토타입의 Scope는 잘 쓰지 않는다고 해서 좀 대충다룬 면이 있다... Spring이 지원해주는 기능에는 이런 부분도 있다, 정도로만 알아두려고 한다. 그래도 Bean의 생명주기와 범위를 알게 되어 좋았고 session이나 request별로 빈을 만들어주는 기능은 나중에 가서 서비스를 구현하면서 쓸 일이 있을 것 같아 알아두는게 좋을 것 같다.


 여태 코딩을 하면서 프로토타입의 빈을 사용해 보지는 않은 것 같다. 어떤 경우에 프로토타입을 쓸지 궁금해서 찾아보니 객체 하나하나의 상태를 기억해야하는 (stateful)한 상황에서 쓰인다고 한다.  real-time에서 반응력을 개선하기 위해 프로토타입을 쓴다는 의견도 있었는데, 반응력을 개선하기 위해서는 퍼포먼스가 개선되어야 하는데 프로토타입 처럼 응답마다 객체를 만들면 결국 퍼포먼스가 떨어져서 싱글톤 형태가 더 좋다는 의견도 있었다.

 한가지 확실한 점은 대부분의 사람들이 빈을 프로토타입 보다는 싱글톤 형태로 만들라고 조언을 하고 있었다.