Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 자바의 정석 기초편 ch4
- 스프링 db2 - 데이터 접근 기술
- 자바의 정석 기초편 ch13
- 자바의 정석 기초편 ch2
- 스프링 mvc2 - 검증
- 2024 정보처리기사 시나공 필기
- 스프링 mvc2 - 타임리프
- 자바의 정석 기초편 ch7
- 자바의 정석 기초편 ch1
- 타임리프 - 기본기능
- 스프링 입문(무료)
- 자바의 정석 기초편 ch8
- @Aspect
- 자바의 정석 기초편 ch9
- 스프링 고급 - 스프링 aop
- jpa - 객체지향 쿼리 언어
- 자바의 정석 기초편 ch6
- 자바의 정석 기초편 ch14
- 2024 정보처리기사 수제비 실기
- 게시글 목록 api
- 자바의 정석 기초편 ch5
- 스프링 mvc2 - 로그인 처리
- 자바의 정석 기초편 ch12
- jpa 활용2 - api 개발 고급
- 코드로 시작하는 자바 첫걸음
- 자바의 정석 기초편 ch3
- 스프링 db1 - 스프링과 문제 해결
- 스프링 mvc1 - 스프링 mvc
- 자바의 정석 기초편 ch11
- 스프링 mvc1 - 서블릿
Archives
- Today
- Total
나구리의 개발공부기록
타임리프 - 기본기능, 주석, 블록, 자바스크립트 인라인, 템플릿 조각, 템플릿 레이아웃 본문
인프런 - 스프링 완전정복 코스 로드맵/스프링 MVC 2편 - 백엔드 웹 개발 핵심 기술
타임리프 - 기본기능, 주석, 블록, 자바스크립트 인라인, 템플릿 조각, 템플릿 레이아웃
소소한나구리 2024. 5. 19. 17:30 출처 : 인프런 - 스프링 MVC 2편 - 백엔드 웹 개발 핵심 기술 (유료) / 김영한님
유료 강의이므로 정리에 초점을 두고 코드는 일부만 인용
1. 주석
1) 주석
(1) BasicController - comments() 추가
@GetMapping("/comments")
public String comments(Model model) {
model.addAttribute("data", "Spring!");
return "basic/comments";
}
(2-1) comments.html
<!DOCTYPE html>
<!-- 타임리프 선언 및 head태그 -->
<body>
<h1>예시</h1>
<span th:text="${data}">html data</span>
<h1>1. 표준 HTML 주석</h1>
<!--
<span th:text="${data}">html data</span>
-->
<h1>2. 타임리프 파서 주석</h1>
<!--/* [[${data}]] */-->
<!--여러줄을 주석 처리 할 때-->
<!--/*-->
<span th:text="${data}">html data</span>
<!--*/-->
<!--로컬 웹 브라우저에서 직접 열때는 주석처리가 되고, 실제로 렌더링 된 경우에는 정상 렌더링이 됨-->
<h1>3. 타임리프 프로토타입 주석</h1>
<!--/*/
<span th:text="${data}">html data</span>
/*/-->
</body>
</html>
(2-2) 표준 HTML 주석: <!-- 내용 -->
- 자바 스크립트의 표준 HTML 주석은 타임리프가 렌더링 하지 않고 그대로 남겨 둠
(2-3) 타임리프 파서 주석: <!--/* ${data}... 등의 내용 */-->
- 타임리프의 진짜 주석
- 렌더링에서 아예 주석 부분을 제거해버림
- 여러줄을 주석처리할 때는 아래처럼 작성(*의 위치를 잘 보고 작성)
<!--/*-->
실행코드n개
<!--*/-->
(2-4) 타임리프 프로토타입 주석: <!--/*/ 내용 /*/-->
- 잘 사용하지 않는 특이한 주석
- HTML 파일을 웹 브라우저에서 그대로 열면 HTML 주석이기 때문에 렌더링 되지 않지만, 타임리프 렌더링을 거치면 해당 부분의 내용이 정상 렌더링 되어 출력이 됨
2. 블록
1) 블록
(1) 타임리프 자체 태그
- <th:block>은 HTML 태그가 아닌 타임리프의 유일한 자체 태그
(2) BasicController - block() 추가
@GetMapping("/block")
public String block(Model model) {
addUsers(model);
return "basic/block";
}
(3) block.html
- 타임리프 특성상 HTML 태그안에 속성으로 기능을 정의해서 사용하는데 예제처럼 th:each만으로 해결하기 어려운 경우에 사용하면 됨
- <th:block>은 렌더링시 태그가 제거됨, (th:each만 있으면 적용한 태그가 제거되지 않음)
- 조건부 렌더링과 반복을 함께 사용하는 경우에도 사용함
- 그러나 이기술은 타임리프에만 있는 특별한 태그이기 때문에 사용하지 않는 것을 권장함
<!DOCTYPE html>
<!-- 타임리프 선언 및 head태그 -->
<body>
<!-- div를 n개 이상 반복 시키고 싶을 때 th:block으로 묶어서 작성
th:each만으로 해결하기 어려울 때 사용-->
<!-- th:block 없이 별도의 div태그로 감싸도 th:each만으로도 div태그를 n개 감쌀 수 있음-->
<th:block th:each="user : ${users}">
<div>
사용자 이름1 <span th:text="${user.username}"></span>
사용자 나이1 <span th:text="${user.age}"></span>
</div>
<div>
요약 <span th:text="${user.username} + ' / ' + ${user.age}"></span>
</div>
</th:block>
<th:block th:if="${users.size() > 0}" th:each="user : ${users}">
<div>
사용자 이름: <span th:text="${user.username}"></span>
</div>
</th:block>
</body>
</html>
3. 자바스크립트 인라인
1) 자바스크립트 인라인
(1) 설명
- 자바스크립트에서 타임리프를 편리하게 사용할 수 있는 자바스크립트 인라인 기능을 지원함
- <script th:inline="javascript"> 로 적용
@GetMapping("javascript")
public String javascript(Model model) {
model.addAttribute("user", new User("userA", 10));
addUsers(model);
return "basic/javascript";
}
(2-1) javascript.html
- IDE에서 태그 부분에 빨간줄이 뜨지만 서버는 정상적으로 가동되고 렌더링도 정상적으로 됨
- 실행 후 페이지 소스보기를 하면 자바스크립트에 정상적으로 값들이 적용된 것을 확인할 수 있음
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- 자바스크립트 인라인 사용 전 -->
<script>
var username = [[${user.username}]];
var age = [[${user.age}]];
//자바스크립트 내추럴 템플릿
var username2 = /*[[${user.username}]]*/ "test username";
//객체
var user = [[${user}]];
</script>
<!-- 자바스크립트 인라인 사용 후 -->
<script th:inline="javascript">
var username = [[${user.username}]];
var age = [[${user.age}]];
//자바스크립트 내추럴 템플릿
var username2 = /*[[${user.username}]]*/ "test username";
//객체
var user = [[${user}]];
</script>
</body>
</html>
(2-2) 텍스트 렌더링
- var username = [[${user.username}]];
- 인라인 사용 전: var username = userA;
- 인라인 사용 후: var username = "userA"; - 인라인 사용 전 렌더링 결과는 userA라는 변수 이름이 그대로 남아 있는데 선언하지 않는 userA라는 변수가 var username에 대입하는 결과로 자바스크립트가 동작하면서 자바스크립트 문법 오류가 발생함
- 실제로 개발자 모드로 들어가서 확인해보면 'Uncaught ReferenceError: userA is not defined' 로 오류가 발생하고 있는 것을 확인할 수 있음
- 타임리프는 제대로 렌더링 했지만 userA가 변수명으로 사용되어서 자바스크립트가 오류가 발생하였고 실제로 개발자가 기대한 것은 "userA"라는 문자였을 것임(숫자의 경우에는 ""가 필요 없기 때문에 정상 렌더링이 됨)
- 인라인 사용 후 렌더링 결과를 보면 문자 타입인 경우 자동으로 "를 포함해 줌
- 추가로 자바 스크립트에서 문제가 될 수 있는 문자가 포함 되어있으면 자동으로 이스케이프 처리도 해줌 ex) " → \"
(2-3) 자바스크립트 내추럴 템플릿
- 타임리프는 HTML 파일을 직접 열어도 동작하는 내추럴 템플릿기능을 제공하여 자바스크립트 인라인 기능을 사용하면 주석을 활용하여 이 기능을 사용할 수 있음
- var username2 = /*[[${user.username}]]*/ "test username";
- 인라인 사용 전: var username2 = /*userA*/ "test username";
- 인라인 사용 후: var username2 = "userA"; - 인라인 사용 전 결과는 순수하게 그대로 해석해 버려서 내추럴 템플릿 기능이 동작하지 않고 심지어 렌더링 내용이 주석처리 되어 버림
- 인라인 사용 후 결과에서는 주석 부분이 제거되고 "userA"가 정확하게 적용 됨
(2-4) 객체
- 타임리프의 자바스크립트 인라인 기능을 사용하면 객체를 JSON으로 자동 변환해줌
- var user = [[${user}]];
- 인라인 사용 전: var user = BasicController.User(username=userA, age=10);
- 인라인 사용 후: var user = {"username" : "userA", "age":10}; - 인라인 사용 전은 객체의 toString()이 호출된 값임
- 인라인 기능을 사용하면 객체를 JSON 형태로 자동으로 변환해줌
(2-5) 자바스크립트 인라인 each(반복)
- 자바스크립트 인라인은 each를 지원하며 아래와 같이 작성하면 반복문 처럼 요소를 하나씩 꺼내서 값을 꺼낼 수 있음
<!-- 자바스크립트 인라인 each -->
<script th:inline="javascript">
[# th:each = "user, stat : ${users}"]
var user[[${stat.count}]] = [[${user}]];
[/]
</script>
4. 템플릿 조각
1) 템플릿 조각
(1) 설명
- 웹 페이지를 개발할 때는 상단 영역, 하단영역, 좌측 카테고리 등등의 여러 페이지에서 함께 사용하는 공통 영역이 많이 있음
- 그런 부분을 코드를 복사해서 사용한다면 해당 부분이 변경 되었을 때 여러 페이지를 모두 수정해야 하므로 비효율 적이게 됨
- 이런 문제를 해결하기 위해 템플릿 조각과 레이아웃 기능을 지원함
(2) TemplateController
package hello.thymeleaf.basic;
@Controller
@RequestMapping("/template")
public class TemplateController {
@GetMapping("/fragment")
public String fragment() {
return "template/fragment/fragmentMain";
}
}
(3) footer.html
- templates하위에 /template/fragment 디렉토리 경로를 만든 후 작성
- "copy"와 "copyParam (param1, param2)" 부분은 사용자가 직접 이름을 지정하는 것으로 자유롭게 지정하되 해당 템플릿 조각을 불러오는 HTML 템플릿에서 작성한 이름으로 적어주면 됨
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<!--여러 html 파일에서 불러와서 쓸 수 있는 조각들,
여기서 "copy"는 사용자가 지정하는 fragment의 이름(변수)라고 보면 됨 -->
<footer th:fragment="copy">
푸터 자리 입니다.
</footer>
<!--파라미터도 넣을 수 있음
마찬가지로 "copyParam(param1, param2)"도 사용자가 모두 자유롭게 이름을 지정할 수 있음 -->
<footer th:fragment="copyParam (param1, param2)">
<p>파라미터 자리 입니다.</p>
<p th:text="${param1}"></p>
<p th:text="${param2}"></p>
</footer>
</body>
</html>
(4-1) fragmentMain.html
- template/fragment/footer :: copy: template/fragment/footer.html에 있는 th:fragment="copy" 부분을 템플릿 조각으로 가져와서 사용한다는 의미임
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>부분 포함</h1>
<!--insert는 div부분 안에 template/fragment/footer 위치의 copy 부분을 가져옴-->
<h2>부분 포함 insert</h2>
<div th:insert="~{template/fragment/footer :: copy}"></div>
<!--replace는 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>
</body>
</html>
(4-2) 부분 포함 insert
- <div th:insert="~{template/fragment/footer :: copy}"></div>
- insert를 사용하면 현재 태그 내부에 조각을 추가함
- 예제에서는 div 태그 내부에 조각이 추가 됨
(4-3) 부분 포함 replace
- <div th:replace="~{template/fragment/footer :: copy}"></div>
- replace는 div태그 자체를 대체해버림
- 페이지 소스보기를 해보면 div 태그 자체가 없어지고 완전히 대체 된 것을 확인할 수 있음
(4-4) 부분 포함 단순 표현식
- <div th:replace="template/fragment/footer :: copy"></div>
- 템플릿 조각을 사용하는 코드가 경로와 이름정도만 있는 것처럼 단순하면 ~{ ... } 부분을 생략하여 사용할 수 있음
- 복잡해지면 사용불가능함
(4-5) 파라미터 사용
- <div th:replace="~{template/fragment/footer :: copyParam('데이터1', '데이터2')}"></div>
- 파라미터를 전달하여 동적으로 조각을 렌더링 할 수 있음
5. 템플릿 레이아웃
1) 템플릿 레이아웃
(1) 설명
- 일부 코드 조각을 가져와서 사용하는 개념을 더 확장해서 코드 조각을 레이아웃에 넘겨서 사용하는 방법
- 예를 들어 <head>에 공통으로 사용하는 css, javascript 같은 정보들을 한 곳에 모아두고, 공통으로 사용하지만 각 페이지마다 필요한 정보를 더 추가해서 사용하는 방법임
- 레이아웃 틀에 나의 코드를 추가하는 방법이라고 보면 됨
(2) TemplateController - layout() 추가
@GetMapping("/layout")
public String layout() {
return "template/layout/layoutMain";
}
(3) base.html
- template하위에 layout디렉토리를 생성 후 작성
- 전체 사이트에서 공통으로 사용하는 레이아웃
<!--전체 사이트에서 공통으로 사용하는 레이아웃-->
<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>
(4) layoutMain.html
- common_header(~{::title},~{::link})이 핵심
- ::title은 현재 페이지의 title태그들을 전달함
- ::link은 현재 페이지의 link태그들을 전달함
<!DOCTYPE html>
<!--base.html을 가져다 쓰는 html-->
<html xmlns:th="http://www.thymeleaf.org">
<!--::title -> title 태그의 내용을 넘김, ::link -> link 태그들을 넘김-->
<!--template/layout/base 위치의 common_header의 코드를 전부 replace-->
<head th:replace="template/layout/base :: common_header(~{::title},~{::link})">
<!-- 레이아웃에 넣을 타이틀-->
<title>메인 타이틀</title>
<!-- 레이아웃에 추가할 link 태그들-->
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
<link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}">
</head>
<body>
메인 컨텐츠
</body>
</html>
(5) 결과 - 페이지 소스보기
- th:replace로 base.html의 head가 layoutMain.html의 head들로 교체가 됨
- 즉, layoutMain.html에 적힌 <title>태그와 <link> 태그 자체를 포함하여 common_header(~{::title}, ~{::link})로 인해base.html의 common_header(title, links) 부분과 완전히 교체가 됨
- base의 공통부분은 그대로 유지되고 넘어간 link 태그들이 추가되어 출력되고, title도 메인 타이틀로 변경되었음
- 레이아웃 개념을 두고 그 레이아웃에 필요한 코드 조각을 전달하여 완성되는 방식으로 이해하면 됨
<!DOCTYPE html>
<!--base.html을 가져다 쓰는 html-->
<html>
<!--::title -> title 태그의 내용을 넘김, ::link -> link 태그들을 넘김-->
<!--template/layout/base 위치의 common_header의 코드를 전부 replace-->
<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>
2) 템플릿 레이아웃 확장
- 템플릿 레이아웃의 개념을 head만 적용하는 것이 아니라 html 전체에 적용
(1) TemplateController - layoutExtend() 추가
@GetMapping("/layoutExtend")
public String layoutExtend() {
return "template/layoutExtend/layoutExtendMain";
}
(2) layoutFile.html
- template 하위에 layoutExtend디렉토리를 만든 후 작성
- 레이아웃이 같은 여러개의 사이트가 있을 때 title과
- <html> 태그의 th:fragment= "layout(매개변수)": 매개변수에 넘어온 값들을 ${매개변수} 부분에 치환하여 값들을 출력
<!DOCTYPE html>
<!--레이아웃이 같은 여러개의 사이트가 있을 때 ${title}과 ${content} 부분만 각 페이지마다 달라야 한다고 가정-->
<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>
(3) layoutExtendMain.html
- html 태그 영역에 th:replace를 사용하여 layoutFile의 html 태그에 있는 layout(title, content)를 대체
- ~{::title}과 ~{::section}으로 <title>과 <section>태그 자체를 layoutFile로 교체함
- 위에서 했던 동일한 개념을 html 태그에 적용하여 html파일 자체에 적용했다고 보면 됨
<!DOCTYPE html>
<!--html부분에 th:replace를 작성하여 통으로 layoutFile로 replace, title과 section을 넘김-->
<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>
(4) 결과
- base.html의 title인 레이아웃 페이지 타이틀이 layoutExtendMain.html의 <title> 태그로 완전히 replace되어 메인 페이지 타이틀로 변경이 되었음
- base.html의 content인 레이아웃 컨텐츠도 layoutExtendMain.html의 <section> 태그로 완전히 replace되어 메인 페이지 컨텐츠, 메인 페이지 포함 내용으로 바뀌었음
- base.html의 <footer>태그는 아무것도 적용하게 없으므로 그대로 출력 되었음
(5) 정리
- 템플릿 조각:
- 사이트가 작으면 템플릿 조각으로 조각들을 불러와서 편리하게 사용하여 쉽게 사이트를 만들 수 있음
- 레이아웃을 한번에 바꾸어야 할 때 작업이 많아지고 중복이 생기는 단점이 생김
- 레이아웃:
- 페이지가 많아지고 관리가 중요할 때에는 레이아웃 개념(템플릿 레이아웃)을 사용하는 것이 좋음
- 중복을 최소화 하고 레이아웃 변경이 수월하나 체계적으로 관리해야 하는 단점이 존재함