<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<header class="p-3 bg-dark text-white">
<div class="container">
<div class="d-flex flex-wrap align-items-center justify-content-lg-start">
<ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0">
<li><a id="home" href="#" class="nav-link px-2 text-secondary">Home</a></li>
<li><a id="hashtag" href="#" class="nav-link px-2 text-secondary">HashTags</a></li>
</ul>
<div class="text-end">
<span id="username" class="text-white me-2">username</span>
<a role="button" id="login" type="button" class="btn btn-outline-light me-2">Login</a>
<a role="button" id="logout" type="button" class="btn btn-outline-light me-2">Logout</a>
</div>
</div>
</div>
</header>
</body>
</html>
❗❗html과 th.xml 이름을 꼭 맞춰줘야한다!❗❗
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="Ryuzy">
<title>게시판 페이지</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
<link href="/css/search-bar.css" rel="stylesheet">
<link href="/css/table-header.css" rel="stylesheet">
</head>
<body>
<header id="header">
헤더 삽입
<hr>
</header>
<main class="container">
<div class="row">
<div class="card card-margin search-form">
<div class="card-body p-0">
<form method="get" id="search-form">
<div class="row">
<div class="col-12">
<div class="row no-gutters">
<div class="col-lg-3 col-md-3 col-sm-12 p-0">
<label for="search-type" hidden>검색 유형</label>
<select class="form-control" id="search-type" name="searchType">
<option>제목</option>
<option>본문</option>
<option>id</option>
<option>닉네임</option>
<option>해시태그</option>
</select>
</div>
<div class="col-lg-8 col-md-6 col-sm-12 p-0">
<label for="search-value" hidden>검색어</label>
<input type="text" placeholder="검색어..." class="form-control" id="search-value" name="searchValue">
</div>
<div class="col-lg-1 col-md-3 col-sm-12 p-0">
<button type="submit" class="btn btn-base">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-search">
<circle cx="11" cy="11" r="8"></circle>
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</svg>
</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="row">
<table class="table" id="article-table">
<thead>
<tr>
<th class="title col-6"><a>제목</a></th>
<th class="hashtag col-2"><a>해시태그</a></th>
<th class="user-id"><a>작성자</a></th>
<th class="created-at"><a>작성일</a></th>
</tr>
</thead>
<tbody>
<tr>
<td class="title"><a>첫글</a></td>
<td class="hashtag">#java</td>
<td class="user-id">김사과</td>
<td class="created-at"><time>2023-01-03</time></td>
</tr>
<tr>
<td>두번째글</td>
<td>#spring</td>
<td>김사과</td>
<td>2023-01-03</td>
</tr>
<tr>
<td>세번째글</td>
<td>#java</td>
<td>김사과</td>
<td>2023-01-03</td>
</tr>
</tbody>
</table>
</div>
<div class="row">
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
<a class="btn btn-primary me-md-2" role="button" id="write-article">글쓰기</a>
</div>
</div>
<div class="row">
<nav id="pagination" aria-label="Page navigation example">
<ul class="pagination justify-content-center">
<li class="page-item"><a class="page-link" href="#">Previous</a></li>
<li class="page-item"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">Next</a></li>
</ul>
</nav>
</div>
</main>
<footer id="footer">
<hr>
푸터 삽입
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"></script>
</body>
</html>
sel로 태그를 선택하여 다양한 기능을 넣을 수 있다.
<?xml version="1.0"?>
<thlogic>
<attr sel="#header" th:replace="header :: header"/>
<attr sel="#footer" th:replace="footer :: footer"/>
<attr sel="main" th:object="${articles}">
<attr sel="#search-form" th:action="@{/articles}" th:method="get"/>
<attr sel="#search-type" th:remove="all-but-first">
<attr sel="option[0]"
th:each="searchType : ${searchTypes}"
th:value="${searchType.name}"
th:text="${searchType.description}"
th:selected="${param.searchType != null && (param.searchType.toString == searchType.name)}"/>
</attr>
<attr sel="#search-value" th:value="${param.searchValue}"/>
<attr sel="#article-table">
<attr sel="thead/tr">
<attr sel="th.title/a" th:text="'제목'" th:href="@{/articles(
page=${articles.number},
sort='title' + (*{sort.getOrderFor('title')} != null ? (*{sort.getOrderFor('title').direction.name} != 'DESC' ? ',desc' : '') : ''),
searchType=${param.searchType},
searchValue=${param.searchValue}
)}"/>
<attr sel="th.hashtag/a" th:text="'해시태그'" th:href="@{/articles(
page=${articles.number},
sort='hashtag' + (*{sort.getOrderFor('hashtag')} != null ? (*{sort.getOrderFor('hashtag').direction.name} != 'DESC' ? ',desc' : '') : ''),
searchType=${param.searchType},
searchValue=${param.searchValue}
)}"/>
<attr sel="th.user-id/a" th:text="'작성자'" th:href="@{/articles(
page=${articles.number},
sort='userAccount.userId' + (*{sort.getOrderFor('userAccount.userId')} != null ? (*{sort.getOrderFor('userAccount.userId').direction.name} != 'DESC' ? ',desc' : '') : ''),
searchType=${param.searchType},
searchValue=${param.searchValue}
)}"/>
<attr sel="th.created-at/a" th:text="'작성일'" th:href="@{/articles(
page=${articles.number},
sort='createdAt' + (*{sort.getOrderFor('createdAt')} != null ? (*{sort.getOrderFor('createdAt').direction.name} != 'DESC' ? ',desc' : '') : ''),
searchType=${param.searchType},
searchValue=${param.searchValue}
)}"/>
</attr>
<attr sel="tbody" th:remove="all-but-first">
<attr sel="tr[0]" th:each="article : ${articles}">
<attr sel="td.title/a" th:text="${article.title}" th:href="@{'/articles/'+${article.id}}"/>
<attr sel="td.hashtag" th:text="${article.hashtag}"/>
<attr sel="td.user-id" th:text="${article.nickname}"/>
<attr sel="td.created-at/time" th:datetime="${article.createdAt}" th:text="${#temporals.format(article.createdAt, 'yyyy-MM-dd')}"/>
</attr>
</attr>
</attr>
</attr>
<attr sel="#write-article" sec:authorize="isAuthenticated()" th:href="@{/articles/form}"/>
<attr sel="#pagination">
<attr sel="li[0]/a" th:text="'previous'"
th:href="@{/articles(page=${articles.number - 1}, searchType=${param.searchType}, searchValue=${param.searchValue})}"
th:class="'page-link' + (${articles.number} <= 0 ? ' disabled':'')" />
<attr sel="li[1]" th:class="page-item" th:each="pageNumber : ${paginationBarNumbers}">
<attr sel="a" th:text="${pageNumber + 1}" th:href="@{/articles(page=${pageNumber}, searchType=${param.searchType}, searchValue=${param.searchValue})}"
th:class="'page-link' + (${pageNumber} == ${articles.number} ? ' disabled' : '')"/>
</attr>
<attr sel="li[2]/a" th:text="'next'"
th:href="@{/articles(page=${articles.number + 1}, searchType=${param.searchType}, searchValue=${param.searchValue})}"
th:class="'page-link' + (${articles.number} >= ${articles.totalPages - 1} ? ' disabled':'')" />
</attr>
</thlogic>
이 코드는 Thymeleaf 템플릿 엔진을 사용한 HTML 코드입니다. Thymeleaf는 서버 측에서 실행되며, HTML 페이지를 생성하는 데 사용됩니다. Thymeleaf 템플릿 엔진을 사용하여 HTML을 동적으로 렌더링합니다.
는 XML 형식의 문서임을 나타내는 선언부입니다.< thlogic> 태그는 Thymeleaf에서 사용되는 커스텀 태그로, Thymeleaf와 함께 사용될 때만 해석되고, 일반 HTML에서는 무시됩니다.
< attr> 태그는 Thymeleaf에서 사용되는 커스텀 태그로, HTML 태그의 속성(attribute) 값을 바인딩합니다.
th:replace 속성은 해당 태그의 내용을 다른 태그로 대체합니다. 위 코드에서는 #header와 #footer라는 ID를 가진 태그를 header :: header와 footer :: footer로 대체하는 것을 의미합니다. 이는 header.html과 footer.html 파일에서 정의된 코드를 가져와 해당 위치에 삽입하는 역할을 합니다.
th:object 속성은 데이터 객체를 지정하는 역할을 합니다. 위 코드에서는 articles라는 객체를 main 태그에 지정합니다. 이를 통해 articles 객체의 데이터를 main 태그 안에서 참조할 수 있습니다.
th:each 속성은 반복문을 실행합니다. 위 코드에서는 #search-type 태그 안에 있는 option[0] 태그를 찾아 각각의 searchTypes 객체의 name 속성과 description 속성 값을 바인딩하고, selected 속성은 param.searchType이 null이 아니면서 param.searchType.toString이 searchType.name과 같으면 true가 되도록 설정합니다.
th:value 속성은 해당 태그의 값(value)을 바인딩합니다. 위 코드에서는 #search-value 태그에 param.searchValue 값을 설정합니다.
th:text 속성은 해당 태그의 텍스트(text)를 바인딩합니다. 위 코드에서는 th:text 속성 안에 표현식(expression)을 사용하여 article 객체의 title, hashtag, nickname, createdAt 속성 값을 텍스트로 설정합니다.
th:href 속성은 해당 태그의 링크 주소(href)를 바인딩합니다. 위 코드에서는 th:href 속성 안에 표현식을 사용하여 page, sort, searchType, searchValue 등의 값을 포함한 주소를 설정합니다.
th:datetime 속성은 해당 태그의 날짜/시간(datetime) 값을 바인딩합니다. 위 코드에서는 article.createdAt 값을 th:datetime 속성에 바인딩하고, th:text 속성 안에 표현식을 사용하여 yyyy-MM-dd 형식으로 포맷팅한 값을 텍스트로 설정합니다.
위의 코드로 다음의 기능을 구현한 것입니다!
1. 검색기능
2. sort기능
3. 페이징기능
4. header/footer 붙여 넣는 기능
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="Ryuzy">
<title>게시글 페이지</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
</head>
<body>
<header id="header">
헤더 삽입
<hr>
</header>
<main id="article-main" class="container">
<header id="article-header" class="py-5 text-center">
<h1>첫번째 글</h1>
</header>
<div class="row g-5">
<section class="col-md-3 col-lg-4 order-md-last">
<aside>
<p><span id="nickname" class="nick-name">김사과</span></p>
<p><a id="email" class="u-url" rel="me" href="mailto:apple@apple.com">apple@apple.com</a></p>
<p><time id="created-at" datetime="2022-01-03T00:00:00">2023-01-03</time></p>
<p><span id="hashtag">#java</span></p>
</aside>
</section>
<article id="article-content" class="col-md-9 col-lg-8">
<pre>본문<br><br></pre>
</article>
</div>
<div class="row g-5" id="article-buttons">
<form id="delete-article-form">
<div class="pb-5 d-grid gap-2 d-md block">
<a class="btn btn-success me-md-2" role="button" id="update-article">수정</a>
<button class="btn btn-danger me-md-2" type="submit">삭제</button>
</div>
</form>
</div>
<div class="row g-5">
<section>
<form class="row g-3" id="comment-form">
<input type="hidden" class="article-id">
<div class="col-md-9 col-lg-8">
<label for="comment-textbox" hidden>댓글</label>
<textarea class="form-control" id="comment-textbox" placeholder="댓글 쓰기.." rows="3" required></textarea>
</div>
<div class="col-md-3 col-lg-4">
<label for="comment-submit" hidden>댓글 쓰기</label>
<button class="btn btn-primary" id="comment-submit" type="submit">쓰기</button>
</div>
</form>
<ul id="article-comments" class="row col-md-10 col-lg-8 pt-3">
<li>
<form class="comment-form">
<input type="hidden" class="article-id">
<div class="row">
<div class="col-md-10 col-lg-9">
<strong>김사과</strong>
<time><small>2023-01-03</small></time>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.<br>
Lorem ipsum dolor sit amet
</p>
</div>
<div class="col-2 mb-3">
<button type="submit" class="btn btn-outline-danger" id="delete-comment-button">삭제</button>
</div>
</div>
</form>
</li>
<li>
<div>
<strong>김사과</strong>
<time><small>2023-01-03</small></time>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.<br>
Lorem ipsum dolor sit amet
</p>
</div>
</li>
</ul>
</section>
</div>
<div class="row g-5">
<nav id="pagination" aria-label="Page navigation">
<ul class="pagination">
<li class="page-item">
<a class="page-link" href="#" aria-label="Previous">
<span aria-hidden="true">« prev</span>
</a>
</li>
<li class="page-item">
<a class="page-link" href="#" aria-label="Next">
<span aria-hidden="true">next »</span>
</a>
</li>
</ul>
</nav>
</div>
</main>
<footer id="footer">
푸터 삽입
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"></script>
</body>
</html>
<?xml version="1.0"?>
<thlogic>
<attr sel="#header" th:replace="header :: header"/>
<attr sel="#footer" th:replace="footer :: footer"/>
<attr sel="#article-main" th:object="${article}">
<attr sel="#article-header/h1" th:text="*{title}"/>
<attr sel="#nickname" th:text="*{nickname}"/>
<attr sel="#email" th:text="*{email}"/>
<attr sel="#created-at" th:datetime="*{createdAt}" th:text="*{#temporals.format(createdAt, 'yyyy-MM-dd HH:mm:ss')}"/>
<attr sel="#hashtag" th:text="*{hashtag}"/>
<attr sel="#article-content/pre" th:text="*{content}"/>
</attr>
<attr sel="article-buttons">
<attr sel="#delete-article-form" th:action="'/articles/' + *{id} + '/delete'" th:method="post">
<attr sel="#update-article" th:href="'/articles/' + *{id} + '/form'"/>
</attr>
</attr>
<attr sel=".article-id" th:name="articleId" th:value="*{id}"/>
<attr sel="#comment-form" th:action="@{/comments/new}" th:method="post">
<attr sel="#comment-textbox" th:name="content"/>
</attr>
<attr sel="#article-comments" th:remove="all-but-first">
<attr sel="li[0]" th:each="articleComment : ${articleComments}">
<attr sel="div/strong" th:text="${articleComment.nickname}"/>
<attr sel="div/small/time" th:datetime="${articleComment.createdAt}" th:text="${#temporals.format(articleComment.createdAt, 'yyyy-MM-dd HH:mm:ss')}"/>
<attr sel="div/p" th:text="${articleComment.content}"/>
<attr sel="form" th:action="'/comments/' + ${articleComment.id} + '/delete'" th:method="post">
<attr sel="div/strong" th:text="${articleComment.nickname}"/>
<attr sel="div/small/time" th:datetime="${articleComment.createdAt}" th:text="${#temporals.format(articleComment.createdAt, 'yyyy-MM-dd HH:mm:ss')}"/>
<attr sel="div/p" th:text="${articleComment.content}"/>
</attr>
</attr>
</attr>
<attr sel="#pagination">
<attr sel="li[0]/a" th:href="*{id} - 1 <= 0 ? '#' : |/articles/*{id - 1}|"
th:class="'page-link' + (*{id} - 1 <= 0 ? ' disabled' : '')"/>
<attr sel="li[1]/a" th:href="*{id} + 1 > ${totalCount} ? '#' : |/articles/*{id + 1}|"
th:class="'page-link' + (*{id} + 1 > ${totalCount} ? ' disabled' : '')"/>
</attr>
</thlogic>
th:replace : 대상 요소를 다른 페이지 또는 fragment로 대체합니다.
th:object : 객체를 현재 태그의 컨텍스트 객체로 설정합니다.
th:text : 요소의 텍스트 값을 설정합니다.
th:datetime : 날짜 및 시간 값을 설정합니다.
th:name : 폼(form) 데이터의 이름을 설정합니다.
th:value : 폼(form) 데이터의 값(value)을 설정합니다.
th:each : 반복문(loop)을 생성합니다.
th:action : 폼(form) 데이터의 전송 대상 URL을 설정합니다.
th:href : 링크의 대상 URL을 설정합니다.
th:remove : 요소를 제거합니다.
삼항연산자를 이용하여 다양하게 구현할 수 있습니다.
삼항 연산자
(조건) ? (참일 때 값) : (거짓일 때 값)
결과값: true or false
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="Ryuzy">
<title>게시판 페이지</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
<link href="/css/search-bar.css" rel="stylesheet">
<link href="/css/table-header.css" rel="stylesheet">
</head>
<body>
<header id="header">
헤더 삽입
<hr>
</header>
<div class="container">
<header id="article-form-header" class="py-5 text-center">
<h1>게시글 작성</h1>
</header>
<form id="article-form">
<div class="row mb-3 justify-content-md-center">
<label for="title" class="col-sm-2 col-lg-1 col-form-label text-sm-end">제목</label>
<div class="col-sm-8 col-lg-9">
<input type="text" class="form-control" id="title" name="title" required>
</div>
</div>
<div class="row mb-3 justify-content-md-center">
<label for="content" class="col-sm-2 col-lg-1 col-form-label text-sm-end">본문</label>
<div class="col-sm-8 col-lg-9">
<textarea class="form-control" id="content" name="content" rows="5" required></textarea>
</div>
</div>
<div class="row mb-4 justify-content-md-center">
<label for="hashtag" class="col-sm-2 col-lg-1 col-form-label text-sm-end">해시태그</label>
<div class="col-sm-8 col-lg-9">
<input type="text" class="form-control" id="hashtag" name="hashtag">
</div>
</div>
<div class="row mb-5 justify-content-md-center">
<div class="col-sm-10 d-grid gap-2 d-sm-flex justify-content-sm-end">
<button type="submit" class="btn btn-primary" id="submit-button">저장</button>
<button type="button" class="btn btn-secondary" id="cancel-button">취소</button>
</div>
</div>
</form>
</div>
<footer id="footer">
푸터 삽입
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"></script>
</body>
</html>
<?xml version="1.0"?>
<thlogic>
<attr sel="#header" th:replace="header :: header"/>
<attr sel="#footer" th:replace="footer :: footer"/>
<attr sel="#article-form-header/h1" th:text="${formStatus} ? '게시글 ' + ${formStatus.description} : _"/>
<attr sel="#article-form" th:action="${formStatus?.update} ? '/articles/' + ${article.id} + '/form' : '/articles/form'" th:method="post">
<attr sel="#title" th:value="${article?.title} ?: _"/>
<attr sel="#content" th:value="${article?.content} ?: _"/>
<attr sel="#hashtag" th:value="${article?.hashtag} ?: _"/>
<attr sel="#submit-button" th:text="${formStatus?.description} ?: _"/>
<attr sel="#cancel-button" th:onclick="'history.back()'"/>
</attr>
</thlogic>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="Ryuzy">
<title>게시판 페이지</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
<link href="/css/search-bar.css" rel="stylesheet">
<link href="/css/table-header.css" rel="stylesheet">
</head>
<body>
<header id="header">
헤더 삽입
<hr>
</header>
<main class="container">
<header class="py-5 text-center">
<h1>해시태그</h1>
</header>
<section class="row">
<div id="hashtags" class="col-9 d-flex flex-wrap justify-content-evenly">
<div class="p-2">
<h2 class="text-center 1h-lg font-monospace">
<a href="#">#java</a>
</h2>
</div>
</div>
</section>
<hr>
<table class="table" id="article-table">
<thead>
<tr>
<th class="title col-6"><a>제목</a></th>
<th class="hashtag col-2"><a>해시태그</a></th>
<th class="user-id col"><a>작성자</a></th>
<th class="created-at col"><a>작성일</a></th>
</tr>
</thead>
<tbody>
<tr>
<td class="title"><a>첫글</a></td>
<td class="hashtag">#java</td>
<td class="user-id">김사과</td>
<td class="created-at"><time>2023-01-03</time></td>
</tr>
<tr>
<td>두번째글</td>
<td>#spring</td>
<td>김사과</td>
<td>2023-01-03</td>
</tr>
<tr>
<td>세번째글</td>
<td>#java</td>
<td>김사과</td>
<td>2023-01-03</td>
</tr>
</tbody>
</table>
<nav id="pagination" aria-label="Page navigation example">
<ul class="pagination justify-content-center">
<li class="page-item"><a class="page-link" href="#">Previous</a></li>
<li class="page-item"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">Next</a></li>
</ul>
</nav>
</main>
<footer id="footer">
<hr>
푸터 삽입
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"></script>
</body>
</html>
<?xml version="1.0"?>
<thlogic>
<attr sel="#header" th:replace="header :: header"/>
<attr sel="#footer" th:replace="footer :: footer"/>
<attr sel="main" th:object="${articles}">
<attr sel="#hashtags" th:remove="all-but-first">
<attr sel="div" th:each="hashtag : ${hashtags}">
<attr sel="a" th:class="'text-reset'" th:text="${hashtag}" th:href="@{/articles/search-hashtag(
page=${param.page},
sort=${param.sort},
searchValue=${hashtag}
)}"/>
</attr>
</attr>
<attr sel="#article-table">
<attr sel="thead/tr">
<attr sel="th.title/a" th:text="'제목'" th:href="@{/articles(
page=${articles.number},
sort='title' + (*{sort.getOrderFor('title')} != null ? (*{sort.getOrderFor('title').direction.name} != 'DESC' ? ',desc' : '') : ''),
searchType=${param.searchType},
searchValue=${param.searchValue}
)}"/>
<attr sel="th.hashtag/a" th:text="'해시태그'" th:href="@{/articles(
page=${articles.number},
sort='hashtag' + (*{sort.getOrderFor('hashtag')} != null ? (*{sort.getOrderFor('hashtag').direction.name} != 'DESC' ? ',desc' : '') : ''),
searchType=${param.searchType},
searchValue=${param.searchValue}
)}"/>
<attr sel="th.user-id/a" th:text="'작성자'" th:href="@{/articles(
page=${articles.number},
sort='userAccount.userId' + (*{sort.getOrderFor('userAccount.userId')} != null ? (*{sort.getOrderFor('userAccount.userId').direction.name} != 'DESC' ? ',desc' : '') : ''),
searchType=${param.searchType},
searchValue=${param.searchValue}
)}"/>
<attr sel="th.created-at/a" th:text="'작성일'" th:href="@{/articles(
page=${articles.number},
sort='createdAt' + (*{sort.getOrderFor('createdAt')} != null ? (*{sort.getOrderFor('createdAt').direction.name} != 'DESC' ? ',desc' : '') : ''),
searchType=${param.searchType},
searchValue=${param.searchValue}
)}"/>
</attr>
<attr sel="tbody" th:remove="all-but-first">
<attr sel="tr[0]" th:each="article : ${articles}">
<attr sel="td.title/a" th:text="${article.title}" th:href="@{'/articles/'+${article.id}}"/>
<attr sel="td.hashtag" th:text="${article.hashtag}"/>
<attr sel="td.user-id" th:text="${article.nickname}"/>
<attr sel="td.created-at/time" th:datetime="${article.createdAt}" th:text="${#temporals.format(article.createdAt, 'yyyy-MM-dd')}"/>
</attr>
</attr>
</attr>
</attr>
<attr sel="#pagination">
<attr sel="li[0]/a" th:text="'previous'"
th:href="@{/articles(page=${articles.number - 1}, searchType=${param.searchType}, searchValue=${param.searchValue})}"
th:class="'page-link' + (${articles.number} <= 0 ? ' disabled':'')" />
<attr sel="li[1]" th:class="page-item" th:each="pageNumber : ${paginationBarNumbers}">
<attr sel="a" th:text="${pageNumber + 1}" th:href="@{/articles(page=${pageNumber}, searchType=${param.searchType}, searchValue=${param.searchValue})}"
th:class="'page-link' + (${pageNumber} == ${articles.number} ? ' disabled' : '')"/>
</attr>
<attr sel="li[2]/a" th:text="'next'"
th:href="@{/articles(page=${articles.number + 1}, searchType=${param.searchType}, searchValue=${param.searchValue})}"
th:class="'page-link' + (${articles.number} >= ${articles.totalPages - 1} ? ' disabled':'')" />
</attr>
</thlogic>