패키지 네임에 - 라는 기호가 안들어가게 주의해야한다!
타임리프
타임리프 공식문서
타임리프 사용 선언
<html xmlns:th="http://www.thymeleaf.org">
타임리프 기본 표현식
타임리프 표현식보는 공식문서
타임리프 텍스트 출력해보기 - text,utext
타임리프는 기본적으로 HTML 태그의 속성에 기능을 정의해서 동작한다.
HTML의 컨텐츠(content)에 데이터를 출력할때는 다음과 같이
th:text
를 사용하면 된다.
BasicController
@Controller
@RequestMapping("/basic")
public class BasicController {
@GetMapping("text-basic")
public String textBasic(Model model) {
model.addAttribute("data", "<b>hellospring</b>");
return "basic/text-basic";
}
}
url호출을 받으면 모델에 데이터를 담고 뷰를 렌더링해준다.
text-basic.html에 타임리프 적용하기
<h1>컨텐츠에 데이터 출력하기</h1>
<ul>
<li>th:text 사용 <span th:text="${data}">뷰가 렌더링되면 여기에있는 내용이 대체된다.</span></li>
<li th:text="|${data} 리터럴대체를 편하게 합치기|">기본값</li>
<li>컨텐츠 안에서 직접 출력하기 = [[${data}]]</li>
<li>컨텐츠 안에서는 [[${data}]] 대괄호를 두개써줘야한다.</li>
</ul>
뷰가 렌더링되면 th:text를 이용한 내용으로 치환된다.
컨텐츠안에서 작성하고 싶다면 [[${변수표현식} ]] 대괄호를 2개사용하고, 안에는 원하는 표현식을 사용한다.
[[....]]
Escape, HTML 엔티티
<>같은 특수문자를 모델에 담은 데이터에 포함되어있다면?
@GetMapping("text-basic")
public String textBasic(Model model) {
model.addAttribute("data", "<b>hellospring</b>");
return "basic/text-basic";
}
처리가 되어있지않은 상태로 결과가 보인다.
소스보기를 하면
타임리프가 <b>같은 태그형식을 보면 그냥 출력하지않고, HTML 엔티티라는것으로 바꿔서 출력해준다.
위에서는 < 가 <로 바꿔진것을 확인할 수 있다.
이렇게 HTML에서 사용하는 특수문자를 HTML 엔티티로 변경하는것을 이스케이프(Escape)라고 한다.
기본적으로 타임리프가 제공하는 th:text , [[....]]는 기본적으로 이스케이프(escape)를 지원한다.
Unescape
그렇다면 태그가 이스케이프 처리되지않고 그대로 보이려면 어떻게해야할까?
th:text대신에 th:utext를 사용하고,
[[...]] 대신에 [(...)]를 사용한다.
수정해서 Unescape 테스트
@GetMapping("text-unescaped")
public String textUnescaped(Model model) {
model.addAttribute("data", "<b> hellospring </b>;");
return "basic/text-unescaped";
}
컨트롤러에서 모델에 <b> 태그를 넣은 데이터를 넣을것이다.
<ul>
<li>th:text 사용 <span th:utext="${data}">뷰가 렌더링되면 여기에있는 내용이 대체된다.</span></li>
<li th:utext="|${data} 리터럴대체를 편하게 합치기|">기본값</li>
<li>컨텐츠 안에서 직접 출력하기 = [(${data})]</li>
<li>컨텐츠 안에서는 [(${data})] 대괄호를 두개써줘야한다.</li>
</ul>
뷰템플릿에서 unescape를 지원하는 표현식으로 수정하고 다시 테스트해보면
<b> 태그가 적용된것을 확인할 수 있다.
text-unescaped.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>text vs utext</h1>
<ul>
<li>th:text = <span th:text="${data}"></span></li>
<li>th:utext = <span th:utext="${data}"></span></li>
</ul>
<h1><span th:inline="none">[[...]] vs [(...)]</span></h1>
<ul>
<li><span th:inline="none">[[...]] = </span>[[${data}]]</li>
<li><span th:inline="none">[(...)] = </span>[(${data})]</li>
</ul>
</body>
</html>
소스보기를 해보면 escape를 제공하는 표현식은 <b>등으로 바뀌어있고
escape를 제공하지않는 표현식은 <b> </b> 태그가 잘들어가 적용되어있다.
이스케이프 처리를 하지않으면 태그를 사용한 입력에 HTML이 깨질 수 있다.
변수 - SpringEL
타임리프에서 변수를 사용할 때는 변수 표현식을 사용한다.
타임리프 변수 표현식에는
스프링EL이라는 스프링이 제공하는 표현식을 사용할 수 있다.
테스트용 컨트롤러
User라는 static 클래스를 데이터용으로 생성한다.
컨트롤러에서 각종 데이터를 담은 후 뷰템플릿을 렌더링해본다.
@GetMapping("/variable")
public String variable(Model model) {
User userA = new User("userA", 10);
User userB = new User("userB", 20);
List<User> list = new ArrayList<>();
list.add(userA);
list.add(userB);
Map<String, User> map = new HashMap<>();
map.put("userA", userA);
map.put("userB", userB);
model.addAttribute("user", userA);
model.addAttribute("users", list);
model.addAttribute("userMap", map);
return "basic/variable";
}
@Data
static class User {
private String username;
private int age;
public User(String username, int age) {
this.username = username;
this.age = age;
}
}
basic/variable.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>SpringEL 표현식</h1>
<ul>Object
<li>${user.username} = <span th:text="${user.username}"></span></li>
<li>${user['username']} = <span th:text="${user['username']}"></span></li>
<li>${user.getUsername()} = <span th:text="${user.getUsername()}"></span></li>
</ul>
<ul>List
<li>${users[0].username} = <span th:text="${users[0].username}"></span>
</li>
<li>${users[0]['username']} = <span th:text="${users[0]['username']}"></span>
</li>
<li>${users[0].getUsername()} = <span th:text="${users[0].getUsername()}"></span></li>
</ul>
<ul>Map
<li>${userMap['userA'].username} = <span th:text="${userMap['userA'].username}"></span></li>
<li>${userMap['userA']['username']} = <span th:text="${userMap['userA']['username']}"></span></li>
<li>${userMap['userA'].getUsername()} = <span th:text="${userMap['userA'].getUsername()}"></span></li>
</ul>
</body>
</html>
결과
결과를 보면
Object , List , Map에서
userA라는 결과를 얻기 위한 다양한 표현식들을 확인 할 수 있다.
첫번째로 user.username 처럼 .을 이용하는 방법은 user의 username을 프로퍼티 접근하는것이다.
자동으로 getUsername()에 접근해서 값을 가져온다.
두번째는
괄호안에 ['프로퍼티명'] 을 이용해서 첫번째처럼 getUsername()을 통해 값을 가져오는방법
세번째는 직접 getUsername()을 호출 하는 방법이다.
List는 인덱싱하는거라 이상하지않은데
Map은 괄호를 이용해서 '키값'을 통해 userA를 찾고 3가지방법으로 값을 가져오는 모습을 보인다.
지역변수 선언
<h1>지역 변수 - (th:with)</h1>
<div th:with="first=${users[0]}">
<p>처음 사람의 이름은 <span th:text="${first.username}"></span></p>
</div>
th:with 를 이용하여 지역변수를 선언할 수 있다.
first라는 변수에는 users[0]의 값이 담기게되고 그 이후부터는 first를 변수처럼 사용할 수 있다.
기본 객체들
타임리프는 기본 객체들을 제공한다.
스프링 3.0부터는 아래의 기본 객체들은 지원하지 않는다.
지원하는 객체는 locale
스프링 부트 3.0이상 부터는 직접 모델에 해당 객체를 추가해서 사용해야한다.
편의객체
접근하기 편하게 만들어 놓은 편의객체가 존재한다.
기본객체, 편의객체 테스트
@GetMapping("/basic-objects")
public String basicObjects(Model model,HttpSession session, HttpServletRequest request, HttpServletResponse response) {
session.setAttribute("sessionData", "Hello Session"); //HttpSession객체에 값을 담아준다.
model.addAttribute("request", request);//모델에 HttpServletRequest도 담아 넘길수 있다.
model.addAttribute("response", response);
model.addAttribute("servletContext", request.getServletContext());
return "basic/basic-objects";
}
@Component("helloBean")
static class HelloBean {
public String hello(String data) {
return "Hello" + data;
}
}
컨트롤러에 다음과 같이 코드를 추가한다 (세션과 모델에 각각 데이터를 담아준다) (세션은 뒤에 로그인파트에서 설명)
그리고 단순한 스프링빈을 추가해준다. (스프링 빈 접근 편의객체 테스트용)
결과확인용 뷰 템플릿
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>식 기본 객체 (Expression Basic Objects)</h1>
<ul>
<li>request = <span th:text="${request}"></span></li>
<li>response = <span th:text="${response}"></span></li>
<li>session = <span th:text="${session}"></span></li>
<li>servletContext = <span th:text="${servletContext}"></span></li>
<li>locale = <span th:text="${#locale}"></span></li>
</ul>
<h1>편의 객체</h1>
<ul>
<li>Request Parameter = <span th:text="${param.paramData}"></span></li>
<li>session = <span th:text="${session.sessionData}"></span></li>
<li>spring bean = <span th:text="${@helloBean.hello('Spring!')}"></span></
li>
</ul>
</body>
</html>
http://localhost:8080/basic/basic-objects?paramData=HelloParam
위의 URL 접속 (파라미터데이터까지 추가)
결과
결과를 보면 request ~ servletContext는 객체를 모델에 넣어줬으므로 객체에 대한 클래스명과 주소값이 나오고
local은 ${#locale}로 출력된것이다. 따로 모델에 넣은값이 아니고 기본으로 제공되는 객체는 #을 이용해서 가져온다.
편의객체 부분에서는
${param.paramData}를 이용해서 URL파라미터로 들어온 값을 가져올 수있다. ( 요청파라미터 접근을 위한 param 편의객체를 지원해준다)
session이라는 편의객체도 지원되므로 ${session.sessionData}로 세션에 담긴 데이터를 가져와볼수도있다.
@를 이용해서 스프링 bean에 접근이 가능한데 (스프링빈 이름을 이용해서 접근한다)
${@helloBean.hello('spring!')} 를 이용하여 helloBean이라는 이름의 스프링빈에 접근해서 메소드를 호출하고 결과를 받아왔다. 전달인자를 주는 부분에서 문자열받으니까 '문자열' 과 같이 작은따음표(타임리프 텍스트 기본표현식)을 이용했다.
유틸리티 객체와 날짜
위에서 보았던 기본객체,편의객체처럼
#을 이용해서 유틸리티 객체를 사용하면 된다.
유틸리티 객체 소개 문서
날짜
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
스프링부트 타임리프를 사용하면 자동으로 추가된다고 한다.
자바8 날짜용 유틸리티 객체
#temporals
사용 예시 테스트
@GetMapping("/data")
public String date(Model model) {
model.addAttribute("localDateTime", LocalDateTime.now());
return "basic/date";
}
모델에 현재 날짜데이터를 보내준다.
뷰 템플릿
<h1>LocalDateTime</h1>
<ul>
<li>default = <span th:text="${localDateTime}"></span></li>
<li>yyyy-MM-dd HH:mm:ss = <span th:text="${#temporals.format(localDateTime,'yyyy-MM-dd HH:mm:ss')}"></span></li>
</ul>
<h1>LocalDateTime - Utils</h1>
<ul>
<li>${#temporals.day(localDateTime)} = <span th:text="${#temporals.day(localDateTime)}"></span></li>
<li>${#temporals.month(localDateTime)} = <span th:text="${#temporals.month(localDateTime)}"></span></li>
<li>${#temporals.monthName(localDateTime)} = <span th:text="${#temporals.monthName(localDateTime)}"></span></li>
<li>${#temporals.monthNameShort(localDateTime)} = <span th:text="${#temporals.monthNameShort(localDateTime)}"></span></li>
<li>${#temporals.year(localDateTime)} = <span th:text="${#temporals.year(localDateTime)}"></span></li>
<li>${#temporals.dayOfWeek(localDateTime)} = <span th:text="${#temporals.dayOfWeek(localDateTime)}"></span></li>
<li>${#temporals.dayOfWeekName(localDateTime)} = <span th:text="${#temporals.dayOfWeekName(localDateTime)}"></span></li>
<li>${#temporals.dayOfWeekNameShort(localDateTime)} = <span th:text="${#temporals.dayOfWeekNameShort(localDateTime)}"></span></li>
<li>${#temporals.hour(localDateTime)} = <span th:text="${#temporals.hour(localDateTime)}"></span></li>
<li>${#temporals.minute(localDateTime)} = <span th:text="${#temporals.minute(localDateTime)}"></span></li>
<li>${#temporals.second(localDateTime)} = <span th:text="${#temporals.second(localDateTime)}"></span></li>
<li>${#temporals.nanosecond(localDateTime)} = <span th:text="${#temporals.nanosecond(localDateTime)}"></span></li>
</ul>
</body>
</html>
현재 날짜 데이터가 localDateTime으로 전달된다.
#temporals이라는 날짜 유틸리티 객체로 전달된 날짜데이터를 가지고 포맷팅한다던지, 날짜를 뽑아낸다던지 등 다양하게 접근할 수 있다.
결과
URL 링크
타임리프에서 URL을 생성할 때는 @{...} 문법을 사용하면 된다.
테스트
컨트롤러
@GetMapping("link")
public String link(Model model) {
model.addAttribute("param1", "data1");
model.addAttribute("param2", "data2");
return "basic/link";
}
url을 생성할때 쿼리파라미터까지 넣기 위해, 파라미터값으로 쓸 데이터를 모델에 담았다.
뷰 템플릿
<ul>
<li><a th:href="@{/hello}">basic url</a></li>
<li><a th:href="@{/hello(param1=${param1}, param2=${param2})}">hello query param</a></li>
<li><a th:href="@{/hello/{param1}/{param2}(param1=${param1}, param2=${param2})}">path variable</a></li>
<li><a th:href="@{/hello/{param1}(param1=${param1}, param2=${param2})}">path variable + query parameter</a></li>
</ul>
href에는 하이퍼링크의 url을 적는 부분이다.
<a>태그의 content는 하이퍼링크의 내용을 적으면 된다.
url생성은 @{...}를 이용해서 만들어준다.
첫번째 (단순한 URL)
@{/hello}
는
http://localhost:8080/hello
이라는 URL을 만들어줄것이고
두번째 (쿼리 파라미터)
@{/hello(param1=${param1}, param2=${param2})}
은 () 소괄호안에 파라미터명, 파라미터값을 넣어줌으로서
http://localhost:8080/hello?param1=data1¶m2=data2
이라는 URL을 만들어준다.
세번째 (경로 변수)
@{/hello/{param1}/{param2}(param1=${param1}, param2=${param2})}
은 {}를 이용해서 변수를 넣고, ()안에 그 변수에 대한 값이 있으면 치환해서 경로변수로 처리 된다.
http://localhost:8080/hello/data1/data2
네번째 (경로 변수 + 쿼리 파라미터)
@{/hello/{param1}(param1=${param1}, param2=${param2})}
처럼 경로변수가 하나인데 ()안에는 2개의 변수가 있다. 치환하고 남는애는 쿼리파라미터로 사용된다.
http://localhost:8080/hello/data1?param2=data2
즉 ()에 넣어주는 변수는 PathVariable이 있다면 치환해주고 없다면 파라미터로 사용된다!
그리고 URL를 생성할때 상대경로 ,절대경로, 프로토콜 기준으로 표현할 수 도 있다.
/hello 이렇게 /로 시작하면 절대경로 (서버주소 + 생성된 URL)
hello 이렇게 /없이 시작하면 상대경로(현재 URL + 생성된 URL)
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#link-urls
'인프런 > 스프링 MVC 2편' 카테고리의 다른 글
6) 메시지 ,국제화 (0) | 2023.02.02 |
---|---|
5)타임리프 - 라디오버튼,셀렉트박스 (0) | 2023.02.02 |
4)타임리프 적용,체크박스,멀티체크박스 (0) | 2023.02.02 |
3) 타임리프 (블록 , 자바스크립트 인라인 , 템플릿 조각, 템플릿 레이아웃) (0) | 2023.02.01 |
2)타임리프 (리터럴,연산,속성값설정,반복,조건부 평가,주석) (1) | 2023.02.01 |
댓글