Spring/핵심원리

[스프링 핵심원리 - 기본편] 내용 정리7

슈코 2023. 9. 4. 22:59

빈 스코프

싱글톤

기본 스코프, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프

 

프로토타입

  • @Scope("prototype")
  • 빈의 생성과 의존관계 주입까지만 관여, 소멸은 신경 x ( 짧은 범위의 스코프 )
  • @PreDestroy같은 종료 메소드 호출 x
  • 조회한 클라이언트쪽에서 관리해야한다( ex) 종료 메소드 호출 )

 

프로토타입 스코프 - 싱글톤 빈과 함께 사용할때 문제

프로토타입 스코프의 빈은 스프링 컨테이너에 요청하면 항상 새로운 객체를 생성해서 반환한다.

그렇지만, 프로토타입을 싱글톤 빈과 함께 사용하는 경우 싱글톤 빈이 스프링 컨테이너에 등록되는 순간 프로토타입도 생성되면서 유지되는 문제가 있다

싱글톤빈에서 프로토타입을 의존관계로 주입하는 순간, 그 프로토타입은 스프링 컨테이너의 관리 대상이 되는 것 이다

그렇기 때문에, 스프링 컨테이너의 관리를 받지않고 사용하려는 의도에서 벗어나는 결과가 나온다

싱글톤빈과 프로토타입을 함께 사용하려면, Provider를 사용해야 한다

 

Provider

 @Autowired
private Provider<PrototypeBean> provider;

public int logic() {
	PrototypeBean prototypeBean = provider.get();
    	prototypeBean.addCount();
    	int count = prototypeBean.getCount();
    	return count;
}

다음과 같이 provider를 생성하여 프로토타입 빈을 사용할때마다 .get() 을 통해서 새로운 프로토타입 빈을 생성한다.

( get() 호출 시 스프링 컨테이너를 통해 해당 빈을 찾아 반환(DL) )

 

그렇지만, 실무에서는 대부분 싱글톤 빈으로 문제를 해결할 수 있어서 직접적으로 사용하는 경우는 많지 않다.

DL이 필요한 경우 사용

 

 

웹 스코프

  • 웹 환경에서만 동작
  • 스프링이 종료시점까지 관리( 종료 메소드 호출 )
  • request
    • 웹 요청이 들어오고 나갈때까지 유지되는 스코프
    • 각각 HTTP 요청마다 별도의 빈 인스턴스가 생성되고, 관리된다
  • session
    • 웹 세션이 생성되고 종료까지 유지되는 스코프
    • HTTP Session과 동일한 생명주기
  • application
    • 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프

 

request 스코프

동시에 여러 HTTP 요청 시 어떤 요청이 남긴 로그인지 구별이 어려움 - requeset 스코프로 구별

UUID를 넣어서 구별해보는 예제

 

@Component
@Scope(value = "request", proxyMode = ScopeProxyMode.TARGET_CLASS)
public class MyLogger {
	
    private String uuid;
    private String requestURL;
    
    public void setRequesetURL(String requestURL) {
    	this.requestURL = requesetURL;
    }
    
    public void log(String message) {
    	System.out.println("[" + uuid + "]" + "[" + requestURL + "]" + message);
    }
    
    @PostConstruct
    public void init() {
    	uuid = UUID.randomUUID().toString();
        System.out.println("[" + uuid + "] request scope bean create:" + this);
    }
    
    @PreDestroy
    public void close() {
    	System.out.println("[" + uuid + "] request scope bean close:" + this);
    }
}

init으로 uuid를 만들어서 필드에 셋팅해준다

시작할때 출력해주고, 종료시에도 마찬가지로 해당 uuid를 출력해준다

log를 찍으면 uuid와 URL, message를 한번에 출력해주는 간단한 로그 로직이다

 

@Scope에 proxyMode는 프록시를 사용할때만 지정해준다.

Provider를 사용할때는 value만 지정해준다. 이때 value만 지정한다면, "request"만 입력할 수 있다

 

@Service
@RequiredArgsConstructor
public class LogDemoService {
	
    // 프록시   private final MyLogger myLogger;
    // provider private final ObjectProvider<MyLogger> myLoggerProvider;
    
    public void logic(String id) {
        // Provider MyLogger myLogger = myLoggerProvider.getObject();
    	myLogger.log("service id = " + id);
    }
}

서비스를 임의로 만들었는데, logic이라는 메서드를 호출하면, 해당 메서드가 Service임을 표시하는 log 메소드를 실행한다

 

@Controller
@RequiredArgsConstructor
public class LogDemoController {

    private final LogDemoService logDemoService;
    // 프록시   private final MyLogger myLogger;
    // Provider private final ObjectProvider<MyLogger> myLoggerProvider;
    
    @RequestMapping("log-demo")
    @ResponseBody
    public String logDemo(HttpServletRequest request) {  
    	String requestURL = request.getRequestURL().toString();
        // Provider MyLogger myLogger = myLoggerProvider.getObject();
        myLogger.setRequestURL(requestURL);
        
        moLogger.log("controller test");
        logDemoService.logic("testId");
        return "OK";
    }
}

해당 컨트롤러는 request로 들어온 URL을 셋팅해주고,

myLogger의 log와 logDemoService.logic을 실행해주는 간단한 기능을 수행한다.

 

String requestURL = request.getRequestURL.toString();

이런 코드는 공통 처리가 가능한 인터셉터, 서블릿 필터 같은 곳에서 적용해주는 것이 좋다.

 

request Scope를 사용하지 않고, 파라미터로 이 모든 정보를 서비스 계층에 넘기면 지저분해진다

구분하기가 어려워지고, 이는 운영하는데 큰 문제를 야기할 수 있다

 

간단한 프로그램이지만,

핵심은 request 스코프를 사용하여, 웹의 생명주기에 내가 신경쓰지 않아도 시작과 끝에 메세지를 남길 수 있고

동시에 여러요청이 올 것이라고 예상되는 지점에, 구분할 수 있는 포인트가 될 수 있다.

스프링을 활용하면, 이와같은 스코프를 쉽게 구현할 수 있다