스프링 인프런 김영한
17. 템플릿 조각
웹 페이지를 개발할 때는 공통 영역이 많이 있다.
예를 들어서 상단 영역이나 하단 영역 좌측 카테고리 등등 여러 페이지에서 함께 사용하는 영역들이 있다.
이런 부분을 코드를 복사해서 사용한다면 변경시 여러 페이지를 다 수정해야 하므로 상당히 비효율 적이다.
타임리프는 이런 문제를 해결하기 위해 템플릿 조각과 레이아웃 기능을 지원한다.
주의! 템플릿 조각과 레이아웃 부분은 직접 실행해보아야 이해된다.
17.1 footer
먼저 footer영역을 만들것이다.
이건 직접적으로 불린다기보다는 다른파일에서 불러서쓰는 조각이다.
th:fragment="copy"으로 이름을 줘서 다른 파일에서 불러서 사용할 수 있다.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<footer th:fragment="copy"> 푸터 자리 입니다. </footer>
<footer th:fragment="copyParam (param1, param2)">
<p>파라미터 자리 입니다.</p>
<p th:text="${param1}"></p>
<p th:text="${param2}"></p>
</footer>
</body>
</html>17.2 fragmentMain
th:insert ~ {경로 :: 이름}으로 불러온다.
<h1>부분 포함</h1>
<h2>부분 포함 insert</h2>
<div th:insert="~{template/fragment/footer :: copy}"></div>
<h2>부분 포함 replace</h2>
<div th:replace="~{template/fragment/footer :: copy}"></div>
<h2>부분 포함 단순 표현식</h2>
<div th:replace="template/fragment/footer :: copy"></div>
<h1>파라미터 사용</h1>
<div th:replace="~{template/fragment/footer :: copyParam ('데이터1', '데이터2')}"></div>17.2.1 부분포함 th:insert=
th:insert 를 사용하면 현재 태그( div ) 내부에 추가한다.
<div th:insert="~{template/fragment/footer :: copy}"></div><h2>부분 포함 insert</h2>
<div>
<footer>
푸터 자리 입니다.
</footer>
</div>17.2.2 부분 포함 replace
div태그를 대신해서 들어가게 된다. 말그대로 교체해버리고 들어간다.
<div th:replace="~{template/fragment/footer :: copy}"></div><h2>부분 포함 replace</h2>
<footer>
푸터 자리 입니다.
</footer>이런식으로 원하는 조각들이 분리되어있는 것을 붙일 수 있다.
17.2.3 부분 포함 단순 표현식
경로만 있는 단순하게 있는것은 ~을 빼도된다.
~{...} 를 사용하는 것이 원칙이지만 템플릿 조각을 사용하는 코드가 단순하면 이 부분을 생략할 수 있다
<div th:replace="template/fragment/footer :: copy"></div><h2>부분 포함 단순 표현식</h2>
<footer>
푸터 자리 입니다.
</footer>17.2.4 파라미터 사용
파라미터를 전달해서 동적으로 렌더링할 수도 있다.
<div th:replace="~{template/fragment/footer :: copyParam ('데이터1', '데이터2')}"></div><h1>파라미터 사용</h1>
<footer>
<p>파라미터 자리 입니다.</p>
<p>데이터1</p>
<p>데이터2</p>
</footer>구조화된화면에 잘 나누어서 넣기만 하면된다.
18. 템플릿 레이아웃1
이전에는 일부 코드 조각을 가지고와서 사용했다면
이번에는 개념을 더 확장해서 코드 조각을 레이아웃에 넘겨서 사용하는 방법에 대해서 알아보자.
예를 들어서 <head> 에 공통으로 사용하는 css , javascript 같은 정보들이 있는데
이러한 공통 정보들을 한 곳에 모아두고 공통으로 사용하지만 각 페이지마다 필요한 정보를 더 추가해서 사용하고 싶다면 다음과 같이 사용하면 된다.
18.1 base.html
<html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="common_header(title,links)">
<title th:replace="${title}">레이아웃 타이틀</title>
<!-- 공통 -->
<link rel="stylesheet" type="text/css" media="all" th:href="@{/css/awesomeapp.css}">
<link rel="shortcut icon" th:href="@{/images/favicon.ico}">
<script type="text/javascript" th:src="@{/sh/scripts/codebase.js}"></script>
<!-- 추가 -->
<th:block th:replace="${links}" />
</head>18.2 layoutMain.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="template/layout/base :: common_header(~{::title},~{::link})">
<title>메인 타이틀</title>
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
<link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}">
</head>
<body>메인 컨텐츠
</body>
</html>18.3 결과
base.html 는 전체서버에서 공통으로 사용하는 부분이라고 보면된다.
base를 그대로 사용하더라도 title을 다 다르게 사용한다거나 추가 링크를 넣고 싶은 경우가 있다.
이것을 바꿔치기 하는 것이다.
base에서 common_header라는 이름으로 파라미터 title,links을 받을수있게 준비해둔다.
<head th:fragment="common_header(title,links)">이걸활용해서
<head th:replace="template/layout/base :: common_header(~{::title},~{::link})">레이아웃 base를 불러오는데 title넘기고 link와 관련된것을 넘기게 된다.
이 링크들은 넣은 값들로 렌더링된다.
이문법을 사용하면 ~{::title}와 ~{::link})에 코드자체를 넣어버리게 되는 것이다.
이런식으로 레이아웃에 내가 원하는 값을 넣을 수 있게 된다.
<!DOCTYPE html>
<html>
<head>
<title>메인 타이틀</title>
<!-- 공통 -->
<link rel="stylesheet" type="text/css" media="all" href="/css/awesomeapp.css">
<link rel="shortcut icon" href="/images/favicon.ico">
<script type="text/javascript" src="/sh/scripts/codebase.js"></script>
<!-- 추가 -->
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/themes/smoothness/jquery-ui.css">
</head>
<body>메인 컨텐츠
</body>
</html>정리
common_header({::title},{::link}) 이 부분이 핵심이다.
::title 은 현재 페이지의 title 태그들을 전달한다.
::link 는 현재 페이지의 link 태그들을 전달한다.
결과를 보면 메인 타이틀이 전달한 부분으로 교체되었다.
공통 부분은 그대로 유지되고 추가 부분에 전달한 <link> 들이 포함된 것을 확인할 수 있다.
이 방식은 사실 앞서 배운 코드 조각을 조금 더 적극적으로 사용하는 방식이다.
쉽게 이야기해서 레이아웃 개념을 두고 그 레이아웃에 필요한 코드 조각을 전달해서 완성하는 것으로 이해하면 된다.
<head th:replace="template/layout/bb :: common_header(~{::title},~{::link})">이부분에서 ~{::}이부분에 변수명같은게 들어가나 싶엇는데 태그 이름이 들어가는 듯하다.
<head th:replace="template/layout/base :: common_header(~{::title},~{::link},~{::input})">로 넣어보앗을때 input태그가 알아서 잘들어가는것을 볼 수 있다.
그런데 base쪽에서 인식을 어떻게하는지는 잘 구분이 안간다.
gpt를 통해 검색해보니
<head th:fragment="common_header(title,links)">이건 그냥 메소드 느낌이라고 보고 아규먼트들이라고 생각하면된다고 한다.
그래서 실제 호출하는 것은 이런것이다.
<title th:replace="${title}">레이아웃 타이틀</title>
<th:block th:replace="${links}" />우리가 메소드를 사용할때 아규먼트와 넣는 값의 이름이 다르더라도 작동하듯이
그래서 매핑할때의 이름과 호출할때의 이름이 달라도 순서대로 알아서 넣어주기 때문에 상관이 없는 것이다.
19. 템플릿 레이아웃2
앞서 이야기한 개념을 <head> 정도에만 적용하는게 아니라 <html> 전체에 적용할 수도 있다
100페이지가 잇다고할때 모양이 다똑같고 footer에는 fotter가 들어가고
컨텐트 부분만 다 다르다고 생각해보자.
<!DOCTYPE html>
<html th:fragment="layout (title, content)" xmlns:th="http://www.thymeleaf.org">
<head>
<title th:replace="${title}">레이아웃 타이틀</title>
</head>
<body>
<h1>레이아웃 H1</h1>
<div th:replace="${content}">
<p>레이아웃 컨텐츠</p>
</div>
<footer> 레이아웃 푸터 </footer>
</body>
</html>레이아웃 파일 자체를 바꿔버리고 싶을때 사용하게 된다.
<!DOCTYPE html>
<html th:replace="~{template/layoutExtend/layoutFile :: layout(~{::title}, ~{::section})}" xmlns:th="http://www.thymeleaf.org">
<head>
<title>메인 페이지 타이틀</title>
</head>
<body>
<section>
<p>메인 페이지 컨텐츠</p>
<div>메인 페이지 포함 내용</div>
</section>
</body>
</html>title과 section을 넘겨서 그 부분들이 바뀌게 된다.
레이아웃틀을 유지하고 컨텐트 부분만 바꾼다.
<!DOCTYPE html>
<html>
<head>
<title>메인 페이지 타이틀</title>
</head>
<body>
<h1>레이아웃 H1</h1>
<section>
<p>메인 페이지 컨텐츠</p>
<div>메인 페이지 포함 내용</div>
</section>
<footer> 레이아웃 푸터 </footer>
</body>
</html>정리
layoutFile.html 을 보면 기본 레이아웃을 가지고 있는데
<html> 에 th:fragment 속성이 정의되어 있다.
이 레이아웃 파일을 기본으로 하고 여기에 필요한 내용을 전달해서 부분부분 변경하는 것으로 이해하면 된다.
layoutExtendMain.html 는 현재 페이지인데 <html> 자체를 th:replace 를 사용해서 변경하는 것을 확인할 수 있다.
결국 layoutFile.html 에 필요한 내용을 전달하면서 <html> 자체를 layoutFile.html 로 변경한다
조각조각 넣는게 중복이 많고 어지러워진다. 그래도 페이지가 별로없을때는 단순하게 이렇게 사용하면된다.
헷갈린다!!
장점은 레이아웃만 바꿔도 전체 정적페이지가 바뀔수있다.
단점은 체계적으로 관리를 해야한다.
타임리프 - 스프링 통합과 폼
20. 프로젝트 설정
스프링 MVC 1편에서 마지막에 완성했던 상품 관리 프로젝트를 떠올려보자.
지금부터 이 프로젝트에 스프링이 지원하는 다양한 기능을 붙여가면서 스프링 MVC를 깊이있게 학습해보자.
입력폼에 대해서 정리해보려고한다.
21. 타임리프 스프링 통합
타임리프는 크게 2가지 메뉴얼을 제공한다.
기본 메뉴얼: https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html
스프링 통합 메뉴얼: https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html
타임리프는 스프링 없이도 동작하지만 스프링과 통합을 위한 다양한 기능을 편리하게 제공한다.
그리고 이런 부분은 스프링으로 백엔드를 개발하는 개발자 입장에서 타임리프를 선택하는 하나의 이유가 된다.
스프링 통합으로 추가되는 기능들
1.스프링의 SpringEL 문법 통합
2.${@myBean.doSomething()} 처럼 스프링 빈 호출 지원
3.편리한 폼 관리를 위한 추가 속성
th:object (기능 강화, 폼 커맨드 객체 선택)
th:field , th:errors , th:errorclass
4.폼 컴포넌트 기능
checkbox, radio button, List 등을 편리하게 사용할 수 있는 기능 지원
5.스프링의 메시지, 국제화 기능의 편리한 통합
6.스프링의 검증, 오류 처리 통합
7.스프링의 변환 서비스 통합(ConversionService)
설정 방법
타임리프 템플릿 엔진을 스프링 빈에 등록하고 타임리프용 뷰 리졸버를 스프링 빈으로 등록하는 방법
https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html#the-springstandarddialect
https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html#views-and-viewresolvers스프링 부트는 이런 부분을 모두 자동화 해준다.
build.gradle 에 다음 한줄을 넣어주면 Gradle은 타임리프와 관련된 라이브러리를 다운로드 받고
스프링 부트는 앞서 설명한 타임리프와 관련된 설정용 스프링 빈을 자동으로 등록해준다
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'원래대로라면 bean에 타임리프를 등록하고 view리졸버를 등록해주고 등등을 해줘야한다.
타임리프 관련 설정을 변경하고 싶으면 다음을 참고해서 application.properties 에 추가하면 된다.
스프링 부트가 제공하는 타임리프 설정 thymeleaf 검색 필요하다.
22. 입력 폼 처리
지금부터 타임리프가 제공하는 입력 폼 기능을 적용해서 기존 프로젝트의 폼 코드를 타임리프가 지원하는 기능을 사용해서 효율적으로 개선해보자.
th:object : 커맨드 객체를 지정한다.
*{...} : 선택 변수 식이라고 한다. th:object 에서 선택한 객체에 접근한다.
th:field
HTML 태그의 id , name , value 속성을 자동으로 처리해준다.
렌더링 전
<input type="text" th:field="*{itemName}" />렌더링 후
<input type="text" id="itemName" name="itemName" th:value="*{itemName}" />등록 폼
th:object 를 적용하려면 먼저 해당 오브젝트 정보를 넘겨주어야 한다.
등록 폼이기 때문에 데이터가 비어있는 빈 오브젝트를 만들어서 뷰에 전달해보자.
2023.06.10
레이아웃부분들이 좀 헷갈리는 것이 있다.
순서대로 들어간다는 느낌을 이해하는게 좋을 것같다.
'기초단계 > SPRING' 카테고리의 다른 글
| 2023.06.24 Spring (0) | 2023.06.26 |
|---|---|
| 2023.06.22 Spring (0) | 2023.06.26 |
| 2023.06.09 Spring (0) | 2023.06.22 |
| 2023.06.07 Spring (0) | 2023.06.22 |
| 2023.06.05 Spring (0) | 2023.06.06 |