Spring MVC & Thymeleaf

2024. 1. 3. 15:42
728x90

1. MVC 패턴

: 사용자 인터페이스를 비즈니스 로직으로부터 분리하는 것을 목표로 만들어진 디자인 패턴

 

  • Model : 어플리케이션을 표현하기 위한 데이터 관리하는 부분
  • View : 사용자에게 정보가 표현되는 방식을 관리하는 부분
  • Controller : 사용자의 입력을 받아, Model이 이해할 수 있는 형태로 변환

  • 1) 외부의 클라이언트가 요청을 보낸다.
  • 2) DispatcherServlet은 적당한 Controller의 RequestMapping으로 위임한다.
  • 3) Controller는 요청을 Model에 명령을 보내고 >> Model은 명령을 처리하여 갱신된 데이터를 전달하거나, 명령 처리의 상태에 대하여 알려준다.
  • 4) Controller는 Model에서 받은 데이터를 표현할 View를 선정해서 DispatcherServlet에 전달한다.
  • 5) DispatcherServlet은 다시 요청을 보낸 클라이언트로 응답을 번역해서 전송한다. 
  • 이때 메서드의 반환이 무엇인지에 따라 데이터를 응답할지/ HTML을 응답할지를 DispatchServlet에서 결정하는데
  • 문자열이 메서드에서 반환되면 일종의 View라고 판단하여 > ViewResolver에게 반환된 문자열과 이름이 일치하는 View를 반환해달라고 요구한다. 

 

2. View - Thymeleaf 사용해보기

1) Template Engine ?

: 단순한 HTML이 아니고, 상황과 사용자에 따라 (Model의 변화에 따라) 드러나는 모습이 다르게 동적으로 바꿔주는 라이브러리의 일종.

- Spring Boot에서는 HTML과의 유사성, 다양한 기능을 가진 Thymeleaf를 권장.

 

//text.html
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<h1>여기의 내용을 바꿀 것</h1>
</body>
</html>


// MvcController class
@Controller
public class MvcController {
    //text로 요청이 오면 text.hmtl을 반환하는 메서드
    @RequestMapping("/text")
    public String text() {
        return "text";
    }
}

 

2) 내용물 대체

 

- 요소 전체 변경 : ${data} 와 Model 활용

// text.html
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<h1 th:text="${message}">여기의 내용을 바꿀 것.</h1>

</body>
</html>

//MvcController class
@Controller
public class MvcController {
    //text로 요청이 오면 text.hmtl을 반환하는 메서드
    @RequestMapping("/text")
    public String text(Model model) {
        model.addAttribute("message", "Hello, Thymeleaf");
        return "text";
    }
}
  • th: text : HTML 요소의 내용을 전달받은 값으로 대치
  • &{data} : model에서 넘겨받은 이름이 data인 데이터로 대치.

- 요소 일부만 바꿀 때 : [[${data}]] + model 활용

// text.html
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<h1>메세지 : [[${message}]] </h1>

</body>
</html>

 

3) 객체를 이용한 표현

: 객체 클래스 생성(public 속성이거나, getter 생성) + model 활용

// Student class
public class Student {
    private String name;
    private String email;

    public Student() {
    }

    public Student(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }
}

// MvcController class
//text-object로 요청이 오면 text-object.html을 반환하는 메서드
    @RequestMapping("/text-object")
    public String textObject(Model model) {
        model.addAttribute("object", new Student("Alex", "alex@gmail.com"));
        return "text-object";
    }
    
// text-object.html
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<!--두개의 p요소로 이름과 이메일을 나타내기-->
<!--Getter가 존재하는 속성 또는 메서드 사용가능-->
<p>이름 : [[${object.getName()}]]</p>
<p>이메일 : [[${object.email}]]</p>
</body>
</html>

 

4) 조건부 표현

: th:if, th:unless 활용

// MvcController class
    @RequestMapping("/is-logged-in")
    public String isLoggedIn(Model model) {
        model.addAttribute("isLoggedIn", true);
        return "if-unless";
    }
    
// if-unless.html
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<!--로그인 여부를 model로 판단하고 보이는 문구를 바꾸는 뷰-->
<h1>Welcome.</h1>
<div th:if="${isLoggedIn}">you are logged in</div>
<div th:unless="${isLoggedIn}">please log in.</div>

</body>
</html>

 

5) 리스트 데이터 반복

: th:each 활용하여 대상 요소를 반복해서 표현

- 리스트 데이터 반복

// MvcController class
	@RequestMapping("/each")
    public String each(Model model) {
        List<String> lists = Arrays.asList("aaa", "bbb", "ccc");
        model.addAttribute("itemList", lists);
        return "each";
    }

// each.html
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>

<h1>Item List</h1>
<div>
    <p th:each="item: ${itemList}">[[${item}]]</p>
</div>
</body>
</html>

 

- 객체 데이터도 반복 가능!!

// MvcController class
    @RequestMapping("/each-student")
    public String eachStudent(Model model) {
        List<Student> studentList = Arrays.asList(
                new Student("Alex", "alex@gmail.com"),
                new Student("Brad", "brad@gmail.com"),
                new Student("Chad", "chad@gmail.com")
        );
        model.addAttribute("studentList", studentList);
        return "each-student";
    }
    
// each-student.html
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
// 학생리스트가 있으면 학생 정보 표시, 없으면 No student 표시
<div th:if="${studentList.isEmpty()}"> No student..</div>
<div th:unless="${studentList.isEmpty()}" th:each="student: ${studentList}">
    <p>이름 : [[${student.name}]]</p>
    <p>이메일 : [[${student.email}]]</p>
</div>

</body>
</html>

 

 

3. HTML Form

: 웹 브라우저에서 사용자의 입력을 받는 용도로 사용. 

 

<form> 요소와 <input> 태그.

// message.html
<h1>Send Messages</h1>
    <form action="/receive" method="post"> // http methods에 따라 보내는 요청의 목적을 나타냄.
        <label for="message"> Message:
        <input type="text" id="message" name="message"> //name 속성을 바탕으로 동작
        </label>
        <!-- 양식 제출 버튼-->
        <input type="submit" value="Send message">
    </form>

<h1>Received Messages</h1>
<form action="/receive">
    <label for="receivedMessage">
        <div id="receivedMessage" th:each="message: ${receivedMessage}">
            [[${message}]]
        </div>
    </label>
</form>

</h1>
</body>
</html>

// FormController
@Controller
public class FormController {
    // 1. 사용자에게 표기하고 싶은 메시지를 전달할 수 있는
    // html을 반환하는 메서드
    @GetMapping("/send")
    public String getSendForm() {
        return "message";
    }

    // 2. 사용자가 전달한 데이터를 처리할 수 있는 메서드
    @PostMapping("/receive")
    public String receiveMessage(
            @RequestParam("message") String message,
            Model model
    ) {
        model.addAttribute("receivedMessage", message);
        return "message";
    }
}
  • @RequestParam : 요청의 값을 메서드의 인자로 전달하는 표시.
  • action/ th:action="@{ }" : Form이 요청을 보내는 곳을 설정. (th:action은 상황에 따라 변할 수 있는 url을 사용해야 할 때)

 

728x90

BELATED ARTICLES

more