Spring Boot 3. Spring MVC & Thymeleaf
Spring Boot 3. Spring MVC & Thymeleaf
Thymeleaf : 실제 화면을 구성하도록 해줌 (JSP와 비슷)
1. Thymeleaf를 사용하는 프로젝트 생성
Spring Boot 프로젝트를 생성할 때 Dependencies 항목에서 Spring Boot, DevTools, Lombok, Spring Web과 더불어 Template Engines 항목에서 Thymeleaf 선택.
Thymeleaf를 사용할 때에는 변경 후에 만들어진 결과를 보관하지 않도록 설정하는 것이 편리
src -> mani -> resources -> application.properties 파일에 아래와 같이 작성
spring.thymeleaf.cache=false
=> Thymeleaf 파일을 수정하고 저장한 후에 브라우저에서 변경된 결과를 확인하기 위한 설정
- 컨트롤러
컨트롤러 클래스는 전반적인 동작 처리
* 예시
프로젝트 내에 controller 페키지 생성 -> SampleController 클래스 생성
SampleController.java
package com.example.ex3.controller;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/sample")
@Log4j2
public class SampleController {
@GetMapping("/test1")
public void SampleController() {
log.info("test1 ...............");
}
}
@Controller : 해당 클래스가 컨트롤러 클래스임을 나타냄
@RequestMapping : 서버에서 요청하기 위함. () 안에는 요청하는 가상 디렉토리 명 작성.
@Log4j2 : Lombok의 기능으로 로그를 남기도록 해줌
@GetMapping : 서버에서 요청시 해당 메소드와 연결. () 안에는 요청하는 가상 디렉토리 명 작성.
log.info() : 로그 레벨이 INFO(애플리케이션의 상태, 이벤트에 대한 정보 등)인 로그를 기록하는 메소드
-> 브라우저에서 localhost:8080/sample/ex1을 입력하면 해당 클래스의 해당 메소드가 호출됨
- Thymeleaf 이용 HTML 작성
src -> main -> resources -> templates 폴더 안에 sample 폴더 생성 -> test1.html 파일 생성
(html 파일의 이름은 controller 클래스에 작성된 메소드의 @GetMapping() 안의 가상 디렉토리명과 일치해야 해당 페이지와 메소드 맵핑 가능)
* 예시
<!DOCTYPE html><html lang="en" xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Test</title>
</head>
<body>
<h1 th:text="${'Hello World'}"></h1>
</body>
</html>
<html> 안의 xmlns 속성 : 없어도 되지만 있으면 좀더 코드의 완성을 도와줌
Thymeleaf의 기본 사용 방법은 기존의 속성 앞에 'th:'를 붙여주고 속성값을 지정
th:text : 해당 태그의 text 속성 지정
"${}" : {} 안에 변수명이나 직접 값을 입력해서 해당 값을 이용할 수 있도록 해줌
<실행결과>
Thymeleaf를 이용하면 기존의 HTML 구조를 그대로 두면서 필요한 동작이나 값을 추가할 수 있음
2. Thymeleaf의 기본 사용 방법
* DTO : Data Transfer Object. 값들이 저장된 객체, MVC 패턴에서 데이터 모델이 해당
* 예시
프로젝트 패키지 안에 dto 패키지를 생성하고 SampleDTO 클래스 생성
SampleDTO.java 에 아래와 같이 코드 작성
package com.example.ex3.dto;
import lombok.Builder;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@Builder(toBuilder = true)
public class SampleDTO {
private Long sno;
private String first;
private String last;
private LocalDateTime regTime;
}
@Data : 해당 클래스가 데이터 클레스 임을 명시. Getter, Setter, toString, equals, hasCode등의 메소드 자동 생성
@Builder(toBuilder = true) : 객체를 생성할 수 있게 Builder() 메소드 제공
LocalDateTime : 현제 날짜와 시간을 받아오고 저장할 수 있는 타입
* 예시 - controller 클래스에서 data 클래스의 객체를 Model에 추가해서 전달
(Model : 데이터를 캡슐화 해서 처리하는데 사용되는 객체)
SampleController.java 에 아래와 같이 메소드 추가
@GetMapping({"/test2"})
public void testModel(Model model) {
// 전송할 객체 리스트 생성
List<SampleDTO> list = IntStream.rangeClosed(1, 10).asLongStream().mapToObj(i -> {
SampleDTO dto = SampleDTO.builder()
.sno(i)
.first("First .. " + i)
.last("Last .. " + i)
.regTime(LocalDateTime.now())
.build();
return dto;
}).collect(Collectors.toList());
model.addAttribute("list", list);
}
@GetMapping : () 안에 가상 디렉토리 명을 작성할 때 {} 안에 작성하면 하나 이상의 URL을 지정할 수 있음
IntStream : int형 데이터를 처리하는데 특화된 기능들 제공
rangeClosed() : IntStream의 메소드 중에 하나로 () 안의 두 정수를 포함하는 int형 값에 대한 스트림 생성
asLongStream() : Stream 클래스의 메소드 중 하나로 해당 스트림의 요소를 Long 타입의 값으로 변환해서 새로운 LongStream을 생성
mapToObj() : IntStream, LongStream, DoubleStream과 같은 기본 타입을 요소로 가지는 스트림에서 요소를 다른 타입으로 변환하는 메소드
builder().build() : @Builder 어노테이션을 추가하면 제동되는 메소드로 builder() 와 build() 메소드 사이에 [필드 이름](필드 값) 형식으로 각 필드에 값을 대입해서 해당 클래스의 객체를 생성
collect(Collectors.toList()) : Collerctor 클래스의 메소드 중 하나. 스트림의 요소를 List로 수집.
model.addAttribute() : Spring MVC 에서 컨트롤러는 모델 객체에 데이터를 추가해서 데이터에 전달하는데 이때 모델 객체에 데이터를 추가하는 메소드. ( ) 안에 이름과 데이터를 순서대로 작성
=> SampleDTO 객체를 10개 생성해서 List에 추가하고 이를 Model에 'lsit'라는 이름으로 추가해서 해당 메소드를 호출하는 뷰에 전송
1) Thymeleaf 기본 사용 방법 - 반복문 처리
th:each 속성 이용
th:each = "[변수] : ${목록}"
목록의 값이 하나씩 변수에 대입되며 반복 (인핸스드 for문과 비슷)
* 예시
src -> main -> resources -> templates -> sample -> test2.html 파일 생성
아래와 같이 작성
<body>
<ul>
<li th:each="dto : ${list}">
[[${dto}]]
</li>
</ul>
</body>
[[ ]] : 인라인 표현식. [[ ]] 안의 내용을 코드에 추가
=> "list" 라는 이름으로 전달된 모델의 데이터를 반복적으로 변수 dto에 저장해서 화면에 출력
<실행결과>
- 반복문의 상태객체(state)
반복문에 부가적으로 사용할 수 있는 state객체가 있음
순번이나 인덱스 번호, 홀수 짝수등을 지정할 수 있음
반복문에서 데이터를 받아올 변수 다음에 , 다음에 상태 객체를 받아올 변수 작성(꼭 이름이 state가 아니어도 됨)
.count -> 1부터 시작, .index -> 0부터 시작
* 예시
<ul>
<li th:each = "dto, state : ${list}">
[[${state.index}]] --> [[${dto}]]
</li>
</ul>
=> state 변수에 상택개체을 받아와서 index 속성 이용
<실행결과>
2) Thymeleaf 기본 사용 방법 - 제어문 처리
th:if ~ unless를 이용하거나 삼항 연산자 스타일 사용 가능
th:if 는 해당 조건이 참일 때, th:unless는 해당 조건이 거짓일 때
th:if(unless)="${ 여기에 조건식 작성 }"
if 와 unless는 하나의 묶음이 아닌 단독으로 처리됨
* 예시 -sno가 짝수만 출력
<ul>
<li th:each = "dto, state : ${list}" th:if="${dto.sno % 2 == 0}">
[[${dto}]]
</li>
</ul>
<실행결과>
* 예시 - sno가 3의 배수일 때만 다른 형식으로 출력
<ul>
<li th:each = "dto, state : ${list}">
<span th:if="${dto.sno % 3 == 0}" th:text="'---------' + ${dto.sno}"></span>
<span th:unless="${dto.sno % 3 == 0}" th:text="${dto.first}"></span>
</li>
</ul>
<실행결과>
- 삼항 연산자
th:[속성] = "${조건식} ? ${참일 때} : ${거짓일 때}"
거짓인 경우 없이 ? 뒤에 참인 경우만 작성할 수도 있음
* 예시 - sno가 3의 배수일 때만 다른 형식으로 출력(삼항 연산자 이용)
<ul>
<li th:each = "dto, state : ${list}"
th:text="${dto.sno % 3 == 0} ? ${'-------- ' + dto.sno} : ${dto.first}">
</li>
</ul>
<실행결과>
* 예시 - 3항 연산자를 이용해서 서로 다른 css 스타일 적용
<head>
<meta charset="UTF-8">
<title>Test2</title>
<style>
.target{background-color: blueviolet}
</style>
</head>
<body>
<ul>
<li th:each = "dto, state : ${list}"
th:class="${dto.sno % 2 == 0} ? ${'target'}"
th:text="${dto}">
</li>
</ul>
</body>
=> 조건이 참이면 해당 <li> 태그의 class가 'target'으로 지정되서 스타일이 적용됨
<실행결과>
3) Thymeleaf 기본 사용 방법 - inline 속성
javaScript 처리에 유용
리다이렉트하면서 전달한 파라미터를 javascript 코드로 받아올 수 있도록 해줌
HTML 파일에서 <script>코드의 th:inline을 javascript로 해주면 javascript 코드에서 전달된 데이터를 이용할 수 있음
* RedirectAttributes
스프링 MVC에서 리다이렉트 시에 데이터를 전달하기 위한 인터페이스
메소드 addAttribute( [데이터 이름], [데이터 값] ) : 리다이렉트 시에 전달할 값 추가.
메소드 addFlashAttribute( [데이터 이름], [데이터 값] ) : 리다이렉트 시에 일회성으로 데이터 전달
-> 리다이렉트된 페이지에서 데이터 이름을 이용해서 데이터 값을 받아볼 수 있음
메소드의 매개변수로 RedirectAttributes 타입의 객체를 지정하고 반환값을 String 타입으로 지정한 뒤 반환값을 "redirect:[리다이렉트 될 경로] 으로 지정
* 예시
SampleController.java
@GetMapping({"/exInline"})
public String exInline(RedirectAttributes redirectAttributes) {
SampleDTO dto = SampleDTO.builder()
.sno(100L)
.first("Frist..100")
.last("Last..100")
.regTime(LocalDateTime.now())
.build();
redirectAttributes.addFlashAttribute("result", "success");
redirectAttributes.addFlashAttribute("dto", dto);
return "redirect:/sample/ex3";
}
@GetMapping("/ex3")
public void ex3() {
log.info("ex3..........");
}
ex3.html
<body>
<h1 th:text="${result}"></h1>
<h1 th:text="${dto}"></h1>
<script th:inline="javascript">
var msg = [[${result}]]
var dto = [[${dto}]]
</script>
</body>
-> 브라우저에서 "/sample/exInline"으로 접속하면 exInline() 메소드가 실행되고 SampleDTO 객체를 생성해서 "result" 라는 이름의 문자열 데이터와 함께 리다이렉트 -> ex3.html 페이지로 리다이렉트 되고 전달된 데이터들을 'result'와 'dto'라는 이름으로 받아봄
<script> 의 th:inline을 javascript로 지정했기 때문에 전달된 데이터를 받아볼 수 있음
<실행결과>
-> 개발자도구에서 페이지의 소스를 보면 <script> 태그 안에 각 변수의 값으로 리다디렉트 하면서 전달받은 데이터가 저장된 것을 확인할 수 있음 (객체 타입은 JSON 형식으로 전달됨)
- th:block
HTML 파일에서 별도의 태그 없이 <th:block> 으로 사용할 수 있음
반복문 등을 별도로 처리할 때에 많이 사용
* 예시
<ul>
<th:block th:each="dto : ${list}">
<li th:text="${dto.sno %3 == 0} ? ${dto.sno} : ${dto.first}"></li>
</th:block>
</ul>
<실행결과>
4) Thymeleaf 기본 사용 방법 - 링크 처리
@{} 을 이용해서 링크 사용
파라미터를 전달해야 하는 상황에서 좀더 가독성 좋은 코드를 작성할 수 있음
a 태그의 th:href 속성에 @{}를 이용해서 링크 전달 가능
* 예시
SampleController.java -> testModel() 메소드에 맵핑할 가상 디렉토리 추가
(/testLink로 접근해도 testMethod 호출)
@GetMapping({"/test2", "/testLink"})
public void testModel(Model model) {
.....
}
testLink.html
<ul>
<li th:each="dto : ${list}">
<a th:href="@{/sample/testView}">[[${dto.first}]]</a>
</li>
</ul>
<실행결과>
-> 링크가 생성되고 href 속성이 지정됨
- 파라미터 추가하기
링크 끝에 () 를 추가하고 [변수 이름] = ${값} 작성
* 예시
<ul>
<li th:each="dto : ${list}">
<a th:href="@{/sample/testView(sno = ${dto.sno})}">[[${dto.first}]]</a>
</li>
</ul>
<실행결과>
- path 추가하기
링크 끝에 /{ [변수] }( [변수] = ${값} ) 작성
* 예시
<ul>
<li th:each="dto : ${list}">
<a th:href="@{/sample/testView/{sno}(sno = ${dto.sno})}">[[${dto.first}]]</a>
</li>
</ul>
<실행결과>
3. Thymeleaf의 기본 객체와 LocalDateTime
Thyneleaf 에는 내부적으로 여러 종류의 기본 객체가 있음
(문자나 숫자, 웹에서 사용되는 파라미터, request, response, session 등)
숫자나 날짜 처리를 위해 #numbers나 #temporals 객체를 별도의 설정 없이 사용할 수 있음
* 예시 - #numbers를 이용해서 정수 자리수를 5로 지정
<ul>
<li th:each="dto : ${list}">
[[${#numbers.formatInteger(dto.sno, 5)}]]
</li>
</ul>
<실행결과>
* 예시 - #temporals 객체를 이용해 날짜 처리
<ul>
<li th:each="dto : ${list}">
[[${dto.sno}]] --- [[${#temporals.format(dto.regTime, 'yyyy/MM/dd')}]]
</li>
</ul>
<실행결과>

댓글
댓글 쓰기