IT 개발/BackEnd

[Eclipse/Spring] Spring MVC Tiles 설정 방법 | JSP 레이아웃 적용하기

으녕로그 2025. 3. 28. 13:52
728x90
반응형

오늘은 tiles 설정을 하면서 애를 먹었던 일기를 작성해보려고 한다. 이 글이 tiles 설정을 하려는 사람들에게 도움이 될 수 있으면 좋겠다.

 

tiles란?

화면의 레이아웃을 지정하여 공통으로 쓰이는 코드를 개별로 작성하지 않고 적용 및 관리를 편리하게 할 수 있도록 해주는 템플릿 엔진입니다.

 

tiles 설정 1. pom.xml에 관련 dependency 추가(버전 통일)

//pom.xml

<!-- JSP, JSTL 설정 -->
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
    <version>11.0.0-M24</version>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>

<!-- tiles 설정 -->
<dependency>
    <groupId>org.apache.tiles</groupId>
    <artifactId>tiles-core</artifactId>
    <version>3.0.8</version>
</dependency>
<dependency>
    <groupId>org.apache.tiles</groupId>
    <artifactId>tiles-jsp</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-extras</artifactId>
    <version>3.0.8</version>
</dependency>

jstl 로 jsp 파일에서 tiles 위치를 잡아주고 있기 때문에 같이 넣어주는 게 좋다.

tiles-extras 는 없어도 괜찮지만 completeAutoLoad 관련 에러가 발생한다면 해당 dependency를 추가해서 정의를 해줘야하는데 자세한 내용은 관련 소스와 함께 아래에서 설명할 예정!

이렇게 dependency를 추가했다면 tiles 관련 bean을 설정해줘야 하는데 dispatcher-servlet.xml 에도 무관하지만 나는 따로 파일을 만들었다. (이게 삽질의 원인이 될 줄은 꿈에도 몰랐지만..)

 

tiles 설정 2. tiles configurer 와 UrlBasedViewResolver 설정

//context-tiles.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- tiles 설정 : JSP head 태그 공통화 -->
    <bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
    	<property name="definitions">
    		<list>
    			<value>/WEB-INF/config/spring/tiles-definitions.xml</value>
    		</list>
    	</property>
    	<!-- completeAutoLoad 속성제거  -->
    	<property name="checkRefresh" value="true" />
    </bean>
    
    <bean id="tilesViewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
    	<property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView" />
    	<property name="order" value="1" />
    </bean>
</beans>

 

tiles의 ViewResolver는 UrlBasedViewResolver로 설정해줘야 한다. tilesConfigurer 는 tiles의 레이아웃 즉 header, body, footer 설정을 담고 있는 파일 tiles-definitions.xml 을 지정해주면 된다.

위 코드로 적용했는데 의존성이 없다는 에러가 난다면 pom.xml의 tiles-extras 라는 의존성이 있는지 확인하자.

없다면 checkRefresh 라는 프로퍼티는 지워야한다. 이 프로퍼티는 tiles-extras 에 있는 속성이기 때문에 확인 필수!

 

tiles 설정 3. tiles-definitions.xml 파일 

//tiles-definitions.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>
	<definition name="defaultTemplate" template="/WEB-INF/views/layout/default.jsp">
		<put-attribute name="header" value="/WEB-INF/views/layout/header.jsp" />
		<put-attribute name="body"   value="" />
		<put-attribute name="footer" value="/WEB-INF/views/layout/footer.jsp" />
	</definition>
	
	<!-- <definition name="mainPage" extends="defaultTemplate">
		<put-attribute name="body" value="/WEB-INF/views/main/login.jsp" />
	</definition>
	 -->
	<definition name="/*/*" extends="defaultTemplate">
		<put-attribute name="body" value="/WEB-INF/views/{1}/{2}.jsp" />
	</definition>
</tiles-definitions>

mainPage 이 부분은 테스트용으로 기재했으며 controller 에서 return 값을 "mainPage"로 했을 때 적용되는 걸 볼 수 있었다. 하지만 나는 유동적으로 변경이 되길 바랬기 때문에 definition name="/*/*" 로 설정해서 {1}, {2} 로 유동적으로 값이 바뀔 수 있도록 지정했다.

즉, definition name == controller or modelandview setViewname 값

 

이렇게 3가지 설정이 끝났다면 이제 레이아웃의 jsp 파일을 만들 차례!

 

tiles 설정 4. 레이아웃 jsp 파일 만들기

//default.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<!DOCTYPE html>
<html>
<head>	
	<tiles:insertAttribute name="header" />
</head>
<body>
	<tiles:insertAttribute name="body" />
	<tiles:insertAttribute name="footer" />
</body>
</html>

//header.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<!-- 공통 css, js  -->
<link rel="stylesheet" type="text/css" href="/resources/css/style.css">
<script type="text/javascript" src="/resources/js/common.js"></script>
<script src="https://code.jquery.com/jquery-3.4.1.js"></script>
<!-- favicon error 대체 -->
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>


//footer.jsp
</html>

이렇게 각 3개의 파일을 만들면 되는데 제일 중요한 건 default.jsp

tiles 태그 라이브러리를 추가한 뒤에 jstl 태그로 입력해준다. 이걸 위해서 pom.xml 에 jstl dependency가 있어야함 없다면 꼭 추가!

 

tiles설정 4. dispatcher-servlet.xml 이 아닌 따로 tiles 설정파일을 만들었다면 dispatcher-servlet.xml 에 import 시켜줘야함!

//dispatcher-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/mvc
           http://www.springframework.org/schema/mvc/spring-mvc.xsd">
		   
    <!-- 스프링 어노테이션 활성화 -->
    <mvc:annotation-driven />
    
    <!-- 인터셉터 로그 기록을 위해 설정 -->
    <mvc:interceptors>
    	<mvc:interceptor>
    		<mvc:mapping path="/**"/>
    		<bean class="com.spring.interceptor.LogInterceptor" />
    	</mvc:interceptor>
    </mvc:interceptors>
    
    <!-- js, css 매핑  -->
    <mvc:resources location="/resources/" mapping="/resources/**" />

    <context:component-scan base-package="com.spring">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>   

    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/" />
        <property name="suffix" value=".jsp" />
        <property name="order" value="2" />
    </bean>
    
    <!-- jsonview 설정  -->
	<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
		<property name="order" value="0" />
	</bean>
	<bean id="jsonView" class="org.springframework.web.servlet.view.json.MappingJackson2JsonView">
		<property name="contentType" value="application/json;charset=UTF-8"></property>
	</bean>
	
	<!-- tiles 설정 -->
	<!-- viewResolver 순서 JsonView -> UrlBased -> Internal -->
	<import resource="/spring/context-tiles.xml" />
</beans>

아래 쪽에 tiles 설정 처럼 import 시켜줘야한다. 그리고 순서도 굉장히 중요한데,

tiles 뷰리졸버는 jsp 보다 먼저, jsonView 보다는 나중에 적용이 되어야 하기 때문에 순서는

JsonView 0 -> UrlBased 1 -> Internal 2 이렇게 지정을 해줘야한다. 이렇게 글로만 쓰면 애매할 것 같아 해당 부분만 아래 모은 건 이렇다.

<!-- jsonview order : 0 -->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
    <property name="order" value="0" />
</bean>
<bean id="jsonView" class="org.springframework.web.servlet.view.json.MappingJackson2JsonView">
    <property name="contentType" value="application/json;charset=UTF-8"></property>
</bean>

<!-- tilesViewResolver order : 1 -->
<bean id="tilesViewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView" />
    <property name="order" value="1" />
</bean>

<!-- JSP ViewResolver order : 2 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/" />
    <property name="suffix" value=".jsp" />
    <property name="order" value="2" />
</bean>

 

적용된 Login.jsp 와 LoginController.java 공유

//login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<!-- 페이지 전용 js -->
<script type="text/javascript" src="/resources/js/main/login.js"></script>
</head>
<body>
	<form id="pageForm" action="/main/userLogin" method="post">
		<div class="pageDiv">
			<label for="userId">ID</label>
			<input type="text" id="userId" name="userId">
			<label for="userPw">PW</label>
			<input type="password" id="userPw" name="userPw">
			<button id="loginBtn" type="submit">login</button>
		</div>
	</form>
</body>
</html>
//LoginController.java

package com.spring.main.controller;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import com.spring.main.service.LoginService;

@Controller
public class LoginController {
	
	private static final Logger logger = LoggerFactory.getLogger(LoginController.class);
	
	@Autowired
	private LoginService service;
	
	@RequestMapping("/")
	public ModelAndView home() {
		ModelAndView mv = new ModelAndView();
		mv.setViewName("/main/login");
		return mv;
	}
	
	@RequestMapping("/main/userLogin")
	public ModelAndView userLogin(@RequestParam Map<String, Object> paramMap, HttpServletRequest request, HttpServletResponse response) {
		ModelAndView mv = new ModelAndView();
		Boolean result = service.userLogin(paramMap);
		if(result) {
			mv.setViewName("redirect:/board/boardMain");
		}else {
			mv.setViewName("redirect:/main/login");
			mv.addObject("login failed");
		}
		return mv;
	}
}

mv.setViewName("/main/login") -> 이렇게 하면 tiles-definitions.xml 의 {1} 은 main, {2} 는 login 이런 식으로 적용이 되는 걸 알 수 있다. 

 

이렇게 테스트 하면 적용이 잘되는 걸 알 수 있다!

그동안 tiles 설정파일을 따로 만든다면 dispatcher-servlet.xml 에 import를 해줘야하는 지 모르고 삽질을 굉장히 오래했었다.. web.xml 에 정의했던 context-*.xml 로 다 적용이 되는 줄로만 알고있었는데..

그렇게 새롭게 알게된 사실도 있고 내 힘으로 설정을 해냈다는게 굉장히 뿌듯!

tiles 설정을 하려는 사람들에게 도움이 될 수 있기를!

728x90
반응형