์๋ฐ ์น Decorator ํจํด ๋ ์ด์์ ์์ง ์์ ๊ฐ์ด๋: SiteMesh 3 & 2 ์ํคํ
์ฒ, ์๋ธ๋ฆฟ ํํฐ(SiteMeshFilter) ๊ฐ๋ก์ฑ๊ธฐ, sitemesh.xml ๋ฐ์ฝ๋ ์ดํฐ ๋งคํ, Spring Boot 3 Java Config ์ฐ๋, ๊ด๋ฆฌ์/์ฌ์ฉ์ ๋ค์ค ๋ ์ด์์ ๋ถ๊ธฐ ๋ฐ AJAX ๋ฐ์ดํจ์ค
## 1. SiteMesh ์ค์น & ์ํคํ
์ฒ ๊ฐ์ด๋ (Architecture)
### ๐ป ์์คํ
์ค์น ๋ฐ ์คํ ์๊ตฌ์ฌ์ (System Requirements)
| ํญ๋ชฉ | ์ต์ ์ฌ์ (Minimum) | ๊ถ์ฅ ์ฌ์ (Recommended) | ์ฐธ๊ณ ๋ฐ ๋น๊ณ |
| :--- | :--- | :--- | :--- |
| **์ด์์ฒด์ (OS)** | Windows, Linux (Ubuntu, RHEL, Alpine), macOS | Ubuntu 22.04 LTS, Windows Server, macOS | Java ์๋ธ๋ฆฟ ์์ง์ด ๋์ํ๋ ๋ชจ๋ OS |
| **CPU** | 1 ์ฝ์ด ์ด์ (x86_64 / ARM64) | 2 ์ฝ์ด ~ 4 ์ฝ์ด ์ด์ | ์๋ธ๋ฆฟ ํํฐ ๋ฒํผ๋ง ๋ฐ HTML DOM ํ์ฑ ์ฐ์ฐ |
| **๋ฉ๋ชจ๋ฆฌ (RAM)** | 512 MB ์ด์ (JVM ํ 256 MB) | 2 GB ~ 4 GB ์ด์ | HTML ์๋ต ๋ฒํผ ๋ฐ ๋ฉ๋ชจ๋ฆฌ ์บ์ฑ |
| **๋์คํฌ (Storage)** | 200 MB ์ฌ์ ๊ณต๊ฐ | 2 GB ์ด์์ ์ฌ์ ๊ณต๊ฐ | WAR ๋ฐฐํฌ๋ณธ, JSP ์ปดํ์ผ ์บ์ ๋ฐ ๋ก๊ทธ |
| **๋คํธ์ํฌ / ๋ฐํ์** | Java 8, 11, 17, 21 LTS & Servlet 3.1+ | OpenJDK 17 LTS ์ด์ & Tomcat 9/10+ | SiteMesh 2.x(`javax.*`), SiteMesh 3.x(`jakarta.*`) |
---
### ๐ฆ ์์กด์ฑ ์ค์ (Maven & Gradle)
```xml
<!-- [1] Maven (pom.xml) - SiteMesh 3.x (ํ๋์ ํ์ค ๊ถ์ฅ) -->
<dependency>
<groupId>org.sitemesh</groupId>
<artifactId>sitemesh</artifactId>
<version>3.0.1</version>
</dependency>
<!-- [2] Maven (pom.xml) - SiteMesh 2.x (์ ์์ ๋ถํ๋ ์์ํฌ / ๋ ๊ฑฐ์ ์ ์ง๋ณด์์ฉ) -->
<dependency>
<groupId>opensymphony</groupId>
<artifactId>sitemesh</artifactId>
<version>2.4.2</version>
</dependency>
```
```groovy
// [3] Gradle (build.gradle)
dependencies {
// SiteMesh 3.x
implementation 'org.sitemesh:sitemesh:3.0.1'
// ๋๋ SiteMesh 2.4.2 ๋ ๊ฑฐ์
// implementation 'opensymphony:sitemesh:2.4.2'
}
```
---
## 2. SiteMesh ๋ฉํ ๋ชจ๋ธ & ๋ฐ์ฝ๋ ์ดํฐ ํจํด (Mental Model)
SiteMesh๋ **Decorator(์ฅ์์) ํจํด**์ ์๋ธ๋ฆฟ ํํฐ(Servlet Filter) ๋ ๋ฒจ์์ ๊ตฌํํ ํ๋ ์์ํฌ์
๋๋ค.
๊ฐ๋ฐ์๋ ์/ํ๋จ ๋ ์ด์์์ ์ ํ ์ ๊ฒฝ ์ฐ์ง ์๊ณ **์์ํ ์ฝํ
์ธ HTML/JSP**๋ง ์์ฑํ๋ฉฐ, ์๋ธ๋ฆฟ ํํฐ๊ฐ ์ต์ข
์๋ต์ ๊ฐ๋ก์ฑ์ ๊ณตํต ํ
ํ๋ฆฟ(Header, Nav, Footer) ์์ ๋ณธ๋ฌธ์ ๋ผ์ ๋ฃ์ต๋๋ค.
### ๐งญ ์๋ธ๋ฆฟ ํํฐ ์ธํฐ์
ํธ & ๋ฐ์ฝ๋ ์ดํ
ํ๋ฆ
```text
[ํด๋ผ์ด์ธํธ ๋ธ๋ผ์ฐ์ ]
โ (1) GET /board/list
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ์๋ธ๋ฆฟ ์ปจํ
์ด๋ (Tomcat) โ
โ โ
โ [SiteMeshFilter] (์์ฒญ ํต๊ณผ) โ
โ โ โ
โ โผ โ
โ [Spring Controller โ board/list.jsp ๋ ๋๋ง] โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ <head><title>๊ฒ์ํ ๋ชฉ๋ก</title></head> โ โ
โ โ <body><table class="list">...</table></body> โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ (2) HTML ๋ฒํผ ๊ฐ๋ก์ฑ๊ธฐ (Intercept Response) โ
โ โผ โ
โ [SiteMesh HTML ํ์ ๋ถ์] โ
โ โโ Title : "๊ฒ์ํ ๋ชฉ๋ก" โ
โ โโ Head : CSS, ํ์ด์ง ์ ์ฉ ๋ฉํ ํ๊ทธ โ
โ โโ Body : ๊ฒ์ํ ํ
์ด๋ธ ์ฝํ
์ธ ๋ณธ๋ฌธ โ
โ โ โ
โ โผ โ
โ [Decorator ํ
ํ๋ฆฟ ์ฃผ์
(/WEB-INF/decorators/default.jsp)] โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ <html> โ โ
โ โ <head><sitemesh:write property='head'/></head> โ โ
โ โ <body> โ โ
โ โ <div class="header">๊ณตํต GNB ๋ค๋น๊ฒ์ด์
</div> โ โ
โ โ <div class="main"><sitemesh:write property='body'/> โ โ
โ โ <div class="footer">๊ณตํต ํธํฐ ์นดํผ๋ผ์ดํธ</div> โ โ
โ โ </body> โ โ
โ โ </html> โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ (3) ์ต์ข
์กฐ๋ฆฝ๋ ์์ฑ HTML ์ ์ก
โผ
[๋ธ๋ผ์ฐ์ ํ๋ฉด ์ต์ข
ํ์]
```
### โ๏ธ SiteMesh vs Apache Tiles vs Thymeleaf Layout ๋น๊ต
| ๋น๊ต ํญ๋ชฉ | SiteMesh | Apache Tiles | Thymeleaf Layout Dialect |
| :--- | :--- | :--- | :--- |
| **ํต์ฌ ํจํด** | **Decorator ํจํด** (์๋ต ์ฌํ ์ฅ์) | **Composite View ํจํด** (์ฌ์ ์กฐ๋ฆฝ) | **Template Inheritance** (ํ
ํ๋ฆฟ ์์) |
| **๋์ ์์น** | ์๋ธ๋ฆฟ ํํฐ(`Servlet Filter`) | ์คํ๋ง ๋ทฐ ๋ฆฌ์กธ๋ฒ(`ViewResolver`) | ํ
ํ๋ฆฟ ์์ง ๋ด๋ถ ํ์ |
| **๊ฐ๋ณ ํ์ด์ง ์ฝ๋** | **์์ ํ ์์ HTML** (`<head>`, `<body>` ํฌํจ ๊ฐ๋ฅ) | ์กฐ๊ฐ JSP ์กฐ๊ฐ ํ์ผ (`<div>`๋ง ์์ฑ) | `layout:decorate` ์์ฑ ์ ์ธ |
| **ํ๋ ์์ํฌ ์ข
์์ฑ** | ์์ (JSP, ์๋ธ๋ฆฟ, ์คํ๋ง, ์ ์ HTML ๋ชจ๋ ์ ์ฉ) | Spring MVC ๋ฐ JSP ๊ฐ๋ ฅ ๊ฒฐํฉ | Thymeleaf ์ ์ฉ |
| **์ ํฉํ ํ๊ฒฝ** | ๋๊ท๋ชจ ๋ ๊ฑฐ์ ํฌํธ, ์ ์ ํ์ด์ง์ ๋์ ํ์ด์ง ํผํฉ ํ๊ฒฝ | ์ ์์ ๋ถํ๋ ์์ํฌ ํ์ค ์ํฐํ๋ผ์ด์ฆ | ์ต์ Spring Boot ๋ชจ๋ ์น ์ ํ๋ฆฌ์ผ์ด์
|
---
## 3. [์ด๋ณด์ 1๋จ๊ณ] 3๋ถ ํฌ๋ก์๋: SiteMesh 3 ๋ฌด์ค์ ๋น ๋ฅธ ์ฐ๋
์คํ๋ง ๋ถํธ ํ๊ฒฝ์์ ์๋ฐ ์ค์ (`FilterRegistrationBean`)์ผ๋ก SiteMesh 3๋ฅผ ๋ฑ๋กํ๊ณ ๊ธฐ๋ณธ ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ ์ฉํฉ๋๋ค.
### โ 1. ์๋ฐ ํํฐ ์ค์ (`SiteMeshConfig.java`)
```java
package com.lucky.config;
import org.sitemesh.builder.SiteMeshFilterBuilder;
import org.sitemesh.config.ConfigurableSiteMeshFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SiteMeshConfig {
@Bean
public FilterRegistrationBean<ConfigurableSiteMeshFilter> siteMeshFilter() {
FilterRegistrationBean<ConfigurableSiteMeshFilter> filter = new FilterRegistrationBean<>();
// SiteMesh ์ค์ ๋น๋ ์ต๋ช
ํด๋์ค ์ ์
filter.setFilter(new ConfigurableSiteMeshFilter() {
@Override
protected void applyCustomConfiguration(SiteMeshFilterBuilder builder) {
// 1) ์ ์ฒด ๊ธฐ๋ณธ ๋ฐ์ฝ๋ ์ดํฐ ์ ์ฉ
builder.addDecoratorPath("/*", "/WEB-INF/views/decorators/main-decorator.jsp");
// 2) API ๋ฐ ์ ์ ์์์ ๋ฐ์ฝ๋ ์ดํฐ ์ ์ธ
builder.addExcludedPath("/api/*");
builder.addExcludedPath("/static/*");
builder.addExcludedPath("/resources/*");
}
});
filter.addUrlPatterns("/*");
filter.setOrder(1); // ์ธ์ฝ๋ฉ ํํฐ ๋ค์์ผ๋ก ์คํ๋๋๋ก ์ค์
return filter;
}
}
```
---
### ๐จ 2. ๊ณตํต ๋ฐ์ฝ๋ ์ดํฐ ๋ ์ด์์ (`main-decorator.jsp`)
```jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- ์๋ณธ ํ์ด์ง์ <title> ํ๊ทธ ์๋ ์ฃผ์
-->
<title><sitemesh:write property='title'/> - LuckyTech World</title>
<!-- ๊ณตํต ๊ธ๋ก๋ฒ ์คํ์ผ์ํธ -->
<link rel="stylesheet" href="/static/css/global.css">
<!-- ์๋ณธ ํ์ด์ง์ <head> ์์ ํฌํจ๋ ์ ์ฉ CSS/์คํฌ๋ฆฝํธ ์ฃผ์
-->
<sitemesh:write property='head'/>
</head>
<body>
<!-- ๊ณตํต ํค๋ -->
<header class="site-header">
<div class="logo">๐ LuckyTech World</div>
<nav class="gnb">
<a href="/">ํ</a>
<a href="/board">๊ฒ์ํ</a>
<a href="/about">์๊ฐ</a>
</nav>
</header>
<!-- ์๋ณธ ํ์ด์ง์ <body> ๋ด์ฉ ์๋ ์ฃผ์
-->
<main class="site-content">
<sitemesh:write property='body'/>
</main>
<!-- ๊ณตํต ํธํฐ -->
<footer class="site-footer">
<p>© 2026 LuckyTech World. All rights reserved.</p>
</footer>
</body>
</html>
```
---
### ๐ 3. ๊ฐ๋ฐ์๊ฐ ์์ฑํ๋ ์ค์ ํ๋ฉด (`content.jsp`)
ํค๋/ํธํฐ๋ฅผ ํฌํจํ์ง ์๊ณ ๋ณธ์ธ์ด ํ์ํ ๋
๋ฆฝ์ ์ธ HTML ํ์ด์ง๋ง ์์ฑํฉ๋๋ค.
```jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<!-- ์ด ํ์ดํ์ ๋ฐ์ฝ๋ ์ดํฐ์ <title> ์๋ฆฌ์ ์๋ ์ฝ์
๋ฉ๋๋ค -->
<title>์๋น์ค ์๊ฐ</title>
<style>
.service-intro { color: #10b981; font-weight: bold; }
</style>
</head>
<body>
<div class="container">
<h1 class="service-intro">SiteMesh 3 ๋ฐ์ฝ๋ ์ดํฐ ํฌ๋ก์๋!</h1>
<p>๊ฐ๋ณ ๋ทฐ ํ์ด์ง์์๋ ๋ ์ด์์ include ์ฝ๋๋ฅผ ์ผ์ ์์ฑํ ํ์๊ฐ ์์ต๋๋ค.</p>
</div>
</body>
</html>
```
#### ๐ฅ๏ธ ๋ธ๋ผ์ฐ์ ์์ค ๋ณด๊ธฐ ๊ฒฐ๊ณผ (Final Rendered HTML)
```html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>์๋น์ค ์๊ฐ - LuckyTech World</title>
<link rel="stylesheet" href="/static/css/global.css">
<style>
.service-intro { color: #10b981; font-weight: bold; }
</style>
</head>
<body>
<header class="site-header">
<div class="logo">๐ LuckyTech World</div>
<nav class="gnb">
<a href="/">ํ</a>
<a href="/board">๊ฒ์ํ</a>
<a href="/about">์๊ฐ</a>
</nav>
</header>
<main class="site-content">
<div class="container">
<h1 class="service-intro">SiteMesh 3 ๋ฐ์ฝ๋ ์ดํฐ ํฌ๋ก์๋!</h1>
<p>๊ฐ๋ณ ๋ทฐ ํ์ด์ง์์๋ ๋ ์ด์์ include ์ฝ๋๋ฅผ ์ผ์ ์์ฑํ ํ์๊ฐ ์์ต๋๋ค.</p>
</div>
</main>
<footer class="site-footer">
<p>© 2026 LuckyTech World. All rights reserved.</p>
</footer>
</body>
</html>
```
---
## 4. [2๋จ๊ณ] ํต์ฌ ๊ธฐ๋ฅ: sitemesh.xml ๋งคํ ๊ท์น & ํ๋กํผํฐ ์ฃผ์
### โ๏ธ 1. XML ์ ์ธ์ ์ค์ (`WEB-INF/sitemesh.xml`)
์๋ฐ ์ฝ๋ ๋์ XML ํ์ผ๋ก ๋งคํ ๊ท์น์ ์ค์ ๊ด๋ฆฌํ ๋ ์ฌ์ฉํฉ๋๋ค.
```xml
<?xml version="1.0" encoding="UTF-8"?>
<sitemesh>
<!-- 1. ๊ธฐ๋ณธ ๋ฐ์ฝ๋ ์ดํฐ ํ
ํ๋ฆฟ ์ง์ -->
<mapping path="/*" decorator="/WEB-INF/views/decorators/default.jsp"/>
<!-- 2. ๊ด๋ฆฌ์ ๊ฒฝ๋ก ๋ค์ค ๋ฐ์ฝ๋ ์ดํฐ ๋ถ๊ธฐ -->
<mapping path="/admin/*" decorator="/WEB-INF/views/decorators/admin.jsp"/>
<!-- 3. ๋ชจ๋ฌ/ํ์
์ฐฝ ์ ์ฉ ์ฌํ ๋ฐ์ฝ๋ ์ดํฐ -->
<mapping path="/popup/*" decorator="/WEB-INF/views/decorators/popup.jsp"/>
<!-- 4. ๋ฐ์ฝ๋ ์ดํฐ ์ ์ฉ ์ ์ธ(Excludes) ํจํด -->
<mapping path="/api/*" exclude="true"/>
<mapping path="/rest/*" exclude="true"/>
<mapping path="/download/*" exclude="true"/>
<mapping path="*.json" exclude="true"/>
</sitemesh>
```
---
### ๐ท๏ธ 2. SiteMesh 3 ํ๊ทธ์ ํ๋กํผํฐ ์ฃผ์
๋ฌธ๋ฒ
| ํ๊ทธ | ์ญํ ๋ฐ ์ค๋ช
|
| :--- | :--- |
| `<sitemesh:write property='title'/>` | ์ฝํ
์ธ ํ์ด์ง์ `<title>` ํ
์คํธ๋ฅผ ์ถ์ถํ์ฌ ์ฝ์
|
| `<sitemesh:write property='body'/>` | ์ฝํ
์ธ ํ์ด์ง์ `<body>` ๋ด๋ถ ์ ์ฒด HTML์ ์ถ์ถํ์ฌ ์ฝ์
|
| `<sitemesh:write property='head'/>` | ์ฝํ
์ธ ํ์ด์ง์ `<head>` ๋ด๋ถ ํ๊ทธ(์คํฌ๋ฆฝํธ, ์ธ๋ผ์ธ ์คํ์ผ, ๋ฉํ) ์ฝ์
|
| `<sitemesh:write property='meta.author'/>` | ์ฝํ
์ธ ์ `<meta name="author" content="lucky">` ์์ฑ๊ฐ ์ถ์ถ |
| `<sitemesh:write property='body.class'/>` | ์ฝํ
์ธ `<body class="dark-mode">`์ ํด๋์ค ์์ฑ๊ฐ ์ฃผ์
|
---
### ๐งฉ 3. ์ปค์คํ
์ปจํ
์ธ ํ๊ทธ ์ ๋ฌ (`<content tag="...">`)
ํ์ด์ง๋ณ๋ก ํน์ ์์ญ(์: ์ฌ์ด๋๋ฐ ์์ ฏ, ํ์ด์ง ์ ์ฉ JavaScript ํ๋จ ์คํฌ๋ฆฝํธ)์ ๋ฐ์ฝ๋ ์ดํฐ์ ํน์ ์์น์ ๊ฝ์ ๋ฃ์ ๋ ์ฌ์ฉํฉ๋๋ค.
#### ๋ณธ๋ฌธ ํ์ด์ง (`detail.jsp`)
```jsp
<body>
<h2>๊ฒ์๊ธ ์์ธ ํ์ด์ง</h2>
<p>๋ณธ๋ฌธ ๋ด์ฉ์
๋๋ค.</p>
<!-- ๋ฐ์ฝ๋ ์ดํฐ ํน์ ์ฌ๋กฏ์ผ๋ก ๋ณด๋ผ ์ปค์คํ
๋ธ๋ก ์ ์ธ -->
<content tag="pageScripts">
<script src="/static/js/chart.min.js"></script>
<script>
console.log("ํ์ด์ง ์ ์ฉ ํ๋จ ์คํฌ๋ฆฝํธ ์คํ");
</script>
</content>
</body>
```
#### ๋ฐ์ฝ๋ ์ดํฐ ํ
ํ๋ฆฟ (`default.jsp`)
```jsp
<body>
<div class="content">
<sitemesh:write property='body'/>
</div>
<!-- ํ๋จ ๊ณตํต ์คํฌ๋ฆฝํธ -->
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/common.js"></script>
<!-- ๊ฐ๋ณ ํ์ด์ง์์ ์ ๋ฌ๋ pageScripts ๋ธ๋ก์ ์ฌ๊ธฐ์ ๋ ๋๋ง -->
<sitemesh:write property='page.pageScripts'/>
</body>
```
---
## 5. [3๋จ๊ณ] ์ค๋ฌด ์์ฉ: SiteMesh 2 ๋ ๊ฑฐ์ & ๋ค์ค ๋ ์ด์์
๋ํ๋ฏผ๊ตญ ๊ณต๊ณต๊ธฐ๊ด ๋ฐ ์ ์์ ๋ถํ๋ ์์ํฌ(eGovFrame) ํ๊ฒฝ์์ ๋๋ฆฌ ์ฐ์ด๋ SiteMesh 2.x ์ค์ ํ์ค์
๋๋ค.
### ๐๏ธ 1. SiteMesh 2.x ํ์ค ๊ตฌ์ฑ (`web.xml` & `decorators.xml`)
#### `WEB-INF/web.xml` ํํฐ ๋ฑ๋ก
```xml
<filter>
<filter-name>sitemesh</filter-name>
<filter-class>com.opensymphony.sitemesh.webapp.SiteMeshFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>sitemesh</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
```
#### `WEB-INF/decorators.xml` ๋ฐ์ฝ๋ ์ดํฐ ์ ์
```xml
<?xml version="1.0" encoding="UTF-8"?>
<decorators defaultdir="/WEB-INF/views/decorators">
<!-- ์ ์ธ ๊ฒฝ๋ก -->
<excludes>
<pattern>/api/*</pattern>
<pattern>/rest/*</pattern>
<pattern>/resources/*</pattern>
<pattern>*.ajax</pattern>
</excludes>
<!-- ์ฌ์ฉ์ ํฌํธ ๊ธฐ๋ณธ ๋ ์ด์์ -->
<decorator name="main" page="main-layout.jsp">
<pattern>/*</pattern>
</decorator>
<!-- ๊ด๋ฆฌ์ ์ด๋๋ฏผ ์ ์ฉ ๋ ์ด์์ -->
<decorator name="admin" page="admin-layout.jsp">
<pattern>/admin/*</pattern>
<pattern>/manage/*</pattern>
</decorator>
</decorators>
```
#### SiteMesh 2 ๋ฐ์ฝ๋ ์ดํฐ JSP ํ๊ทธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ (`main-layout.jsp`)
```jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="decorator" uri="http://www.opensymphony.com/sitemesh/decorator" %>
<%@ taglib prefix="page" uri="http://www.opensymphony.com/sitemesh/page" %>
<!DOCTYPE html>
<html>
<head>
<title><decorator:title default="๊ธฐ๋ณธ ์์คํ
๋ช
"/></title>
<decorator:head/>
</head>
<body class="<decorator:getProperty property='body.class' default='normal'/>">
<div id="header">
<jsp:include page="/WEB-INF/views/layout/gnb.jsp"/>
</div>
<div id="container">
<!-- ์๋ณธ ๋ณธ๋ฌธ ์ฝ์
-->
<decorator:body/>
</div>
<div id="footer">
<jsp:include page="/WEB-INF/views/layout/footer.jsp"/>
</div>
</body>
</html>
```
---
### ๐ก๏ธ 2. AJAX & REST JSON ์๋ต ์ ๋ฐ์ฝ๋ ์ดํฐ ์๋ ๋ฐ์ดํจ์ค ํํฐ
๋น๋๊ธฐ Fetch/AJAX ์์ฒญ ์ HTML ๋ฐ์ฝ๋ ์ดํฐ๊ฐ ๊ฐ์ธ์ ธ JSON ํ์ฑ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ ๋ฌธ์ ๋ฅผ ์์ฒ ๋ฐฉ์งํ๋ ์ปค์คํ
ํํฐ ๊ตฌํ์
๋๋ค.
```java
package com.lucky.filter;
import jakarta.servlet.http.HttpServletRequest;
import org.sitemesh.builder.SiteMeshFilterBuilder;
import org.sitemesh.config.ConfigurableSiteMeshFilter;
public class CustomSiteMeshFilter extends ConfigurableSiteMeshFilter {
@Override
protected void applyCustomConfiguration(SiteMeshFilterBuilder builder) {
// ๊ธฐ๋ณธ ๋ฐ์ฝ๋ ์ดํฐ
builder.addDecoratorPath("/*", "/WEB-INF/views/decorators/default.jsp");
builder.addDecoratorPath("/admin/*", "/WEB-INF/views/decorators/admin.jsp");
// ์ ์ ๋ฆฌ์์ค ์ ์ธ
builder.addExcludedPath("/static/*");
builder.addExcludedPath("/upload/*");
// ์ปค์คํ
์์ฒญ ๊ฒ์ฌ (AJAX ์์ฒญ ํค๋ ๊ฐ์ง ์ ๋ฐ์ฝ๋ ์ดํฐ ์คํต)
builder.addCustomRule(new org.sitemesh.content.tagrules.TagRuleBundle() {
// ์ปค์คํ
ํ๊ทธ ๊ท์น ํ์ ์ ๊ตฌํ
});
}
// ๋๋ ์ปจํธ๋กค๋ฌ์์ ํค๋ ์ ๋ฌ ๋๋ URL ํ์ฅ์(.json)๋ก ๋ถ๊ธฐ ๊ถ์ฅ
}
```
---
## 6. ํ๋ก๋์
ํธ๋ฌ๋ธ์ํ
& ์ด์ ์ฒดํฌ๋ฆฌ์คํธ (Troubleshooting)
### ๐ ์์ฃผ ๋ฐ์ํ๋ ์ค๋ฅ & ํด๊ฒฐ ๊ฐ์ด๋
#### 1. ํ๊ธ ์ธ์ฝ๋ฉ ๊นจ์ง ํ์ (EUC-KR / ๊นจ์ง ๋ฌธ์ ์ถ๋ ฅ)
- **์์ธ**: ์๋ธ๋ฆฟ ํํฐ ์ฒด์ธ์์ `SiteMeshFilter`๊ฐ `CharacterEncodingFilter`๋ณด๋ค ๋จผ์ ์คํ๋๋ฉด ์์ฒญ/์๋ต ์ธ์ฝ๋ฉ์ด ๊นจ์ง๋๋ค.
- **ํด๊ฒฐ์ฑ
**: ์คํ๋ง ๋ถํธ์์ `CharacterEncodingFilter`๋ฅผ `Ordered.HIGHEST_PRECEDENCE`๋ก ์ง์ ํ๊ณ , SiteMesh๋ ๊ทธ ์ดํ(์: Order 1 ๋๋ 2)๋ก ๋ฑ๋กํด์ผ ํฉ๋๋ค.
```java
filterRegistrationBean.setOrder(2);
```
#### 2. Spring Boot 3 (Jakarta Servlet) ํธํ์ฑ ๋ฌธ์
- **์์ธ**: SiteMesh 2.x์ SiteMesh 3.0.1 ์ด์ ๋ฒ์ ์ ๊ณผ๊ฑฐ `javax.servlet.*` ํจํค์ง๋ฅผ ์ฐธ์กฐํ๋ฏ๋ก Spring Boot 3 (`jakarta.servlet.*`) ํ๊ฒฝ์์ `ClassNotFoundException`์ด ๋ฐ์ํฉ๋๋ค.
- **ํด๊ฒฐ์ฑ
**:
- Spring Boot 3 ํ๊ฒฝ์์๋ `org.sitemesh:sitemesh:3.0.1` ์ด์์ ์ฌ์ฉํ๊ฑฐ๋ ํฐ์บฃ ๋ง์ด๊ทธ๋ ์ด์
๋๊ตฌ๋ฅผ ์ ์ฉํด์ผ ํฉ๋๋ค.
- ์ต์ Spring Boot 3 ์ ๊ท ํ๋ก์ ํธ๋ผ๋ฉด **Thymeleaf Layout Dialect** ๋์
์ ๊ถ์ฅํฉ๋๋ค.
#### 3. ์ค๋ณต๋ `<head>` ๋๋ ์คํ์ผ์ํธ ๋ค์ค ๋ก๋ฉ ๋ฒ๊ทธ
- **์์ธ**: `<sitemesh:write property='head'/>`๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ๊ฐ๋ณ ํ์ด์ง์์ ๊ณตํต CSS๋ฅผ ์ค๋ณต ์ํฌํธํ ๋ ๋ฐ์.
- **ํด๊ฒฐ์ฑ
**: ๊ณตํต ๋ผ์ด๋ธ๋ฌ๋ฆฌ(Bootstrap, Tailwind, jQuery ๋ฑ)๋ **๋ฐ๋์ ๋ฐ์ฝ๋ ์ดํฐ ํ
ํ๋ฆฟ์๋ง 1ํ ์ ์ธ**ํ๊ณ , ๊ฐ ํ์ด์ง์๋ ๊ณ ์ ํ ํ์ด์ง ์ ์ฉ ์คํฌ๋ฆฝํธ๋ง ๋จ๊ฒจ๋ก๋๋ค.
#### 4. ํ์ด์ง ํน์ ํ๋ฉด์์๋ง ๋ฐ์ฝ๋ ์ดํฐ ์ ์ฉ์ ๋๊ณ ์ถ์ ๋ (์์ ๋ฐ์ดํจ์ค)
- JSP ์๋จ์ ๋ฉํ ํ๊ทธ๋ฅผ ๋ช
์ํ์ฌ SiteMesh ์์ง์ ์คํต์ ์ง์ํฉ๋๋ค:
```jsp
<!-- SiteMesh 2.x ์์ ๋ฐ์ฝ๋ ์ดํฐ ๋นํ์ฑํ -->
<meta name="decorator" content="none"/>
```
์๊ฒฌ ๋ฐ ์ง๋ฌธ
0์์ง ๋ฑ๋ก๋ ์๊ฒฌ์ด ์์ต๋๋ค. ์ฒซ ๋ฒ์งธ ๋๊ธ์ ๋จ๊ฒจ๋ณด์ธ์!
๋๊ธ ์์
๋๊ธ ์ญ์