728x90
반응형
🔥 들어가며
"TemplateInputException", "SpelEvaluationException", "Error resolving template"...
Thymeleaf를 사용하다 보면 마주치는 수많은 오류들. 빨간 에러 페이지를 보면 막막하지만, 사실 각 오류는 명확한 원인과 해결책을 가지고 있습니다.
이 글에서는 Thymeleaf 개발 중 자주 발생하는 오류들을 체계적으로 정리하고, 실제 사례와 함께 해결 방법을 제시합니다. 더 이상 Thymeleaf 오류가 두렵지 않게 될 것입니다!
📑 목차
템플릿 파싱 오류
1. TemplateInputException: Template Not Found
오류 메시지
org.thymeleaf.exceptions.TemplateInputException:
Error resolving template [users/list], template might not exist or might not be accessible by any of the configured Template Resolvers
원인
- 템플릿 파일 경로 오류
- 파일명 오타
- 잘못된 디렉토리 구조
해결 방법
파일 구조 확인
src/main/resources/
└── templates/
├── users/
│ ├── list.html ✅ 올바른 위치
│ └── form.html
└── index.html
Controller 반환값 확인
java
@Controller
public class UserController {
@GetMapping("/users")
public String listUsers(Model model) {
// ❌ 잘못된 방법
return "users/list.html"; // .html 확장자 포함
return "/users/list"; // 앞에 슬래시
// ✅ 올바른 방법
return "users/list"; // templates/ 기준 상대 경로
}
}
application.properties 설정
properties
# 기본 설정 (보통 수정 불필요)
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
# 캐시 비활성화 (개발 환경)
spring.thymeleaf.cache=false
2. 닫히지 않은 태그 오류
오류 메시지
org.xml.sax.SAXParseException: The element type "input" must be terminated by the matching end-tag "</input>"
원인
- HTML5가 아닌 XML 모드에서 self-closing 태그 미사용
- 태그 중첩 오류
해결 방법
HTML5 모드 사용
properties
# application.properties
spring.thymeleaf.mode=HTML
올바른 태그 사용
html
<!-- ❌ 잘못된 방법 (XML 모드) -->
<input type="text" name="username">
<br>
<img src="logo.png">
<!-- ✅ 올바른 방법 1: Self-closing -->
<input type="text" name="username" />
<br />
<img src="logo.png" />
<!-- ✅ 올바른 방법 2: HTML5 모드 사용 -->
<input type="text" name="username">
<br>
<img src="logo.png">
3. 잘못된 속성 구문
오류 메시지
org.thymeleaf.exceptions.TemplateProcessingException:
Could not parse as expression: "${user.name"
원인
- 따옴표 누락 또는 불일치
- 중괄호 누락
해결 방법
html
<!-- ❌ 잘못된 방법 -->
<p th:text=${user.name}>Name</p> <!-- 따옴표 누락 -->
<p th:text="${user.name>Name</p> <!-- 닫는 중괄호 누락 -->
<p th:text='${user.name}'>Name</p> <!-- 작은따옴표 사용 -->
<!-- ✅ 올바른 방법 -->
<p th:text="${user.name}">Name</p>
<!-- 따옴표 안에 따옴표가 필요한 경우 -->
<p th:text="|User's name: ${user.name}|">Name</p>
표현식 평가 오류
1. SpelEvaluationException: Null Pointer
오류 메시지
org.springframework.expression.spel.SpelEvaluationException:
EL1007E: Property or field 'name' cannot be found on null
원인
- null 객체의 속성 접근
- Controller에서 모델 추가 누락
해결 방법
안전한 네비게이션 연산자 사용
html
<!-- ❌ 위험한 방법 -->
<p th:text="${user.name}">Name</p>
<!-- ✅ 안전한 방법 1: 조건부 렌더링 -->
<p th:if="${user != null}" th:text="${user.name}">Name</p>
<!-- ✅ 안전한 방법 2: 안전 연산자 -->
<p th:text="${user?.name}">Name</p>
<!-- ✅ 안전한 방법 3: 기본값 제공 -->
<p th:text="${user?.name ?: 'Anonymous'}">Name</p>
<!-- ✅ 안전한 방법 4: Elvis 연산자 -->
<p th:text="${user?.name} ?: 'No name'">Name</p>
중첩 객체 안전 접근
html
<!-- ❌ 위험 -->
<p th:text="${user.address.city}">City</p>
<!-- ✅ 안전 -->
<p th:text="${user?.address?.city ?: 'Unknown'}">City</p>
<!-- 더 복잡한 경우 -->
<div th:object="${user}">
<p th:text="*{address?.city}">City</p>
<p th:text="*{address?.street}">Street</p>
</div>
2. 타입 변환 오류
오류 메시지
org.springframework.expression.spel.SpelEvaluationException:
EL1030E: The operator 'ADD' is not supported between objects of type 'java.lang.String' and 'java.lang.Integer'
원인
- 잘못된 타입 간 연산
- 문자열과 숫자 혼용
해결 방법
html
<!-- ❌ 잘못된 방법 -->
<p th:text="${'Total: ' + order.amount}">Total</p>
<!-- ✅ 올바른 방법 1: 문자열 연결 -->
<p th:text="|Total: ${order.amount}|">Total</p>
<!-- ✅ 올바른 방법 2: 연결 연산자 -->
<p th:text="${'Total: ' + #numbers.formatDecimal(order.amount, 1, 2)}">Total</p>
<!-- 숫자 연산 -->
<p th:text="${order.quantity * order.price}">Total Price</p>
<!-- 타입 변환 -->
<p th:text="${#strings.toString(order.quantity)}">Quantity</p>
3. 컬렉션 접근 오류
오류 메시지
org.springframework.expression.spel.SpelEvaluationException:
EL1025E: The collection has '0' elements, index '0' is invalid
원인
- 빈 컬렉션 접근
- 잘못된 인덱스
해결 방법
html
<!-- ❌ 위험한 방법 -->
<p th:text="${users[0].name}">First User</p>
<!-- ✅ 안전한 방법 1: 크기 확인 -->
<p th:if="${!users.isEmpty()}" th:text="${users[0].name}">First User</p>
<!-- ✅ 안전한 방법 2: 안전 연산자 -->
<p th:text="${users[0]?.name ?: 'No users'}">First User</p>
<!-- ✅ 안전한 방법 3: 조건부 표현 -->
<p th:text="${#lists.size(users) > 0 ? users[0].name : 'Empty'}">First User</p>
<!-- 반복문 사용 -->
<div th:each="user, iterStat : ${users}">
<p th:if="${iterStat.first}" th:text="${user.name}">First User</p>
</div>
레이아웃 관련 오류
1. Fragment Expression 오류
오류 메시지
org.thymeleaf.exceptions.TemplateInputException:
Error resolving fragment: "${content}": template or fragment could not be resolved
원인
- Thymeleaf Layout Dialect 의존성 누락
- 잘못된 fragment 구문
해결 방법
의존성 추가
xml
<!-- pom.xml -->
<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
<version>3.3.0</version>
</dependency>
레이아웃 파일 (layout/default.html)
html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<title layout:title-pattern="$CONTENT_TITLE - $LAYOUT_TITLE">My Site</title>
</head>
<body>
<header>
<nav><!-- Navigation --></nav>
</header>
<!-- 콘텐츠가 삽입될 위치 -->
<main layout:fragment="content">
Default content
</main>
<footer>
<p>© 2024 My Site</p>
</footer>
</body>
</html>
페이지 파일 (users/list.html)
html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/default}">
<head>
<title>User List</title>
</head>
<body>
<div layout:fragment="content">
<h1>Users</h1>
<!-- 페이지 콘텐츠 -->
</div>
</body>
</html>
2. 순환 참조 오류
오류 메시지
org.thymeleaf.exceptions.TemplateProcessingException:
Circular template reference detected: "fragments/header" -> "layout/main" -> "fragments/header"
원인
- 템플릿 간 순환 참조
- 잘못된 include 구조
해결 방법
html
<!-- ❌ 순환 참조 발생 -->
<!-- layout/main.html -->
<div th:replace="~{fragments/header :: header}"></div>
<!-- fragments/header.html -->
<div th:fragment="header" th:replace="~{layout/main :: content}">
<!-- 순환 참조! -->
</div>
<!-- ✅ 올바른 구조 -->
<!-- layout/main.html -->
<body>
<div th:replace="~{fragments/header :: header}"></div>
<div layout:fragment="content"></div>
<div th:replace="~{fragments/footer :: footer}"></div>
</body>
<!-- fragments/header.html -->
<div th:fragment="header">
<header>
<!-- 헤더 내용만 포함 -->
</header>
</div>
Fragment 오류
1. Fragment Not Found
오류 메시지
org.thymeleaf.exceptions.TemplateProcessingException:
Fragment "header" was not found in template "fragments/common"
원인
- Fragment 이름 오타
- Fragment 정의 누락
해결 방법
Fragment 정의 (fragments/common.html)
html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<!-- Fragment 정의 -->
<div th:fragment="header">
<header>
<h1>My Website</h1>
</header>
</div>
<!-- 파라미터를 받는 Fragment -->
<div th:fragment="alert(type, message)">
<div th:class="|alert alert-${type}|" th:text="${message}">
Alert message
</div>
</div>
<!-- 동적 Fragment -->
<div th:fragment="dynamic(content)">
<div class="container" th:utext="${content}">
Dynamic content
</div>
</div>
</body>
</html>
Fragment 사용
html
<!-- ✅ 올바른 사용법 -->
<!-- replace: 전체 태그 교체 -->
<div th:replace="~{fragments/common :: header}"></div>
<!-- insert: 태그 내부에 삽입 -->
<div th:insert="~{fragments/common :: header}"></div>
<!-- include: 내용만 포함 (deprecated) -->
<div th:include="~{fragments/common :: header}"></div>
<!-- 파라미터 전달 -->
<div th:replace="~{fragments/common :: alert('success', 'Operation completed!')}"></div>
<!-- 조건부 Fragment -->
<div th:replace="${user.isAdmin()} ? ~{fragments/admin :: menu} : ~{fragments/user :: menu}"></div>
2. Fragment 표현식 오류
오류 메시지
org.thymeleaf.exceptions.TemplateProcessingException:
Exception evaluating SpringEL expression: "fragments/common :: ${fragmentName}"
원인
- 동적 Fragment 이름 사용 시 구문 오류
해결 방법
html
<!-- ❌ 잘못된 방법 -->
<div th:replace="~{fragments/common :: ${fragmentName}}"></div>
<!-- ✅ 올바른 방법 1: 전처리 -->
<div th:replace="~{fragments/common :: __${fragmentName}__}"></div>
<!-- ✅ 올바른 방법 2: 조건부 선택 -->
<div th:replace="${condition} ? ~{fragments/common :: fragment1} : ~{fragments/common :: fragment2}"></div>
<!-- ✅ 올바른 방법 3: Fragment 매개변수 -->
<div th:replace="~{fragments/common :: dynamic(${content})}"></div>
반복문과 조건문 오류
1. th:each 오류
오류 메시지
org.thymeleaf.exceptions.TemplateProcessingException:
Exception evaluating SpringEL expression: "user.name" (template: "users/list" - line 10, col 15)
원인
- 반복 변수 스코프 오류
- null 컬렉션 반복
해결 방법
html
<!-- ❌ 잘못된 방법 -->
<tr th:each="users : ${user}"> <!-- 변수명 혼동 -->
<td th:text="${users.name}"></td>
</tr>
<!-- ✅ 올바른 방법 -->
<tr th:each="user : ${users}">
<td th:text="${user.name}"></td>
</tr>
<!-- null 안전 처리 -->
<tbody th:if="${users != null}">
<tr th:each="user : ${users}">
<td th:text="${user.name}"></td>
</tr>
</tbody>
<tbody th:unless="${users != null}">
<tr>
<td colspan="3">No users found</td>
</tr>
</tbody>
<!-- 상태 변수 사용 -->
<tr th:each="user, iterStat : ${users}"
th:class="${iterStat.odd} ? 'odd' : 'even'">
<td th:text="${iterStat.count}">1</td>
<td th:text="${user.name}">Name</td>
<td th:text="${iterStat.first} ? 'FIRST' : ''"></td>
<td th:text="${iterStat.last} ? 'LAST' : ''"></td>
</tr>
2. 중첩 반복문 오류
원인
- 변수 이름 충돌
- 스코프 혼동
해결 방법
html
<!-- ❌ 문제가 될 수 있는 방법 -->
<div th:each="category : ${categories}">
<div th:each="item : ${items}">
<!-- 어느 스코프의 item인지 불명확 -->
</div>
</div>
<!-- ✅ 명확한 방법 -->
<div th:each="category : ${categories}">
<h3 th:text="${category.name}">Category</h3>
<ul>
<li th:each="product : ${category.products}">
<span th:text="${product.name}">Product</span>
<span th:text="${category.name}">Still accessible</span>
</li>
</ul>
</div>
<!-- 인덱스 활용 -->
<table>
<tr th:each="row, rowStat : ${matrix}">
<td th:each="cell, colStat : ${row}"
th:text="|[${rowStat.index},${colStat.index}] = ${cell}|">
Cell
</td>
</tr>
</table>
3. 조건문 오류
오류 메시지
org.thymeleaf.exceptions.TemplateProcessingException:
Cannot execute subtraction: operands are "null" and "1"
원인
- 조건문에서 null 처리 누락
- 잘못된 비교 연산
해결 방법
html
<!-- ❌ 위험한 조건 -->
<div th:if="${user.age > 18}">Adult</div>
<!-- ✅ 안전한 조건 1 -->
<div th:if="${user != null and user.age != null and user.age > 18}">Adult</div>
<!-- ✅ 안전한 조건 2 -->
<div th:if="${user?.age != null and user.age > 18}">Adult</div>
<!-- 복잡한 조건 -->
<div th:switch="${user?.status}">
<p th:case="'ACTIVE'" class="text-success">Active</p>
<p th:case="'INACTIVE'" class="text-warning">Inactive</p>
<p th:case="'BANNED'" class="text-danger">Banned</p>
<p th:case="*" class="text-muted">Unknown</p>
</div>
<!-- th:unless 사용 -->
<div th:unless="${#lists.isEmpty(errors)}">
<ul>
<li th:each="error : ${errors}" th:text="${error}"></li>
</ul>
</div>
날짜/시간 처리 오류
1. 날짜 포맷 오류
오류 메시지
org.thymeleaf.exceptions.TemplateProcessingException:
Exception evaluating SpringEL expression: "#dates.format(user.createdAt, 'yyyy-MM-dd')"
원인
- null 날짜 값
- 잘못된 날짜 타입
- Java 8 시간 API 사용
해결 방법
html
<!-- ❌ 잘못된 방법 (Java 8+ LocalDateTime에는 #dates 사용 불가) -->
<span th:text="${#dates.format(user.createdAt, 'yyyy-MM-dd')}"></span>
<!-- ✅ Java 8 시간 API 사용 -->
<!-- LocalDateTime -->
<span th:text="${#temporals.format(user.createdAt, 'yyyy-MM-dd HH:mm:ss')}"></span>
<!-- LocalDate -->
<span th:text="${#temporals.format(user.birthDate, 'yyyy-MM-dd')}"></span>
<!-- null 안전 처리 -->
<span th:text="${user.createdAt != null ? #temporals.format(user.createdAt, 'yyyy-MM-dd') : 'N/A'}"></span>
<!-- 다양한 포맷 -->
<span th:text="${#temporals.format(user.createdAt, 'EEEE, MMMM dd, yyyy')}"></span>
<span th:text="${#temporals.format(user.createdAt, 'dd/MM/yyyy HH:mm')}"></span>
<!-- 상대 시간 (커스텀 유틸리티 필요) -->
<span th:text="${@dateUtils.getRelativeTime(user.createdAt)}"></span>
2. 타임존 오류
원인
- 서버와 클라이언트 타임존 불일치
- 타임존 변환 누락
해결 방법
java
// 커스텀 유틸리티 빈
@Component("dateUtils")
public class DateUtils {
public String formatWithTimezone(LocalDateTime dateTime, String timezone) {
if (dateTime == null) return "";
ZonedDateTime zdt = dateTime.atZone(ZoneId.systemDefault())
.withZoneSameInstant(ZoneId.of(timezone));
return DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z")
.format(zdt);
}
public String getRelativeTime(LocalDateTime dateTime) {
if (dateTime == null) return "";
LocalDateTime now = LocalDateTime.now();
long minutes = ChronoUnit.MINUTES.between(dateTime, now);
if (minutes < 1) return "방금 전";
if (minutes < 60) return minutes + "분 전";
if (minutes < 1440) return (minutes / 60) + "시간 전";
return (minutes / 1440) + "일 전";
}
}
html
<!-- 사용 예 -->
<span th:text="${@dateUtils.formatWithTimezone(user.createdAt, 'Asia/Seoul')}"></span>
<span th:text="${@dateUtils.getRelativeTime(post.createdAt)}"></span>
보안 관련 오류
1. Spring Security 통합 오류
오류 메시지
org.thymeleaf.exceptions.TemplateProcessingException:
Exception evaluating SpringEL expression: "#authorization.expression('hasRole(''ROLE_ADMIN'')')"
원인
- Spring Security Dialect 누락
- 잘못된 권한 표현식
해결 방법
의존성 추가
xml
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
</dependency>
올바른 사용법
html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<body>
<!-- 인증 확인 -->
<div sec:authorize="isAuthenticated()">
Welcome, <span sec:authentication="name">User</span>!
</div>
<!-- 권한 확인 -->
<div sec:authorize="hasRole('ADMIN')">
<a href="/admin">Admin Panel</a>
</div>
<!-- 복잡한 권한 표현식 -->
<div sec:authorize="hasRole('USER') and hasAuthority('WRITE_PRIVILEGE')">
<button>Create Post</button>
</div>
<!-- 인증 정보 접근 -->
<p>Username: <span sec:authentication="principal.username"></span></p>
<p>Authorities: <span sec:authentication="principal.authorities"></span></p>
<!-- CSRF 토큰 -->
<meta name="_csrf" th:content="${_csrf.token}"/>
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
</body>
</html>
2. XSS 방지 오류
원인
- 안전하지 않은 HTML 출력
- 잘못된 이스케이핑
해결 방법
html
<!-- ❌ 위험: XSS 취약점 -->
<div th:utext="${userInput}"></div>
<!-- ✅ 안전: 자동 이스케이핑 -->
<div th:text="${userInput}"></div>
<!-- HTML이 필요한 경우: 서버에서 검증 -->
<div th:utext="${@htmlSanitizer.sanitize(userContent)}"></div>
<!-- JavaScript 내 데이터 -->
<script th:inline="javascript">
/* ✅ 자동 이스케이핑 */
var username = [[${user.name}]];
/* ❌ 위험: 이스케이핑 없음 */
var data = [(${userData})];
</script>
<!-- URL 파라미터 -->
<a th:href="@{/users(name=${userName})}">Link</a>
디버깅 전략
1. 디버그 모드 활성화
properties
# application.properties
spring.thymeleaf.cache=false
logging.level.org.thymeleaf=DEBUG
logging.level.org.thymeleaf.TemplateEngine.CONFIG=TRACE
2. 유틸리티 객체 활용
html
<!-- 객체 정보 확인 -->
<div th:text="${#objects.nullSafe(user, 'NULL')}"></div>
<div th:text="${user.class.name}"></div>
<!-- 컬렉션 정보 -->
<p>Size: <span th:text="${#lists.size(users)}"></span></p>
<p>Empty: <span th:text="${#lists.isEmpty(users)}"></span></p>
<!-- 디버그 정보 출력 -->
<div th:if="${@environment.acceptsProfiles('dev')}">
<h3>Debug Info</h3>
<pre th:text="${#objects.toString(user)}"></pre>
<pre th:text="${#vars}"></pre>
</div>
<!-- 조건부 디버깅 -->
<th:block th:if="${@environment.getProperty('debug.enabled') == 'true'}">
<div class="debug-panel">
<h4>Model Attributes</h4>
<dl th:each="attr : ${#vars.getVariableNames()}">
<dt th:text="${attr}">name</dt>
<dd th:text="${#vars.getVariable(attr)}">value</dd>
</dl>
</div>
</th:block>
3. 커스텀 에러 페이지
html
<!-- templates/error/404.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Page Not Found</title>
</head>
<body>
<h1>404 - Page Not Found</h1>
<p>The requested page could not be found.</p>
<div th:if="${@environment.acceptsProfiles('dev')}">
<h3>Debug Information</h3>
<p>Timestamp: <span th:text="${timestamp}"></span></p>
<p>Path: <span th:text="${path}"></span></p>
<p>Error: <span th:text="${error}"></span></p>
<p>Message: <span th:text="${message}"></span></p>
</div>
</body>
</html>
4. 커스텀 Dialect 생성
java
@Component
public class CustomDialect extends AbstractProcessorDialect {
public CustomDialect() {
super("Custom Dialect", "custom", 1000);
}
@Override
public Set<IProcessor> getProcessors(String dialectPrefix) {
Set<IProcessor> processors = new HashSet<>();
processors.add(new DebugAttributeTagProcessor(dialectPrefix));
return processors;
}
}
public class DebugAttributeTagProcessor extends AbstractAttributeTagProcessor {
private static final String ATTR_NAME = "debug";
public DebugAttributeTagProcessor(String dialectPrefix) {
super(
TemplateMode.HTML,
dialectPrefix,
null,
false,
ATTR_NAME,
true,
1000,
true
);
}
@Override
protected void doProcess(
ITemplateContext context,
IProcessableElementTag tag,
AttributeName attributeName,
String attributeValue,
IElementTagStructureHandler structureHandler) {
if ("true".equals(attributeValue)) {
String debug = String.format(
"<!-- Debug: %s = %s -->",
tag.getElementCompleteName(),
context.getVariable(tag.getElementCompleteName())
);
structureHandler.insertImmediatelyBefore(debug, false);
}
}
}
사용:
html
<div custom:debug="true" th:text="${user.name}">Name</div>
성능 최적화 팁
1. Fragment 캐싱
html
<!-- 캐시 가능한 Fragment -->
<div th:fragment="expensive-calculation" th:cache="true" th:cache-ttl="3600">
<!-- 비용이 큰 연산 결과 -->
</div>
2. 지연 로딩
html
<!-- 조건부 Fragment 로딩 -->
<div th:if="${showDetails}">
<div th:replace="~{fragments/user-details :: details(${user})}"></div>
</div>
<!-- AJAX 로딩 준비 -->
<div id="user-details" data-user-id="${user.id}" data-load-on-demand="true">
<button onclick="loadUserDetails()">Load Details</button>
</div>
3. 프리컴파일
java
@Configuration
public class ThymeleafConfig {
@Bean
public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver);
engine.setEnableSpringELCompiler(true); // SpEL 컴파일러 활성화
return engine;
}
}
마무리
Thymeleaf 오류는 처음에는 당황스럽지만, 패턴을 이해하면 빠르게 해결할 수 있습니다. 이 가이드를 참고하여 더 안정적이고 유지보수하기 쉬운 템플릿을 작성하시기 바랍니다.
핵심 체크리스트
- ✅ null 안전 연산자 사용 (?.)
- ✅ Fragment 경로 정확히 지정
- ✅ 타입 안전성 확인
- ✅ 보안 취약점 방지 (th:text 사용)
- ✅ 적절한 오류 처리
- ✅ 개발 환경에서 캐시 비활성화
디버깅 순서
- 브라우저 개발자 도구 확인
- 서버 로그 확인
- Thymeleaf 디버그 모드 활성화
- 단순한 템플릿으로 테스트
- 단계별 문제 격리
참고 자료
태그: #Thymeleaf #SpringBoot #ErrorHandling #WebDevelopment #Debugging
728x90
반응형
'유용한 컴공 테크닉' 카테고리의 다른 글
Claude Code 완벽 가이드: AI와 함께하는 차세대 코딩 경험 (5) | 2025.07.23 |
---|---|
Spring Boot 오류 완벽 가이드: 에러 메시지로 배우는 트러블슈팅 (1) | 2025.07.21 |
Docker로 Spring Boot 애플리케이션 배포하기: 개발부터 운영까지 완벽 가이드 (3) | 2025.07.21 |
Spring Boot와 MariaDB/MySQL 연결하기: H2에서 실전 DB로 레벨업 (0) | 2025.07.21 |
Spring Boot + Thymeleaf로 웹 페이지 만들기: Bootstrap을 곁들인 CRUD 완성하기 (1) | 2025.07.20 |