타임리프의 스프링 동작원리
0.타임리프의 목적
타임리프는 자바에서 서버사이드 렌더링을 지원해주는 자바 템플릿 엔진이다.
타임리프의 주요 목적은 유지 관리가 수월한 템플릿을 작성하도록 지원하는 것이다.
이는 타임리프의 핵심 기능인 natural template을 통해 제공해주는데 네츄럴 템플릿은 서버 사이드 렌더링을 하는데 필요한 데이터가 없더라도 프로토 타입으로서의 역할을 해줄 수 있다.
이 특징이 기존 자바 템플릿엔진중의 하나인 JSP와 가장 다른점이다. JSP는 화면을 보기 위해선 서버의 도움이 필요하지만, 타임리프는 없어도 가능하다.
그렇기 때문에 타임리프를 사용한다면, 디자인팀과 개발팀 사이에 생기는 커뮤니케이션 비용을 줄여준다.
타임리프는 6가지 템플릿을 지원한다.
Html, xml, text,javascript,css, raw
1.DispatchServlet
//
디스패처 서블릿? -
HTTP 프로토콜로 들어오는 모든 요청을 받아 적합한 컨트롤러에 위임해주는 프론트 컨트롤러
클라이언트의 request를 Tomcat과 같은 서블릿 컨테이너가 요청을 받는다.
이 모든 요청을 프론트 컨트롤러인 디스패처 서블릿이 가장 먼저 받는다.
그러면, 디스패처 서블릿은 공통적인 작업을 먼저 처리한 후에 해당 요청을 처리해야 하는 컨트롤러를 찾아 작업을 위임한다.
Front Controller는 주로 서블릿 컨테이너의 제일 앞에서 서버로 들어오는 클라이언트의 모든 요청을 받아서 처리해주는 컨트롤러이다.
MVC 구조에서 함께 사용되는 디자인 패턴이다.
디스패처 서블릿의 장점? -
Spring MVC는 디스패처서블릿의 등장에 의해 web.xml의 역할을 상당히 축소시켰다.
과거에 모든 서블릿을 URL 매핑을 위해 web.xml에 모두 등록해주야 했지만,
디스패처서블릿이 해당 어플리케이션에 들어오는 모든 요청을 핸들링해주고 공통작업을 처리면서 편해졌다.
컨트롤러를 구현하면 디스패처 서블릿이 알아서 적합한 컨트롤러로 위임해주는 구조가 되었다.
정적자원의 처리? -
디스패처 서블릿이 요청을 controller로 넘겨주는 방식은 효율적으로 보인다.
그러나 디스패처 서블릿이 모든 요청을 처리하다보니 이미지, html/css/js 등과 같은 정적 파일에 대한 요청 또한 모두 가로채는 바람에
정적자원을 불러오지 못하는 상황도 발생한다.
이러한 문제를 해결하기 위해, 2가지가 고안된다.
/apps의 url로 접근시, 디스패처 서블릿이 담당
/resources의 url로 접근시, 디스패처 서블릿이 컨트롤할 수 없으므로 담당하지 않는다.
방식의 문제는 없지만, 코드가 지저분해지며, 요청에 대해 url을 붙여주어야 하므로, 직관적인 설계가 될 수 없다.
2차원적으로 설정된 자원 경로를 탐색하여 자원을 탐색하는 것
디스패처 서블릿은 적합한 컨트롤러와 메소드를 찾아서 요청을 위임해야한다.
1 클라이언트의 요청을 디스패처 서블릿이 받음
2 요청 정보를 통해 위임할 컨트롤러 찾음
3 요청을 컨트롤러로 위임할 핸들러 어댑터를 찾아서 전달함
4 핸들러 어댑터가 컨트롤러로 요청을 위임함
5 비즈니스 로직처리
6 컨트롤러가 반환값을 반환함
7 핸들러어댑터가 반환값을 처리함
8 서버의 응답을 클라이언트로 반환함
/////타임리프와 JSP와의 차이점
타임리프는 HTML, XML, JavaScript, CSS 및 일반 텍스트를 처리 할 수 있는 웹 및 독립형 환경에서 사용할 수 있는 Java 탬플릿 엔진이다.
타임리프는 html파일을 가져와서 파싱해서 분석후 정해진 위치에 데이터를 치환해서 웹 페이지를 생성한다.
JSP는 서블릿으로 변환되어 실행이 되어진다. JSP 내부에서 자바 코드를 사용할 수 있다.
타임리프는 자바코드를 사용할 수없고, jsp처럼 커스텀 태그와 같은 기능도 없다.
장단점 :
JSP 장점: 서블릿이다보니, 뭐든지 할 수 있다. 그러나 MVC구조가 사용되면서, JSP에 비즈니스 로직을 넣으면 유지보수가 힘들어져 요즘은 자바 코드를 사용하지 못하게 한다.
페이지의 프론트 타입을 제공할 수 있다는 것이 장점입니다.
타임리프로 작성된 페이지를 웹 브라우저로 열어보면 실제 보여질 내용과 동일하게 보여집니다.
////
타임리프가 스프링에서의 동작원리
DispatcherServlet은 HandlerMapping 전략에 따라 요청을 핸들링 해줄 핸들러를 선택해준다.
기본적으로 Sping MVC에서 제공해주는 핸들러 매핑 구현체로는 BeanNameUrlHandlerMapping과 RequestMappingHandler가 있다.
BeanNameUrlHandlerMapping은 요청 URL에서 / 다음에 오는 문자열과 매칭이 되는 빈 이름이 있다면 그 빈이 요청을 처리해주는 핸들러로 선택이 되는 전략이고,
RequestMappingHandler는 @Controller 애노테이션 같이 붙는 @RequsetMapping 애노테이션의 URL과 매칭이 되는 해당 컨트롤러 빈이 있다면 요청을 처리할 핸들러 역할을 한다.
적합한 핸들러를 선택했다면 요청을 각 핸들러에게 넘겨주고 실행을한다. 이를 위해 adapter 패턴이 실행되고 핸들러 아답터를 통해서 각 핸들러가 실행된다.
디스패처 서블릿은 이런 핸들러 아답터 인터페이스를 통해 핸들러에 접근할 수 있다.
핸들러 아답터를 통해 요청을 처리하고 나면 리턴되는 결과물로 modelandview를 받는다.
모델엔뷰는 MVC Framework의 Model과 VIEW를 모두 갖고있는 객체라는 뜻
반한된 값을 바탕으로 디스페처 서블릿은 기본적으로 가지고 있는ContentNegotiatingViewResolver 를 통해서 이를 처리해줄 수 있는 적합한 view를 찾는다.
ViewResolver는 View값과 Locale값을 바탕으로 랜더링할 View 객체를 찾는 역할을 한다.
여기서 타임리프를 사용한다면 타임리프View값이 리턴되고 타임리프View 객체의 render() 메소드를 통해서 주어진 Model을 가지고 동적 페이지를 만드는 렌더링 과정을 시작한다.
-타임리프뷰
디스패처서블릿에서 ContentNegotiatingViewResolver 를 통해 타임리프뷰 객체를 찾았고 찾은 타임리프 객체를 통해 동적 렌더링을 시작합니다.
타임리프뷰 객체는 렌더링을 하기 위해 여러가지 정보들을 가지고 있는 Context 객체를 만드는데 이런 Context 객체 안에는 Model 정보 뿐 아니라, Locale과 관련된 정보, 타임리프 템플릿 파일을 찾기 위한 TemplateResolver, Request, Response 같은 정보도 포함하고 있습니다.
localhost:8080/basic/basic-objects?paramData=HelloParam으로 접속시
Context 객체를 만든 후에는 이제 타임리프의 템플릿을 찾고 파싱하면서 실제로 동적 렌더링을 해주는 작업을 해달라는 요청을 타임리프 엔진에게 보냅니다. 이 작업은 타임리프 엔진의 process() 메소드를 통해서 이뤄집니다.
-템플릿엔진
//템플릿 엔진
서버에서 DB 혹은 API에서 가져온 데이터를 미리 정의된 Template에 넣어 Html을 그려서 클라이언트에 전달해주는 역할입니다.
HTML 코드에서 고정적으로 사용되는 부분은 템플릿으로 만들어두고 동적으로 생성되는 부분만 템플릿 특정 장소에 끼워 넣는 방식으로 동작할 수 있도록 해줍니다.
//
템플릿엔진이 실제로 동적 페이지를 만드는 작업을 하지는 않고 템플릿 매니저가 수행합니다.
템플릿 엔진은 템플릿 매니저가 이 작업을 하기 위해 어려운 객체 초기화 작업을 하고 여러 가지 설정 정보들을 가지고 있는 역할을 합니다.
이중에서 템플릿을 찾고 가져오기 위한 템플릿 리졸버에 관련된 정보와 템플릿 처리 중에 확장해서 사용할 수 있는 기능인 Dialect와 관련된 정보가 있고, 국제화를 위한 외부 메시지 같은 것들도 있습니다.
외부 메시지는 메시지리졸버를 통해서 가능하고, 기본적으로 스탠다드매시지리졸버를 사용합니다.
그리고 템플릿엔진을 사용할 때 몇 가지 주의사항들이 있는데,
템플릿 엔진의 인스턴스는 미리 하나 만들어놓고 재사용하는 것을 권장하는데 이유는 생성 비용이 비싸서라고 합니다.
-템플릿매니저
템플릿매니저의 parseAndProcess() 메소드를 통해 동적 페이지인 뷰가 만들어집니다.
템플릿매니저는 이전에 템플릿 처리를 한 적이 있다면 LRU (LRU 알고리즘: 가장 오랫동안 참조되지 않은 페이지를 교체하는 기법) 기반의 캐싱 매커니즘을 통해 캐시 처리를 해서 바로 반환하도록 하고 만약 캐싱되어 있지 않다면 템플릿리졸버의 resolveTemplate() 메소드를 통해 Template이름을 바탕으로 경로에 있는 Template을 가져오도록 합니다.
여기서 주의할 점은 가져왔다고 해서 Template이 있는 건 아닙니다. 이는 TemplateResolver의 구현에 따라서 다른데, 어떠한 템플릿리졸버는 Performance를 위해 존재하는 여부를 체크안하는 경우도 있습니다.
템플릿을 가지고 오는 역할을 하는건 최종적으로 Spring Core Class에 있는 Resource 인터페이스의 구현체인 ClassPathResource를 통해서 이뤄지고 템플릿이 있는지 체크하는 메소드는 exist()이다. 그 후 실제로 템플릿에 프로세싱 작업을 하기 위해, ProcessHandlerChain을 만들고 각 Template에 맞는 Parser를 가지고 와서 Parsing 작업을 해준다. 즉 HTML Template이라면 HTMLParse를 가지고 와서 파싱해준다.
Parsing 작업이 끝나면 만들었던 ProcessHandlerChain을 통해 View를 만드는 작업을 끝낸다.
--신기했던 점
Th:inline=”javascript”를 사용해서 쓰는데, 데이터를 받아오는 과정에서 에러가 생기면 아래과정이 작동하지 않는다. 런타임에러가 발생하나 해당 부분 빼고는 화면을 받아온다.
에러로그가 뜨지만 그 전 화면까지는 실행시켜진다.