스프링 컨테이너 사용
지난 섹션 3에서 구현했던 그림, 서블릿 컨테이너를 코드에서 띄우고, 서블릿을 모든 요청을 다 받아서 뒤에 있는 오브젝트에 작업을 위임하고 필요하면 파라미터 같은 것들을 바인딩해서 전달하는 Front Controller를 만들었다.
뒤에 Hello Controller라는 오브젝트를 하나 만든 다음에 Hello Controller를 사용하는 방식으로 구성했다.
이 강의에서는 Hello Controller라는 이 오브젝트를 이제는 스프링 컨테이너 안에다가 집어넣고 스프링 컨테이너에서 꺼내서 사용하는 방식으로 구현한다.
스프링 컨테이너는 크게 2가지가 필요하다.
첫 번째는 비즈니스 로직을 담고 있는 비즈니스 오브젝트(POJO), 평범한 자바 오브젝트. 특정 클래스를 상속하거나 하는 방식으로 만들지 않는 자바 오브젝트를 말한다.
두 번째는 이렇게 만들어진 코드들을 어떤 식으로 구성할지에 대한 구성 정보를 담고 있는 Configuration Metadata 라는 거, 애플리케이션을 어떻게 구성할 것인가 그 데이터를 담고 있는 정보.
이 두가지를 스프링 컨테이너가 조합을 한다.
그래서 궁극적으로 바로 사용 가능한 완전히 구성된 어떤 시스템을 완성한다.
스프링 컨테이너가 내부에서 비즈니스 오브젝트 코드와 컨피규레이션 메타데이터를 조합해서 Bean이라고 불리는 오브젝트들을 구성해서 서버 애플리케이션으로 만들어 준다.
기존 섹션 3에서는 프론트 컨트롤러에서 객체를 생성해서 사용했었는데, 위 코드에서는 스프링 컨테이너에 객체를 등록하고, 스프링 컨테이너에서 스프링 빈을 꺼내서 사용하는 방식으로 구현되어 있다.
ApplicationContext 인터페이스 구현체 종류가 스프링 컨테이너이다.
ApplicationContext 종류중에서 코드에 의해서 손쉽게 만들 수 있도록 만들어진게 하나 있는데 예제에선 이 Generic Application을 사용하고 있다.
스프링 컨테이너가 빈이 어떻게 구성되어지는가 어떤 클래스로 빈을 만들 것인가 이런 정보를 다 갖고 있게 되면 진짜 Bean 오브젝트를 만들게 된다. 우측 메서드로 오브젝트 생성/초기화 함 (applicationContext.refresh();)
의존 오브젝트 추가
스프링 컨테이너는 이런 싱글톤 패턴을 사용한 것과 유사하게 어떤 타입의 오브젝트를 딱 한번만 만들어두고 이것을 계속 재사용할 수 있게 해주는 기능을 제공한다.
그래서 스프링 컨테이너를 싱글톤 레지스트리라고도 한다.
Dependency Injection
Dependency Injection에는 제 3의 존재가 필요하다. 이걸 Assembler라고 부른다.
헬로 컨트롤러는 헬로 인터페이스를 구현한 어떠한 클래스에 의존을 하는데 소스코드 레벨에서는 의존하고 싶지 않을 때,
런타임에서 사용하는 오브젝트를 바꾸고 싶을 때, 누군가가 이게 가능하도록 만들어 줘야 되는데, 이게 가능해지는 이유는 헬로 컨트롤러가 사용하는 오브젝트를 직접 new 키워드를 사용해서 만드는 대신에 외부에서 그 오브젝트를 만들어서 헬로 컨트롤러가 사용할 수 있도록 주입을 해주는 그 작업을 해주는 것을 Assembler라고 부른다.
이 Assembler를 스프링 컨테이너라고 부른다.
의존 오브젝트 DI 사용
기존 HelloController 클래스에서 HelloService 클래스를 직접 만들어서 사용하고 있었는데, 스프링 컨테이너를 통하여 주입받아서 사용하게 바꾸어야 한다.
그럼 위 이미지에서 기존 AS-IS 부분을 삭제하고, 스프링이 넣어줄 수 있도록 TO-BE 처럼 변수와 생성자를 만든다.
그리고 스프링 컨테이너에 아래처럼 SimpleHelloService 구현체를 빈으로 등록한다.
그러면 스프링 컨테이너가 HelloController Bean을 생성할 때 HelloService 인터페이스의 구현체가 필요한 것을 체크하고, 등록된 Bean에서 HelloService의 구현체를 찾아서 알아서 HelloController 클래스를 만들 때 넣어서 빈을 만든다.
DispatcherServlet으로 전환
서블릿 매핑, 바인딩 작업을 지원해주는 dispatcherServlet 생성,
그리고 dispatcherServlet이 매핑, 바인딩 하는데 참고할 WebApplicationContext를 전달한다.
그런데 현재 상태만으로는 404에러가 발생한다.
왜냐하면 이 이후에 스프링 컨테이너가 가진 object에 요청을 넘겨줘야 하는데, 이걸 못찾았기 때문이다. (아래 바인딩, 매핑 부분 체크한 이미지 참조)
(현재 상태에서는 디스패처 서블릿한테 어떤 오브젝트가 웹 요청 정보를 가지고 들어오면 이걸 전달해줘라 뭐 이런 거에 대한 아무런 힌트를 주지도 않았기 때문, 이 정보를 어떤 식으로든 디스패처 서블릿에게 전달을 해줘야 되는데 굉장히 다양한 방법이 있다.
스프링 초기에는 xml에다가 그것을 다 명시해 줬음. 어떤 url로 들어오면은 어떤 bean이 이것을 처리해달라.
이런 것을 xml에 쭉 나열하기도 했고, 그 이후에 여러가지 관례를 이용해서 연결하는 등의 많은 방법들이 나왔는데 가장 각광을 받았고 개발자들이 좋아했던 방법은 맵핑 정보를 survlet의 코드로 직접 넣는 대신에 그 요청을 처리할 컨트롤러 클래스 안에다가 맵핑 정보를 대신 집어 넣는 방법이었다.)
애노테이션 매핑 정보 사용
@GetMapping("/hello")에는 두 가지 정보가 들어가있다.
1. get Method로 들어오는 것 중에서
2. url이 /hello 인 것을 이 컨트롤러의 메서드가 처리하도록 만들겠다.
디스패처 서블릿은 위에서 서블릿 컨테이너인 애플리케이션 컨텍스트를 생성자로 받았다. ( new DispatcherServlet(applicationContext) )
그러면 디스패처 서블릿은 Bean을 다 뒤진 다음 웹 요청을 처리할 수 있는 맵핑 정보를 가지고 있는 클래스를 찾는다.
(ex : get 매핑이나, request 매핑이 붙어 있으면 이거는 웹 요청을 처리할 수 있도록 만들어진 웹 컨트롤러구나 판단을 하고 요청 정보를 추출하고, 매핑에 사용할 맵핑 테이블을 하나 만든다. 그리고 이후에 웹 요청이 들어오면 테이블을 참조해서 이걸 담당할 Bean 오브젝트와 메소드를 확인한다.)
dispatcherServlet이 매핑 정보를 만들 때
1. 클래스 레벨에 있는 정보를 먼저 참고한다.
2. 메소드 레벨에 붙어 있는 정보를 거기다가 추가한다.
스프링 컨테이너로 통합
목표 : 이전 시간에 앞 부분에서는 Spring Container를 생성하고 Bean을 등록해서 초기화하는 작업을 하는 SpringContainer 작업 파트가 있었고, 그렇게 만들어진 SpringContainer를 활용하면서 SurvletContainer를 코드에서 생성하고 필요한 FrontController 역할을 하는 Dispatcher Survlet을 등록하는 SurvletContainer 초기화 코드로 구성되어 있었다.
이번 시간에는 서블릿 컨테이너를 만들고 서블릿을 초기화하는 작업을 스프링 컨테이너가 초기화 되는 과정 중에 일어나도록 코드를 바꿔보는게 목표이다.
스프링 컨테이너의 초기화 작업은 Refresh라는 메소드에서 다 일어난다.
템플릿 메소드 패턴은 상속을 통해서 기능을 확장하도록 만든 패턴이니, GenericWebApplicationContext 라는 클래스를 상속해서 새로운 클래스를 하나 만들어야 한다. 그래야 오버라이딩 해서 hook 메소드의 기능을 집어 넣을 수 있기 때문이다.
익명 클래스를 사용하여 GenericWebApplicationContext 를 상속받은 클래스를 만든 다음, onRefresh() 안에 서브릿 컨테이너 초기화하는 코드를 집어넣음.
자바코드 구성 정보 사용
목표 : 스프링 컨테이너가 사용하는 구성 정보, 우리가 만든 코드를 어떻게 오브젝트로 만들어서 컨테이너 내에 컴포넌트로 등록해 두고 스프링 컨테이너 안에 들어있는 Bean 오브젝트가 또 다른 오브젝트를 사용한다면(의존하고 있다면) 이 관계를 어떻게 맺어줄 것인가, 어느 시점에 그 오브젝트를 주입해 줄 것인가 이런 정보들을 스프링 컨테이너에다가 구성 정보로 제공을 해줘야 한다.
이 구성 정보를 제공하는 방법이 여러가지가 있을 수 있는데, 예전에는 외부 설정 파일을 이용했고, 현재 실습에서는 Factory Method를 이용해본다.
SpringContainer가 빈 오브젝트를 가진 Factory Method를 가진 클래스다 라는 것을 먼저 인지하도록 클래스 레벨에도 @Configuration 어노테이션을 붙여줘야 된다.
그러면 이 클래스를 Spring Container가 보고 이 안에 @Bean 어노테이션이 붙은 Factory Method가 있겠구나, 그걸 이용해서 Bean Object를 만들면 되겠네 라고 인식을 할 수 있다.
아래 이미지를 보면 스프링 빈으로 등록할 인스턴스를 생성하는, @Bean 어노테이션이 붙은 Factory method가 정의되어 있고, 스프링 컨테이너에 Factory Method가 있다는 것을 알려주기 위해 클래스 레벨에 @Configuration을 붙인 것을 확인할 수 있다.
그리고 어노테이션 기반 자바코드 구성 정보를 사용하기 위해서는 AnnotationConfigWebApplicationContext를 new 해서 사용해야 하는 것을 확인할 수 있다.
@Component 스캔
Bean으로 사용할 클래스의 레이블에 @Component 를 붙이고, @Configuration 이 있는 클래스에(이건 정확히 맞는지 모름) @ComponentScan을 붙이면,이 클래스가 있는 패키지부터 시작해 그 하위 패키지를 뒤져서 @Component라는 애노테이션이 붙은 모든 클래스를 빈으로 등록을 한다.
Bean으로 등록할 때 필요하다면 의존 오브젝트를 찾아내고 이걸 생성자로 호출할 때 파라미터로 넘겨주기도 한다.
SpringBootApplication
이전 강의에서 만들었던 스프링 컨테이너 생성 부분, 디스패처 서블릿 만드는 부분, 서블릿 컨테이너 생성 후 디스패처 서블릿 추가하고 실행하는 로직 부분을 아래 이미지처럼 별도 클래스로 분리하고, main 메서드에서
MySpringApplication.run(HellobootApplication.class, args); 으로 실행하면
기존에 관습적으로 사용하던 SpringApplication.run(HellobootApplication.class, args)와 동일한 로직을 수행한다.
'스프링 관련 > 토비의 스프링 부트 - 이해와 원리' 카테고리의 다른 글
섹션 9. Spring JDBC 자동 구성 개발 (0) | 2024.06.09 |
---|---|
섹션 8. 외부 설정을 이용한 자동 구성 (0) | 2024.06.06 |
섹션 7 조건부 자동 구성 (0) | 2024.06.03 |
섹션 6. 자동 구성 기반 애플리케이션 (0) | 2024.05.30 |
섹션3 독립 실행형 서블릿 애플리케이션 (0) | 2024.05.19 |
댓글