Thymeleaf: fragment 로 구획 정리

diary 프로그램 화면의 기본 레이아웃은 아래 그림과 같아.

빨간색 사각형 영역이 네비게이터, 파란색 사각형 영역이 컨텐츠, 초록색 사각형 영역이 풋터야. 현재 코딩되어 있는 thymeleaf 용 템플릿 파일 home.html, write.html, editdiary.html 에는 모두 네비게이터, 컨텐츠, 풋터에 해당되는 html 태그들이 중복적으로 들어가있어. 심지어 home.html 파일에 들어가있는 네비게이터 영역의 html 태그들과 write.html 파일에 들어있는 네비게이터 영역의 html 태그가 내용이 서로 다르지.

이번 포스트에서는 thymeleaf 의 fragment 를 이용해서 중복적인 코드를 하나로 합쳐서 적용하는 방법에 대해서 알아볼까해.

일단, 레이아웃의 각 영역에 대해서 다음과 같이 이름을 붙여볼께.

영역템플릿 파일 이름
네비게이터navigator.html
컨텐츠content.html
풋터footer.html

사실 컨텐츠 영역에 표시될 템플릿 파일은 컨텐츠에 따라서 달라질테니 content.html 파일을 별도로 만들지는 않을거야. navigator.html 과 footer.html 파일을 template/fragments 디렉토리 아래에 만들어볼께.


navigator.html 파일과 footer.html 파일은 전체 웹페이지에서 네비게이터와 풋터에 해당되는 내용만 작성하면 되지만, thymeleaf 의 fragment 로서 동작하려면 아래와 같은 기본 구조를 갖춰야 해.


<div th:fragment="fragment-navigator">

th:fragment 는 fragment 를 선언할 때 사용하는 속성인데, 속성값으로 fragment 의 이름을 설정해주면 돼. 그러면 다른 웹페이지에서 th:replace 태그를 이용해서 이 fragment 이름으로 조각을 불러서 사용할 수가 있어.

<div th:replace="/fragments/navigator.html :: fragment-navigator"></div>

navigator 조각에 대한 html 파일 경로에서 .html 은 생략해도 돼.

그럼 실제로 navigator.html 파일에 네비게이터 영역에 해당하는 html 태그들을 넣고, home.html 에서 fragment 로 불러오는 방법을 사용해볼께.

기존 home.html 파일의 네비게이터 영역의 코드는 아래 영역이야.


이 영역의 코드를 잘라내기해서 navigator.html 에 붙여넣어볼께.


sec:authorize 나 sec:authentication 에 밑줄이 그어지는데 일단 무시하고, home.html 에서 코드가 빠져나간 부분에 아래와 같이 fragment 를 불러오는 코드를 작성해볼께.


이 상태에서 프로그램을 빌드해서 실행해보면 기존과 같이 잘 동작하는걸 확인할 수 있어. 그럼 소스보기로 어떻게 html 코드가 합쳐졌는지를 살펴볼께.


div 태그 사이에 navigator.html 에 작성한 코드가 들어와있네. navigator.html 파일에서 밑줄 그어졌던 sec:authorize 와 sec:authentication 도 제대로 처리가 되었어. 조각에서는 인식불가였지만, 조각을 부르는 home.html 에 xmlns:sec=”http://www.thymeleaf.org/extras/spring-security” 가 정의되어 있기 때문에 제대로 처리된거지.

그럼 write.html 과 editdiary.html 에도 home.html 에서 fragment 를 호출하기 위해서 작성한 코드를 집어넣어볼께.



이제 write.html 과 editdiary.html 에서도 로그인된 사용자의 이메일 주소가 보이게 될거야.



이번에는 풋터 fragment 를 작성해볼께.


home.html 의 기존 풋터 영역의 코드는 아래와 같이 수정했어.

<div th:replace="/fragments/footer.html :: fragment-footer"></div>

이제 write.html 과 editdiary.html 에도 동일하게 적용하면 돼.

fragment 를 사용하니 html 페이지의 코드가 한결 간결해졌지.

이번에는 <head></head> 영역에 공통적으로 집어넣고 있는 bootstrap 관련 태그들을 fragment 화시켜볼께.


이번에는 <head> 태그를 사용해서 fragment 를 정의해봤어.

home.html 에서 이 fragment 를 사용하는 코드는 다음과 같이 하면 돼.


fragment 를 정의할 때 사용한 태그와 사용할 때 태그를 서로 맞춰주면 문제없어.

이 상태에서 프로그램을 빌드해서 실행시키면 서버로그에 fragement 사용 표현식이 Deprecated 되었다고 표시가 되네.

2024-03-08T14:47:46.758+09:00  WARN 29248 --- [nio-8080-exec-4] actStandardFragmentInsertionTagProcessor : [THYMELEAF][http-nio-8080-exec-4][diary/editdiary] Deprecated unwrapped fragment expression "/fragments/head :: fragment-head" found in template diary/editdiary, line 4, col 7. Please use the complete syntax of fragment expressions instead ("~{/fragments/head :: fragment-head}"). The old, unwrapped syntax for fragment expressions will be removed in future versions of Thymeleaf.
2024-03-08T14:47:46.823+09:00  WARN 29248 --- [nio-8080-exec-4] actStandardFragmentInsertionTagProcessor : [THYMELEAF][http-nio-8080-exec-4][diary/editdiary] Deprecated unwrapped fragment expression "/fragments/navigator :: fragment-navigator" found in template diary/editdiary, line 8, col 10. Please use the complete syntax of fragment expressions instead ("~{/fragments/navigator :: fragment-navigator}"). The old, unwrapped syntax for fragment expressions will be removed in future versions of Thymeleaf.
2024-03-08T14:47:47.165+09:00  WARN 29248 --- [nio-8080-exec-4] actStandardFragmentInsertionTagProcessor : [THYMELEAF][http-nio-8080-exec-4][diary/editdiary] Deprecated unwrapped fragment expression "/fragments/footer :: fragment-footer" found in template diary/editdiary, line 105, col 10. Please use the complete syntax of fragment expressions instead ("~{/fragments/footer :: fragment-footer}"). The old, unwrapped syntax for fragment expressions will be removed in future versions of Thymeleaf.

아래와 같은 표현식으로 사용을 하라고 하네.

<head th:replace="~{/fragments/head :: fragment-head}"></head>

또 한가지!!

fragment 파일의 경로를 쓸때 / 부터 시작했는데, 윈도우에서는 문제없이 돌아가더라도 linux 서버에 올리면 문제가 발생해.

ERROR 24-03-11 08:53:13[http-nio-8080-exec-7] [[dispatcherServlet]:175] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/diary/home.html]")] with root cause
org.thymeleaf.exceptions.TemplateInputException: Error resolving template [/fragments/navigator], template might not exist or might not be accessible by any of the configured Template Resolvers (template: "diary/home" - line 10, col 10)

fragments 디렉토리는 templates 디렉토리 아래에 존재하고 리소스 경로는 Spring Boot 에서 /templates/ 로 자동 설정되는데, /fragments 로 시작하면 /templates//fragments 로 들어가버려. 그래서 문제가 발생하는건데, /fragments 가 아니라 fragments 로 시작하면 리눅스 서버에서도 문제가 발생하지 않아.

이 경로 문제 때문에 spring boot: UI template 경로 에서도 개고생했었는데, 또 한번 개고생을 했네. 쩝.

Leave a Comment