Tiles framework 란?
Tiles allows authors to define page fragments which can be assembled into a complete pages at runtime. These fragments, or tiles, can be used as simple includes in order to reduce the duplication of common page elements or embedded within other tiles to develop a series of reusable templates. These templates streamline the development of a consistent look and feel across an entire application.
Tiles는 웹 프로그래머가 페이지를 여러개의 조각으로 나누어 하나의 페이지로 조립해서 사용 할 수 있게끔 도와주는 프레임워크 입니다. 상단메뉴, 좌측 메뉴 등 공통적으로 사용할 페이지들을 모듈화 해서 변경되어야 하는 부분만 쉽게 변경해 사용 할 수 있게끔 돕습니다.
2017년 7월 이후로 새로운 버전이 나오지 않고 있지만, deprecated 된 건 아니고 더이상 필요한 부분이 없을만큼 잘 개발이 되어 있는 거라고 선생님께서 말씀하셨습니다.
저는 아래와 같이 jsp:include 를 통해 페이지를 모듈 화 시켜두었는데요. 해당 페이지를 Tiles를 적용하는 방식으로 변경 해 보겠습니다.
project_template.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="en">
<head>
<jsp:include page="/resources/assets/custom_template/preScript.jsp"></jsp:include>
</head>
<body>
<jsp:include page="/resources/assets/custom_template/preloader.jsp"></jsp:include>
<div id="main-wrapper">
<jsp:include page="/resources/assets/custom_template/navheader.jsp"></jsp:include>
<jsp:include page="/resources/assets/custom_template/header.jsp"></jsp:include>
<jsp:include page="/resources/assets/custom_template/sidebar-project.jsp"></jsp:include>
<!--**********************************
Content body start
***********************************-->
<div class="content-body"></div>
<!--**********************************
Content body end
***********************************-->
<jsp:include page="/resources/assets/custom_template/footer.jsp"></jsp:include>
</div>
<jsp:include page="/resources/assets/custom_template/postScript.jsp"></jsp:include>
</body>
</html>
Tiles 공식 홈페이지에 방문하면 위와 같은 화면을 볼 수 있습니다.
가장 마지막 배포 버전인 3.0.8 의 최소 요구사항은 아래와 같습니다.
1. 제일 먼저 pom.xml에 maven dependency를 추가 해 줍니다.
<!-- Presentation framework -->
<dependency>
<groupId>org.apache.tiles</groupId>
<artifactId>tiles-extras</artifactId>
<version>3.0.8</version>
</dependency>
<dependency>
<groupId>org.apache.tiles</groupId>
<artifactId>tiles-servlet</artifactId>
<version>3.0.8</version>
</dependency>
<dependency>
<groupId>org.apache.tiles</groupId>
<artifactId>tiles-jsp</artifactId>
<version>3.0.8</version>
</dependency>
2. Servlet-Context.xml 에 tiles를 등록해줍니다.
<!-- Tiles START -->
<beans:bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer"
p:definitions="/WEB-INF/tiles/tiles.xml"
p:preparerFactoryClass="org.springframework.web.servlet.view.tiles3.SpringBeanPreparerFactory"
/>
<beans:bean id="tilesViewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<beans:property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView" />
<beans:property name="order" value="2" />
</beans:bean>
<!-- Tiles END -->
p:order 는 우선순위를 의미합니다. 우선순위가 높은 view Resolver가 null을 return 할 경우에 다음 우선순위의 view resolver에게 view가 요청됩니다.
BeanNameViewResolver (order 1)
> spring container에 bean으로 등록된 view 이름을 찾아 사용하는 resolver.
tilesViewResolver (order 2)
> tiles template 으로 정의 된 뷰를 찾는 view resolver. 템플릿의 이름은 논리적 뷰 이름에 prefix, suffix를 붙여 구성.
InternalResourceViewResolver
> 웹 어플리케이션의 WAR 파일 내에 포함된 뷰 템플릿을 찾는다.뷰 템플릿의 경로는 논리적 뷰 이름에 prefix, suffix를 붙여서 구성된다. InternalResourceViewResolver가 우선순위가 높을 경우 그보다 낮은 우선순위의 viewResolver들은 사용되지 않게 된다.
: 위에 나열된 순서로 우선 순위를 부여합니다. 반드시 InternalResourceViewResolver 보다 우선순위가 높아야 작동합니다.
tilesConfigurer 와 tilesViewResolver 이렇게 두개의 bean이 등록 됩니다.
3. .xml 파일 생성.
p:definitions="/WEB-INF/tiles/tiles.xml" 에 맞춰
/WEB-INF/tiles/tiles.xml" 라는 파일을 생성합니다.
파일에 모듈들을 등록해줍니다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
"http://tiles.apache.org/dtds/tiles-config_3_0.dtd" >
<tiles-definitions>
<!-- menu list -->
<definition name="super" template="/WEB-INF/views/project/project_template.jsp">
<!-- <put-attribute name="title" value="gaia" /> -->
<put-attribute name="preScript" value="/resources/assets/custom_template/preScript.jsp" />
<put-attribute name="preloader" value="/resources/assets/custom_template/preloader.jsp" />
<put-attribute name="navheader" value="/resources/assets/custom_template/navheader.jsp" />
<put-attribute name="header" value="/resources/assets/custom_template/header.jsp" />
<put-attribute name="sidebar-project" value="/resources/assets/custom_template/sidebar-project.jsp" />
<put-attribute name="footer" value="/resources/assets/custom_template/footer.jsp"/>
<put-attribute name="postScript" value="/resources/assets/custom_template/postScript.jsp" />
</definition>
<definition name="project/*" extends="super">
<put-attribute name="content" value="/WEB-INF/views/project/{1}.jsp" />
</definition>
</tiles-definitions>
간단하게 설명하자면, definition 에 보이는 project/* 이 project/{변수} 패턴의 뷰 네임을 처리합니다. *이 한개있기 때문에 해당 변수는 그대로 value의 {1} 의 값으로 쓰여서 예를 들어 "project/main" 이라는 논리적인 뷰 네임이 리턴 된다면, tiles가 content 라는 project_template.jsp 의 빈 칸에 /WEB-INF/views/project/main.jsp 라는 타일을 만든 하나의 뷰를 만들어 리턴하게 됩니다.
이제 위에서 모듈화 해 두었던 project_template 파일을 tiles에 등록한 파일들을 토대로 재 구성 합니다.
project_template.jsp
<%--
* [[개정이력(Modification Information)]]
* Date Modifier Modification
* ---------- --------- -----------------
* 12 May 2021 Shane Initial Commit
* Copyright (c) 2021 by Team Gaia All right reserved
--%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<%-- <title><tiles:getAsString name="title" /></title> --%>
<tiles:insertAttribute name="preScript" />
</head>
<body>
<tiles:insertAttribute name="preloader" />
<div id="main-wrapper">
<tiles:insertAttribute name="navheader" />
<tiles:insertAttribute name="header" />
<tiles:insertAttribute name="sidebar-project" />
<!--**********************************
Content body start
***********************************-->
<div class="content-body">
<tiles:insertAttribute name="content" />
</div>
<!--**********************************
Content body end
***********************************-->
<tiles:insertAttribute name="footer" />
</div>
<tiles:insertAttribute name="postScript" />
</body>
</html>
미리 모듈화를 시켜 두었기 때문에 어렵지 않게 변경 할 수 있습니다.
이제 아래의 컨트롤러를 타면 자동으로 tiles가 페이지를 렌더링 해 줍니다.
package best.gaia.project.controller;
import java.util.Optional;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.servlet.ServletContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.WebApplicationContext;
import best.gaia.project.service.ProjectService;
@Controller
@RequestMapping("{manager_nick:^(?:(?!admin$|view$|restapi$).)*$}/{project_title:^(?:(?!new$|overview$|help$|setting$|activity$).)*$}")
public class ProjectTestController {
@Inject
private ProjectService service;
@Inject
private WebApplicationContext container;
private ServletContext application;
@PostConstruct
public void init() {
application = container.getServletContext();
}
private static final Logger logger = LoggerFactory.getLogger(ProjectTestController.class);
@RequestMapping(value = {"","{pageParam}"})
public String projectMenuOverview(
@PathVariable String manager_nick
,@PathVariable String project_title
,@PathVariable Optional<String> pageParam
,Model model
) {
model.addAttribute("manager_nick", manager_nick);
model.addAttribute("project_title", project_title);
String pageParamString = pageParam.isPresent() ? pageParam.get() : "overview";
model.addAttribute("pageParam", pageParamString);
return "project/"+pageParamString;
}
}
코드가 좀 복잡해 보이긴 한데 신경쓰지말고 return 에서 project/{변수} 이렇게 논리적인 뷰 네임을 반환한다는 것만 확인 하면 됩니다.
이상입니다.