이번 시간에는 웹 스코프에 대해서 알아보았다.
지난 시간까지 싱글톤과 프로토타입 스코프를 배웠다.
싱글톤은 스프링 컨테이너의 시작과 끝까지 함께하는 긴 스코프이고,
프로토타입은 생성과 의존관계 주입, 그리고 초기화까지만 진행하는 특별한 스코프이다.
이제는 웹 스코프에 대해서 알아보자.
웹 스코프는 말 그대로 웹 환경에서만 동작하기 때문에 웹 스코프이고,
웹 스코프는 프로토타입과 다르게 스프링이 종료시점까지 관리를 한다. 그렇기 때문에 종료 메소드가 호출된다.
웹 스코프의 종류로는,
request : 서버에 누군가 접속을 하면 http 요청이 들어올 것이고, 그 http 요청 하나가 들어오고 나갈때까지 유지되는 스코프이다. 각각의 http 요청마다 따로 호출이 되기 때문에, 별도의 빈 인스턴스가 생성되고 관리된다.
예를 들어, 클라이언트 A와 B가 있을 때, A와 B가 스프링 컨테이너에 동시에 http에 요청을 한다고 하자.
그러면 컨트롤러에서 request scope와 관련된 것들을 호출 할 때, 클라이언트 A와 B는 각각 다른 인스턴스를 할당 받는다.
또한, 컨트롤러에서 서비스를 호출해서 request scope에 빈을 리턴할 때는, 각 클라이언트에 맞는 인스턴스를 전달해준다.
즉, 요청에 따라 각각 다른 스프링 빈이 생성되어서 관리된다.
session : session은 HTTP session과 동일한 생명주기를 갖고 있는 스코프.
application : 서블릿 컨텍스트(ServletContext)와 동일한 생명주기를 가지는 스코프.
websocket : 웹 소켓과 동일한 생명주기를 가지는 스코프.
이번 강의에서는 주로 request 스코프를 예제로 다루고, 나머지도 범위만 다르지 동작 방식은 비슷하다.
웹 스코프는 웹 환경에서만 동작하기 때문에 라이브러리 추가가 필요하다.
implementation 'org.springframework.boot:spring-boot-starter-web'
위의 코드로 gradle 라이브러리를 추가해준다.
참고로, 스프링 부트는 웹 라이브러리가 없으면 지난 강의들에서 썼던 'AnnotationConfigApplicationContext'를 기반으로 애플리케이션을 구동한다. 웹 라이브러리가 추가되면 웹과 관련된 추가 설정과 환경이 필요하므로, 'AnnotationConfigServletWebServerApplicationContext'를 기반으로 애플리케이션을 구동한다.
request 스코프 예제 개발에 들어가기 앞서,
동시에 여러 HTTP 요청이 온다면, 로그를 남길 때, 섞이기 때문에 구분하기가 어렵다.
이럴때 사용하기 좋은것이 request 스코프이다.
로그를 남길때 포맷이 [UUID][requestURL] {message} 이런 형식으로 로그를 남기게 된다.
그렇게 되면 UUID(Universally Unique Identifier)는 유니크한 아이디이기 때문에 (즉, UUID가 같으면 같은 고객이구나),
UUID로 http 요청이 들어오고 나갈때까지의 로그를 구분해서 확인할 수 있는 것이다.
또한 URL 정보도 로그로 남겨서 어떤 URL을 요청했는지 확인할 수도 있다.
코드를 작성하기 위해, 패키지와 그 안에 클래스를 만들어준다.
로그를 만들어주는 코드를 작성해준다.
먼저 Scope는 request로 지정해주면, 이 빈은 스프링 컨테이너에 요청하는 시점에 http 요청당 하나씩 생성이 되고,
http 요청이 끝나는 시점에서 소멸이 된다.
UUID를 받아서 생성해주는 init 초기화 메소드를 만들어주고,
요청을 관리하고 고객 요청이 서버에서 빠져나갈때 close 메소드가 호출되고 빈 객체가 소멸될 것이다.
그리고 requestURL은 빈이 생성되는 시점에는 알 수 없기 때문에, 외부에서 setter를 통해서 입력을 받을 것이다.
그리고 컨트롤러를 만들기 위해 패키지와 클래스를 생성한다.
생성자를 통해서 의존관계 주입을 받아주는 코드를 15,16번째 라인에 작성한다.
그리고 19번째 라인에는, view 화면 없이 문자를 그냥 바로 반환해주는 ResponseBody를 통해서 받아준다.
파라미터로는 HttpServletRequest를 작성해주는데,
이것은 자바에서 제공하는 표준 Servlet 규약에 의한 http request 정보를 받을 수 있다.
request.getRequestURL().toString()을 통해서 고객의 요청 url을 알 수 있고, 그 요청을 myLogger에 찍어준다.
실무에서는 requestURL을 MyLogger에 저장하는 부분은, 컨트롤러보다는 공통 처리가 가능한 스프링 인터셉터나 서블릿 필터같은 곳을 활용하는 것이 좋다. 예제에서는 테스트이기 때문에 참고용으로!
그리고 서비스단에서는, logic 메소드를 통해서 파라미터로 받은 id를 로그에 찍어준다.
여기서 중요한 점은, request scope를 사용하지 않고, 파라미터로 이 모든 정보를 서비스 계층에 넘긴다면,
파라미터가 많아서 지저분해진다. 더 문제는 requestURL 같은 웹과 관련된 정보가 웹과 관련없는 서비스 계층까지 넘어가게 된다.
웹과 관련된 부분은 컨트롤러까지만 사용해야 한다. 서비스 계층은 웹 기술에 종속되지 않고, 가급적 순수하게 유지하는 것이 유지보수 관점에서 좋기 때문이다.
request scope의 MyLogger 덕분에 이런 부분을 파라미터로 넘기지 않고, MyLogger의 멤버변수에 저장해서 코드와 계층을 깔끔하게 유지할 수 있다.
이렇게 세팅을 하고 서버를 올리면..? 오류가 발생한다.
왜냐하면, 스프링 애플리케이션을 실행하는 시점에 싱글톤 빈은 생성해서 주입이 가능하지만,
request 스코프 빈은 실제 고객의 요청이 와야 생성되기 때문에!
서버 올릴 때는 아직 request 요청이 안왔기 때문에.. 오류를 리턴한다.
그래서 다음 시간에 이어서 Provider를 써서 문제를 해결할 것이다!
댓글