[Spring] MVC 패턴 웹 페이지 3 - HTML, 그리고 Thymeleaf

2025. 6. 25. 12:43·Project/Web

HTML , CSS는 잘 몰라서 부트스트랩 프레임워크의 힘을 빌렸다..

스프링 부트에서 정적 리소스로 쓰기 위해 부트 스트랩으로 받은 파일들은 /resources/static 영역에 모아 두었다.


컨트롤러 생성

이제 컨트롤러와 뷰 템플릿을 구현 한다.

package hello.itemservice.web.basic;

import hello.itemservice.domain.item.Item;
import hello.itemservice.domain.item.ItemRepository;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import java.util.List;

@Controller
@RequestMapping("/basic/items")
@RequiredArgsConstructor // 생성자 만들어줌 + 생성자가 하나니까 AutoWired도 발생
public class BasicItemController {

    private final ItemRepository itemRepository;

    @GetMapping
    public String items(Model model) {
        List<Item> items = itemRepository.findAll();
        model.addAttribute("items", items);
        return "basic/items";
    }

    // 테스트용 데이터
    @PostConstruct
    public void init() {

        itemRepository.save(new Item("itemA", 10000, 10));
        itemRepository.save(new Item("itemB", 20000, 20));
        itemRepository.save(new Item("itemC", 30000, 30));
    }
}

먼저 itemRepository에 있는 모든 상품을 조회하고 모델에 담는다. 그리고 뷰 템플릿을 호출한다.

클래스 레벨에  @RequiredArgsConstructor을 붙여줌으로써 itemRepository의 생성자와 @Autowired 생략 가능

또한, 테스트용 데이터를 넣기 위해 @PostConstruct 메서드를 사용했다.


Thymeleaf 로 뷰 템플릿 생성

타임리프는 정적인 HTML을 동적으로 사용할 수 있게 해주는 템플릿 엔진이다.

본격적으로 타임리프를 사용하기 전에 항상 타임리프 사용 선언을 다음과 같이 해줘야 한다

<html xmlns:th="http://www.thymeleaf.org">

 

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org"> // 여기서 선언
<head>
    <meta charset="utf-8">
    <link th:href="@{/css/bootstrap.min.css}"
            href="../css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container" style="max-width: 600px">
    <div class="py-5 text-center">
        <h2>상품 목록</h2>
    </div>
    <div class="row">
        <div class="col">
            <button class="btn btn-primary float-end"
                    onclick="location.href='addForm.html'"
                    th:onclick="|location.href='@{/basic/items/add}'|"
                    type="button">상품 등록
            </button>
        </div>
    </div>
    <hr class="my-4">
    <div>
        <table class="table">
            <thead>
            <tr>
                <th>ID</th>
                <th>상품명</th>
                <th>가격</th>
                <th>수량</th>
            </tr>
            </thead>
            <tbody>
            <tr th:each="item : ${items}">
                <td><a href="item.html" th:href="@{/basic/items/{itemId}(itemId=${item.id})}" th:text="${item.id}">회원id</a></td>
                <td><a href="item.html" th:href="@{/basic/items/{itemId}(itemId=${item.id})}" th:text="${item.itemName}">상품명</a></td>
                <td th:text="${item.price}">10000</td>
                <td th:text="${item.quantity}">10</td>
            </tr>
            </tbody>
        </table>
    </div>

</div> <!-- /container -->

</body>
</html>

 

속성 변경

<link th:href="@{/css/bootstrap.min.css}"
            href="../css/bootstrap.min.css" rel="stylesheet">

먼저 상대경로 였던 기존 HTML 코드를 뷰템플릿으로 렌더링 후에 절대 경로로 수정해주었다.

이게 어떤 문법이냐면, 기존의 href 태그의 값이 뷰 템플릿을 거치게 되면 th:href 의 값으로 대체가 된다. 다음 예시도 보자.

<button class="btn btn-primary float-end"
        onclick="location.href='addForm.html'"
        th:onclick="|location.href='@{/basic/items/add}'|"
        type="button">상품 등록
</button>

기존의 onclick 주소를 th: 태그를 통해 설정한 주소로 이동하도록 속성을 변경할 수 있다.

이처럼 타임리프의 핵심은 th:xxx 가 붙은 부분이 뷰 템플릿을 거치게 되면 기존 HTML 부분을 대체 한다는 점이다. 이게 왜 핵심이냐면, 만약 th:xxx 부분을 빼먹거나 실수해도 웹 브라우저가 해당 태그 부분을 무시하고 정상적으로 화면을 그려줄 수 있기 때문에 오류가 발생하지 않는다는 점이다.

 

다른 타임 리프 문법이 있는데요..

@{} : 링크 표현식

|...| : 리터럴 대체 : || 안에 문자를 모두 문자열로 인식하여 문자열 익스케이프 처리를 따로 하지 않아도 된다.

th:each: for-each문과 생긴 것도 비슷하다.

<tr th:each="item : ${items}">
    <td><a href="item.html" th:href="@{/basic/items/{itemId}(itemId=${item.id})}" th:text="${item.id}">회원id</a></td>
    <td><a href="item.html" th:href="@{/basic/items/{itemId}(itemId=${item.id})}" th:text="${item.itemName}">상품명</a></td>
    <td th:text="${item.price}">10000</td>
    <td th:text="${item.quantity}">10</td>
</tr>

${} : 변수 표현식 : 모델의 값이나 타임리프 변수로 선언된 값을 조회 가능


Natural Templates

JSP는 HTML과 자바코드를 섞어 작성한 파일이므로 순수 HTML로 열어보면 더이상 열 수 없는 파일이 된다.

하지만 타임리프는 확장자명에서도 보이듯이 HTML 포맷을 유지한다. 

즉, HTML을 그대로 유지하면서 뷰 템플릿도 사용할 수 있는 타임리프의 특징을 네추럴 템플릿 이라고 한다.

'Project > Web' 카테고리의 다른 글

[Spring] MVC 패턴 웹 페이지 6 - 상품 등록 처리하기 @ModelAttribute  (0) 2025.06.25
[Spring] MVC 패턴 웹 페이지 5 - 상품 등록 폼  (0) 2025.06.25
[Spring] MVC 패턴 웹 페이지 4 - 상품 상세 페이지  (0) 2025.06.25
[Spring] MVC 패턴 웹 페이지 2 - 상품 도메인, 저장소 구현  (0) 2025.06.24
[Spring] MVC 패턴 웹 페이지 1 - 요구사항 분석  (0) 2025.06.24
'Project/Web' 카테고리의 다른 글
  • [Spring] MVC 패턴 웹 페이지 5 - 상품 등록 폼
  • [Spring] MVC 패턴 웹 페이지 4 - 상품 상세 페이지
  • [Spring] MVC 패턴 웹 페이지 2 - 상품 도메인, 저장소 구현
  • [Spring] MVC 패턴 웹 페이지 1 - 요구사항 분석
xuv2
xuv2
집에 가고 싶다
  • xuv2
    xuvlog
    xuv2
  • 전체
    오늘
    어제
    • 전체 글 모아보기 (170) N
      • 잡담 (9)
      • 도전 , 자격증 (2)
      • Error (5)
      • Java (23)
      • Spring (39) N
        • Core (10)
        • MVC (20)
        • Thymeleaf (9) N
      • DataBase (6)
        • Database Modeling (4)
        • SQL (2)
      • HTTP (11)
      • Network (17)
      • Software Engineering (3)
      • Operating System (3)
      • Algorithm (16)
      • Project (18)
        • Web (9)
        • iOS (8)
        • Python (1)
      • A.I (13)
      • Linux (5)
  • 블로그 메뉴

    • 홈
  • 링크

    • Github
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
xuv2
[Spring] MVC 패턴 웹 페이지 3 - HTML, 그리고 Thymeleaf
상단으로

티스토리툴바