HTML은 정적이다. 동적인 리소스를 만들기 위해선 다른 기능이 필요하다. 이때 사용되는 것이 서블릿이다.
서블릿을 한 줄로 정의하자면 아래와 같습니다.
클라이언트의 요청을 처리하고, 그 결과를 반환하는
Servlet 클래스의 구현 규칙을 지킨 자바 웹 프로그래밍 기술
간단히 말해서, 서블릿이란 자바를 사용하여 웹을 만들기 위해 필요한 기술입니다. 그런데 좀더 들어가서 설명하면
클라이언트가 어떠한 요청을 하면 그에 대한 결과를 다시 전송해주어야 하는데, 이러한 역할을 하는 자바 프로그램입니다.
서블릿:
스프링 부트가 내장 웹서버 톰캣을 띄운다.
톰켓은 내부에 서블릿 컨테이너를 가지고 있다. 이를 통해 서블릿 생성(HelloServlet 생성)
- JAVA를 사용하여 웹을 만들기 위해 필요한 프로그래밍 기술입니다. (Servlet 클래스의 구현 규칙을 지켜야합니다.)
- 또한, 이러한 웹 기반의 요청에 대해 동적으로 처리해주는 역할로서 Server Side에서 작동(WAS)합니다.
- 웹 페이지 개발 시, 웹 화면(HTML)은 JSP로 표현하고, 복잡한 프로그래밍은 서블릿으로 구현하여 조화롭게 사용합니다.
- 클라이언트가 요청을 하면 그 결과를 다시 전송해주는 프로그램입니다.
- HttpServlet이라는 Java의 클래스를 상속받습니다.
서블릿의 기능: 임시 저장소 기능
해당 HTTP 요청이 시작부터 끝날 때 까지 유지되는 임시 저장소 기능 저장: request.setAttribute(name, value)
조회: request.getAttribute(name)
세션 관리 기능
request.getSession(create: true)
중요
HttpServletRequest, HttpServletResponse를 사용할 때 가장 중요한 점은 이 객체들이 HTTP 요청 메시지, HTTP 응답 메시지를 편리하게 사용하도록 도와주는 객체라는 점이다. 따라서 이 기능에 대해서 깊이있는 이해를 하려면 HTTP 스펙이 제공하는 요청, 응답 메시지 자체를 이해해야 한다.
템플릿 엔진(JSP)
서블릿 덕분에 동적으로 원하는 HTML을 마음껏 만들 수 있다. 정적인 HTML 문서라면 화면이 계속 달라지는 회원의 저장 결과라던가, 회원 목록 같은 동적인 HTML을 만드는 일은 불가능 할 것이다. 하지만, 자바 코드만으로 HTML을 만드는 것은 매우 불편하다. 따라서, HTML문서에 동적인 부분만 자바 코드로 넣으면 더 편리할 것이다. 그래서 나온것이 템플릿 엔진(JSP< Thymeleaf 등) 이다.
서블릿, JSP의 한계
서블릿: 뷰화면을 위한 HTML을 만드는 작업에 자바 코드가 섞인다.
JSP: 뷰 생성하는 HTML 작업을 깔끔하게 가져가고, 중간중간 동적 변경이 필요한 부분만 자바코드로 처리
하지만, JSP 역시 회원 저장과 같은 경우 반은 비즈니스 로직, 반은 HTML로 보여지기 위한 뷰 영역이다.
JSP에 너무 많은 코드가 노출된다.( JSP 몇전줄씩.;... 유지보수 최악!)
MVC 패턴
비즈니스 로직은 서블릿 처럼 다른 곳에서 처리하고, JSP는 목적에 맞게 HTML로 화면(View)를 만드는 영역에 집중하도록 한다.
MVC 패턴 - 개요 뷰 영역과 비즈니스 로직 영역의 분리로 인해, 비즈니스 로직을 호출하는 부분이 변경되어도 뷰 부분의 코드를 손댈 필요가 없어진다!
HTML 코드 하나 수정해야 하는데, 수백줄의 자바 코드가 함께 있다고 상상해보라! 또는 비즈니스 로직을 하나 수정해야 하는데 수백 수천줄의 HTML 코드가 함께 있다고 상상해보라.
MVC 패턴의 도입으로 가능해진것들
1. 변경의 라이프 사이클 ( 뷰, 비즈니스 로직의 라이프 사이클이 다르기에, 나눠놓는 것이 좋은데, 이것이 가능해짐!)
사실 이게 정말 중요한데, 진짜 문제는 둘(뷰영역, 비즈니스 로직 영역) 사이에 변경의 라이프 사이클이 다르다는 점이다. 예를 들어서 UI 를 일부 수정하는 일과 비즈니스 로직을 수정하는 일은 각각 다르게 발생할 가능성이 매우 높고 대부분 서로에게 영향을 주지 않는다. 이렇게 변경의 라이프 사이클이 다른 부분을 하나의 코드로 관리하는 것은 유지보수하기 좋지 않다. (물론 UI가 많이 변하면 함께 변경될 가능성도 있다.)
2. 기능 특화
특히 JSP 같은 뷰 템플릿은 화면을 렌더링 하는데 최적화 되어 있기 때문에 이 부분의 업무만 담당하는 것이 가장 효과적이다.
MVC 패턴이 시사하는 바: 뷰 영역과 비즈니스 로직 영역의 분리
요청이 들어오면 컨트롤러가 매핑 받고, 내부적으로(보통 서비스 단 호출) 비즈니스 로직을 처리하고 얻은 데이터를 Model 영역에 담는다. View는 Model의 데이터를 기반으로 뷰를 생성한다. 그렇기에 뷰 영역에서는 데이터 접근, 비즈니스 로직에 대해 몰라도 된다!
MVC 패턴은 지금까지 학습한 것 처럼 하나의 서블릿이나, JSP로 처리하던 것을 컨트롤러(Controller)와 뷰(View)라는 영역으로 서로 역할을 나눈 것을 말한다. 웹 애플리케이션은 보통 이 MVC 패턴을 사용한다.
컨트롤러: HTTP 요청을 받아서 파라미터를 검증하고, 비즈니스 로직을 실행한다. 그리고 뷰에 전달할 결과 데이터를 조회해서 모델에 담는다.
모델: 뷰에 출력할 데이터를 담아둔다. 뷰가 필요한 데이터를 모두 모델에 담아서 전달해주는 덕분에 뷰는 비즈니스 로직이나 데이터 접근을 몰라도 되고, 화면을 렌더링 하는 일에 집중할 수 있다.
뷰: 모델에 담겨있는 데이터를 사용해서 화면을 그리는 일에 집중한다. 여기서는 HTML을 생성하는 부분을 말한다.
컨트롤러에 비즈니스 로직을 둘 수도 있지만, 이렇게 되면 컨트롤러가 너무 많은 역할을 담당한다. 그래서 일반적으로 비즈니스 로직은 서비스(Service)라는 계층을 별도로 만들어서 처리한다. 그리고 컨트롤러는 비즈니스 로직이 있는 서비스를 호출하는 역할을 담당한다. 참고로 비즈니스 로직을 변경하면 비즈니스 로직을 호출하는 컨트롤러의 코드도 변경될 수 있다. 앞에서는 이해를 돕기 위해 비즈니스 로직을 호출한다는 표현 보다는, 비즈니스 로직이라 설명했다.
기본 MVC 패턴의 한계
기능이 복잡해질 수 록 컨트롤러에서 공통으로 처리해야 하는 부분이 점점 더 많이 증가할 것이다. 단순히 공통 기능을 메서드로 뽑으면 될 것 같지만, 결과적으로 해당 메서드를 항상 호출해야 하고, 실수로 호출하지 않으면 문제가 될 것이다. 그리고 호출하는 것 자체도 중복이다.
정리하면 공통 처리가 어렵다는 문제가 있다.
프론트 컨트롤러(Front Controller)
이 문제를 해결하려면 컨트롤러 호출 전에 먼저 공통 기능을 처리해야 한다. 소위 수문장 역할을 하는 기능이 필요하다 . 프론트 컨트롤러(Front Controller) 패턴을 도입하면 이런 문제를 깔끔하게 해결할 수 있다. (입구를 하나로!) 스프링 MVC의 핵심도 바로 이 프론트 컨트롤러에 있다.
일반 컨트롤러
프론트 컨트롤러 도
스프링은 프론트 컨트롤러 패턴을 따르고 이를 DispatcherServlet이 담당한다.
Dispatcher Servlet
- 클라이언트가 요청을 주면, Servlet Container가 요청을 받는데요. 이때 제일 앞에서 서버로 들어오는 모든 요청을 처리하는Front Controller라는 것을 Spring에서 정의하였고, 이를 Dispatcher Servlet이라고 합니다.
- 기존에는 모든 Servlet에 대해 URL 매핑을 활용하기 위해서 web.xml에 모두 등록해주어야 했지만, dispatcher servlet이 해당 어플리케이션으로 들어오는 모든 요청을 핸들링해주면서 작업의 효율을 높였습니다.
(Spring Boot에서는 @SpringBootApplication 이라는 어노테이션으로 web.xml 파일을 대체할 수 있습니다.)
- 즉, Controller로 향하는 모든 웹 요청의 진입점으로써 웹 요청을 처리하고 결과 데이터를 Client에게 응답합니다.
- Dispatcher Servlet을 이용한다는 것은 스프링에서 제공하는 MVC 모델을 이용하겠다는 것으로 생각하면 됩니다
Dispathcer Servlet 동작 과정
ex)
1. user/register에 요청이 오면, Spring의 Dispatcher Servlet으로 전달됩니다.
2. Dispatcher Servlet이 UserContoller에 있는 registerUser()과 같은 적절한 Method를 찾습니다.
3. 해당 Method는 @PostMapping('/user/register'), @RequestMapping(value="/user/register", Request.Method=Post) 와 같은 어노테이션(Handler Mapping)을 보고 찾습니다.
4. 함수를 수행하고나서 적절한 view를 Response 합니다.
Web 동작 과정
ApplicationContext
ApplicationContext를 스프링 컨테이너라고 한다. ApplicationContext는 BeanFactory 인터페이스의 하위 인터페이스이다. 즉, ApplicationContext는 BeanFactory에 부가기능을 추가한 것이다.
BeanFactory는 스프링 컨테이너의 최상위 인터페이스이다. 스프링 빈을 관리하고 조회하는 역할을 한다. ApplicationContext는 BeanFactory + 부가 기능(국제화 기능, 환경 변수 관련 처리, 애플리케이션 이벤트, 리소스 조회)을 가진다.
정확히는 스프링 컨테이너를 부를 때, BeanFactory, ApplicationContext를 구분해서 말하지만, BeanFactory를 직접적으로 사용하는 경우는 거의 없다. 왜냐하면 ApplicationContext가 BeanFactory의 모든 기능을 가지고 있기 때문이다.
ApplicationContext의 구현체가 여러가지 있는데, 구현체에 따라 스프링 컨테이너를 XML을 기반으로 만들 수도 있고, 자바 클래스로 만들 수도 있다. 이게 가능한 이유는 빈 등록을 BeanDefinition으로 추상화해서 생성 하기 때문이다. XML로 하든, 자바로 하든 BeanDefinition이 생성된다.
스프링 컨테이너 내부에는 빈 저장소가 존재한다. 빈 저장소는 key로 빈 이름을 가지고 있으며, value로 실제 빈 객체를 가지고 있다.
스프링 컨테이너는 기본적으로 빈을 싱글톤으로 관리해준다. 따라서 싱글톤 컨테이너라고 불리기도 한다. 스프링 컨테이너가 빈을 싱글톤으로 관리해주면서 기존 싱글턴 패턴의 문제점(싱글톤 패턴 구현을 위한 코드가 추가되어야함, 구체 클래스에 의존, 유연성이 떨어짐 etc)은 없어지고, 싱글톤의 장점(매번 인스턴스를 생성할 필요없이 단 하나만 생성해서 비용을 줄일 수 있다.)만 가져갈 수 있다.
Server start 단계
- Web server init
- Root WebApplicationContext 로딩
- Web server start
Client 호출 단계
- Client -> Web server 으로 request 보냄
- 동적 Web server -> Servlet container로 전달
- Servlet container 쓰레드 생성
- DispatcherServlet init (서블릿 생성 안되어 있을경우)
- 생성된 쓰레드에서 DispatcherServlet service() 메서드 호출
- HandlerMapping을 통해 매핑 컨트롤러 조회
- HandlerAdapter를 통해 매핑 컨트롤러에 request 전달
- 개발자가 구현한 Controller -> Service -> Repository … 동작
'Spring boot' 카테고리의 다른 글
스프링 핵심원리 13(완) [스프링 AOP 실전 주의 사항] (0) | 2023.06.19 |
---|---|
스프링 핵심원리 12 [커스텀 AOP 어노테이션 만들기] (1) | 2023.06.19 |
스프링 핵심원리 11 [포인트 컷] (0) | 2023.06.17 |
스프링 핵심원리 10 [스프링 AOP 사용법] (0) | 2023.06.17 |
스프링 핵심원리 9 [스프링 AOP] (0) | 2023.06.17 |