Thymeleaf를 이용해 layout 만들기

뚜우웅이·2023년 1월 31일
0

SpringBoot웹

목록 보기
2/23

프로젝트 설정

start.spring.io에서 프로젝트 설정을 해줍니다.

git 설치

git을 설치한 후에
IntelliJ VCS 메뉴에서 git을 연동하고 github에 repository를 만들어줍니다.
버전 관리가 되지 않은 파일들을 선택한 후에 커밋해줍니다.

Bootstrap 사용한 메뉴창

templates 폴더 밑에 index.html 파일을 생성한 후 BootStrap에서 가져온 코드를 붙여 넣어줍니다.

<!doctype html>
<html>
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">

    <title>Hello, world!</title>
  </head>
  <body>
    <h1>Hello, world!</h1>

    <!-- Optional JavaScript; choose one of the two! -->

    <!-- Option 1: jQuery and Bootstrap Bundle (includes Popper) -->
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-Fy6S3B9q64WdZWQUiU+q4/2Lc9npb8tCaSX9FK7E8HnRr0Jz8D6OP9dO5Vg3Q9ct" crossorigin="anonymous"></script>

    <!-- Option 2: Separate Popper and Bootstrap JS -->
    <!--
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.min.js" integrity="sha384-+sLIOodYLS7CIrQpBjl+C7nPvqq+FbNUBDunl/OZv93DB7Ln/533i8e/mZXLi/P+" crossorigin="anonymous"></script>
    -->
  </body>
</html>

아래 템플릿 사용
https://getbootstrap.com/docs/4.6/examples/starter-template/
소스코드 보기를 한 후 아래 코드를 복사하여 indexl.html body 부분에 붙여 넣어줍니다.

<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
  <a class="navbar-brand" href="#">Navbar</a>
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
  </button>

  <div class="collapse navbar-collapse" id="navbarsExampleDefault">
    <ul class="navbar-nav mr-auto">
      <li class="nav-item active">
        <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="#">Link</a>
      </li>
      <li class="nav-item">
        <a class="nav-link disabled">Disabled</a>
      </li>
      <li class="nav-item dropdown">
        <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-expanded="false">Dropdown</a>
        <div class="dropdown-menu">
          <a class="dropdown-item" href="#">Action</a>
          <a class="dropdown-item" href="#">Another action</a>
          <a class="dropdown-item" href="#">Something else here</a>
        </div>
      </li>
    </ul>
    <form class="form-inline my-2 my-lg-0">
      <input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">
      <button class="btn btn-secondary my-2 my-sm-0" type="submit">Search</button>
    </form>
  </div>
</nav>

<main role="main" class="container">

  <div class="starter-template">
    <h1>Bootstrap starter template</h1>
    <p class="lead">Use this document as a way to quickly start any new project.<br> All you get is this text and a mostly barebones HTML document.</p>
  </div>

</main>

java -> com.project.myhome 폴더 밑에 controller 패키지를 생성한 후 HomeController Class를 생성합니다.

첫 페이지는 index.html로 경로 지정없이 작성해줍니다.

package com.project.myhome.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {
    @GetMapping
    public String index(){
        return "index";
    }
}

Application 실행 후 localhost:8080를 검색창에 입력 후 들어가면 화면이 스타일이 적용 되지 않은 것을 확인할 수 있습니다.

index.html 파일에

<link href="starter-template.css" rel="stylesheet">

위의 코드를 추가한 후에 static 폴더 밑에 starter-template.css을 생성한 후 아래 코드를 넣어줍니다.

body {
  padding-top: 5rem;
}
.starter-template {
  padding: 3rem 1.5rem;
  text-align: center;
}

서식을 적용하니 제대로 나오는 것을 알 수 있습니다.

현재 static 폴더에는 css파일을, templates 폴더에는 thymeleaf파일을 넣었습니다.

index.html 상단 메뉴의 이름과 내용을 바꾸고 사용하지 않을 기능은 주석처리를 해줍니다.

<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css"
          integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
    <link href="starter-template.css" rel="stylesheet">
    <title>Hello, world!</title>
</head>
<body>
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
    <a class="navbar-brand" href="#">Spring Boot Tutorial</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault"
            aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" id="navbarsExampleDefault">
        <ul class="navbar-nav mr-auto">
            <li class="nav-item active">
                <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#">게시판</a>
            </li>
<!--            <li class="nav-item">-->
<!--                <a class="nav-link disabled">Disabled</a>-->
<!--            </li>-->
<!--            <li class="nav-item dropdown">-->
<!--                <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-expanded="false">Dropdown</a>-->
<!--                <div class="dropdown-menu">-->
<!--                    <a class="dropdown-item" href="#">Action</a>-->
<!--                    <a class="dropdown-item" href="#">Another action</a>-->
<!--                    <a class="dropdown-item" href="#">Something else here</a>-->
<!--                </div>-->
<!--            </li>-->
        </ul>
<!--        <form class="form-inline my-2 my-lg-0">-->
<!--            <input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">-->
<!--            <button class="btn btn-secondary my-2 my-sm-0" type="submit">Search</button>-->
<!--        </form>-->
    </div>
</nav>

<main role="main" class="container">

    <div class="starter-template">
        <h1>Spring Boot Tutorial</h1>
        <p class="lead">Spring Boot를 이용해 웹 페이지 제작<br>
            Spring Security, JPA를 이용해 보안 설정과 데이터 다루기.</p>
    </div>

</main>

<!-- Optional JavaScript; choose one of the two! -->

<!-- Option 1: jQuery and Bootstrap Bundle (includes Popper) -->
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"
        integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
        crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-Fy6S3B9q64WdZWQUiU+q4/2Lc9npb8tCaSX9FK7E8HnRr0Jz8D6OP9dO5Vg3Q9ct"
        crossorigin="anonymous"></script>

<!-- Option 2: Separate Popper and Bootstrap JS -->
<!--
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.min.js" integrity="sha384-+sLIOodYLS7CIrQpBjl+C7nPvqq+FbNUBDunl/OZv93DB7Ln/533i8e/mZXLi/P+" crossorigin="anonymous"></script>
-->
</body>
</html>

th:fragment -> Header, Footer, Navigation bar 같이 모든 페이지에 보여져야하는 항목인 경우 따로 분리해서 관리해줍니다.

<footer th:fragment="copy">
  &copy; 2011 The Good Thymes Virtual Grocery
</footer>
<div th:replace="footer :: copy"></div>

-> copy라는 fragment를 만든 후 footer라는 파일에 사용

templates 폴더 밑에 fragments 폴더 생성 후 common.html 파일을 생성해줍니다.
common.html 파일 밑에 thymeleaf docs에서 가져온 코드를 넣고 index.html에 있는 내부 코드를 가져와 붙여넣어줍니다.

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">
    <body>
    <nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top" th:fragment="menu">
        <a class="navbar-brand" href="#">Spring Boot Tutorial</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault"
                aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>

        <div class="collapse navbar-collapse" id="navbarsExampleDefault">
            <ul class="navbar-nav mr-auto">
                <li class="nav-item active">
                    <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">게시판</a>
                </li>
                <!--            <li class="nav-item">-->
                <!--                <a class="nav-link disabled">Disabled</a>-->
                <!--            </li>-->
                <!--            <li class="nav-item dropdown">-->
                <!--                <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-expanded="false">Dropdown</a>-->
                <!--                <div class="dropdown-menu">-->
                <!--                    <a class="dropdown-item" href="#">Action</a>-->
                <!--                    <a class="dropdown-item" href="#">Another action</a>-->
                <!--                    <a class="dropdown-item" href="#">Something else here</a>-->
                <!--                </div>-->
                <!--            </li>-->
            </ul>
            <!--        <form class="form-inline my-2 my-lg-0">-->
            <!--            <input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">-->
            <!--            <button class="btn btn-secondary my-2 my-sm-0" type="submit">Search</button>-->
            <!--        </form>-->
        </div>
    </nav>
    </body>
</html>

index.html nav에 아래 코드를 추가해줍니다.

th:replace="fragments/common :: menu"

게시판 사용을 위한 controller 생성

controller 폴더 밑에 BoardController 클래스를 생성합니다.

package com.project.myhome.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/board")
public class BoardController {
    @GetMapping("/list")
    public String list(){
        return "board/list";
    }
}

controller를 이용하여 경로를 지정해줍니다.

html 파일 생성 및 수정

templates 폴더 밑에 board 폴더를 만든 후 list.html 파일을 생성 후 title, h1 태그 수정 후 context root를 변경해줍니다.
-> context root를 변경하지 않으면 starter-template.css의 위치를 찾지 못한다.

글자 표시될 div의 class를 변경해줍니다.

<div class="container">
    <h2>게시판</h2>
</div>

localhost:8080/board/list에 들어가면 화면이 잘 나오는 것을 볼 수 있습니다.

index.html과 list.html의 head부분이 겹치기 때문에 common.html에 넣어줍니다.
title은 겹치면 안 되기 때문에 thymeleaf에 있는 매개 변수로 가져오는 코드를 작성해줍니다.

common.html

<head th:fragment="head(title)">
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css"
          integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
    <link href="starter-template.css" rel="stylesheet">
    <title th:text="${title}">Hello, world!</title>
</head>
<link href="starter-template.css" th:href="@{/starter-template.css}" rel="stylesheet">

index.html

<head th:replace="fragments/common :: head('Hello, Spring Boot!')">

list.html

<head th:replace="fragments/common :: head('게시판')">

common.html 수정
메뉴바에서 홈이나 게시판을 클릭할 시 그 부분만 active상태로 변경되게 합니다.
thymeleaf 문법에 있는 classappend 속성을 이용해줍니다.

<tr th:each="prod : ${prods}" class="row" th:classappend="${prodStat.odd}? 'odd'">

조건문이 true인 경우에만 적용됩니다.
index.html과 list.html에서 fragments를 호출하는 menu에 parameter를 사용합니다.

list.html

<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top" th:replace="fragments/common :: menu('board')">

index.html

<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top" th:replace="fragments/common :: menu('home')">

common.html

<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top" th:fragment="menu(menu)">
<li class="nav-item" th:classappend="${menu} == 'home' ? 'active'">
                    <a class="nav-link" href="#" th:href="@{/}">Home <span class="sr-only" th:if="${menu} == 'home'">(current)</span></a>
                </li>
                <li class="nav-item" th:classappend="${menu} == 'board' ? 'active'">
                    <a class="nav-link" href="#" th:href="@{/board/list}">게시판 <span class="sr-only" th:if="${menu} == 'board'">(current)</span></a>
                </li>

링크 연결과 메뉴바에 불이 잘 들어오는 것을 확인할 수 있습니다.

profile
공부하는 초보 개발자

0개의 댓글