사이먼's 코딩노트
[SpringBoot] Bootstrap 및 폰트 적용 / 공통 layout 추가 본문
[Bootstrop 및 폰트 적용]
- 기본 스타일시트를 생성하였다면, 보다 아름다운 디자인을 위해 부트스트랩을 도입해봅시다.
- 부트스트랩(Bootstrap)이란 프론트엔드 프레임워크로 웹 페이지를 쉽게 디자인할 수 있게 도와주고, 전에 포스팅을 했던 테일즈윈드 데이지UI와 같이 각종 레이아웃, 버튼, 입력창 등의 디자인을 CSS와 JavaScript로 만들어 놓은 곳이다.
- 부트스트랩은 트위터를 개발하면서 만들어졌으며, 현재 지속적으로 관리되고 있는 오픈소스 프로젝트로, 웹 디자이너의 도움 없이도 개발자 혼자서 상당히 괜찮은 수준의 웹 페이지를 만들 수 있게 도와준다는 것이 가장 큰 장점이다.
- 직접 설치해서 부트스트랩을 적용하는 방법도 있지만, cdnjs에서 부트스트랩 링크를 복사하여 코드에 직접 추가하여 부트스트랩을 사용할 예정이다.
- 부트스트랩을 통해 디자인을 적용해 볼 템플릿은 question_detail.html와 question_list.html 이다.
- 아래는 question_list.html 템플릿에 부트스트랩을 적용한 코드이다.
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css">
<div class="container my-3">
<table class="table">
<thead class="table-info">
<tr>
<th>번호</th>
<th>제목</th>
<th>작성일시</th>
</tr>
</thead>
<tbody>
<tr th:each="question, loop : ${questionList}">
<td th:text="${loop.count}"></td>
<td>
<a th:href="@{|/question/detail/${question.id}|}" th:text="${question.subject}"></a>
</td>
<td th:text="${#temporals.format(question.createDate, 'yyyy-MM-dd HH:mm')}"></td>
</tr>
</tbody>
</table>
</div>
- question_list는 현재 테이블로 구성된 총 질문 리스트를 출력하는 화면이다.
- 부트스트랩을 템플릿 상단에 link 태그를 통해 적용시켜주고 테이블 각 태그에 부트스트랩에서 제공하는 class 네임을 붙혀주게 되면 기존의 테이블 디자인보다 더 세련된 모습을 볼 수 있다.
- 아래는 부트스트랩 적용 후 변경된 localhost:8090/question/list 의 화면 디자인이다.
[폰트 적용]
- 다음은 question_detail.html에 부트스트랩을 적용하면서 폰트도 새로 적용해봅시다.
- 폰트 적용은 눈누 홈페이지를 통해 코드를 참조했다.
- 아래는 question_detail.html 템플릿에 부트스트랩과 폰트를 모두 적용한 코드이다.
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css">
<style>
@font-face {
font-family: 'Pretendard-Regular';
src: url('https://fastly.jsdelivr.net/gh/Project-Noonnu/noonfonts_2107@1.1/Pretendard-Regular.woff') format('woff');
font-weight: 400;
font-style: normal;
}
html > body {
font-family: 'Pretendard-Regular';
}
</style>
<div class="container my-3">
<!-- 질문 -->
<h2 class="border-bottom py-2" th:text="${question.subject}"></h2>
<div class="card my-3">
<div class="card-body">
<div class="card-text" style="white-space: pre-line;" th:text="${question.content}"></div>
<div class="d-flex justify-content-end">
<div class="badge bg-light text-dark p-2 text-start">
<div th:text="${#temporals.format(question.createDate, 'yyyy-MM-dd HH:mm')}"></div>
</div>
</div>
</div>
</div>
<!-- 답변의 갯수 표시 -->
<h5 class="border-bottom my-3 py-2"
th:text="|${#lists.size(question.answerList)}개의 답변이 있습니다.|"></h5>
<!-- 답변 반복 시작 -->
<div class="card my-3" th:each="answer : ${question.answerList}">
<div class="card-body">
<div class="card-text" style="white-space: pre-line;" th:text="${answer.content}"></div>
<div class="d-flex justify-content-end">
<div class="badge bg-light text-dark p-2 text-start">
<div th:text="${#temporals.format(answer.createDate, 'yyyy-MM-dd HH:mm')}"></div>
</div>
</div>
</div>
</div>
<!-- 답변 반복 끝 -->
<!-- 답변 작성 -->
<form th:action="@{|/answer/create/${question.id}|}" method="post" class="my-3">
<textarea name="content" id="content" rows="10" class="form-control"></textarea>
<input type="submit" value="답변등록" class="btn btn-primary my-2">
</form>
</div>
- 코드 상단에 link 태그를 통해 마찬가지로 부트스트랩을 적용하고 각 태그별로 부트스트랩에서 제공하는 class 네임을 붙혀주게 되면 기존보다 더 세련된 디자인의 폼 형태를 볼 수 있다.
- 폰트는 그 아래 style 태그를 통해 적용했고, html > body 라는 선택자를 통해 전체 폰트를 Pretendard-Regular로 지정하였다.
- 사실 폰트는 CSS에서 코드를 작성하지만, 지금과 같이 HTML에서 style 태그를 사용하고 그 안에 폰트를 지정해도 크게 상관없다.
- 아래는 부트스트랩 적용 후 변경된 localhost:8090/question/detail/2의 화면 디자인이다.
- 아래 표는 참고용으로 부트스트랩 클래스에 대한 몇 가지 설명을 담았다.
[공통 layout 추가 및 템플릿 상속]
- 현재까지 작성된 템플릿의 문제가 있다면 표준 HTML 구조로 작성하지 않고 <body> 태그가 있다는 가정 하에 일반 태그들을 사용하였다.
- 사실 어떤 웹 브라우저를 사용하더라도 웹 페이지가 동일하게 보이고 정상적으로 작동하게 하려면 반드시 웹 표준을 지키는 HTML 문서로 작성을 하는 것이 당연하다.
- 표준 HTML 구조를 작성하는 방법은 .html 파일을 생성한 다음 !를 작성하고 tab 버튼을 누르게 되면 자동으로 구조가 완성된다.
- 하지만 모든 템플릿에 일일이 표준 HTML 구조를 적용시키는 것은 body 아래의 요소를 제외하고는 모두 같은 내용으로 중복된다.
- 그렇게 되면 link 태그에서 CSS 파일 이름이 변경되거나 새로운 CSS 파일을 추가할 때마다 모든 템플릿을 일일이 수정해야하는 수고로움이 발생한다.
- 타임리프는 이런 중복의 불편함을 해소하기 위해템플릿 상속 기능을 제공하고, 템플릿 상속은 기본 틀이 되는 템플릿을 먼저 작성하고 다른 템플릿에서 기본 템플릿을 상속해 사용하는 방법이다.
- 먼저 기본 틀이 되는 공통 템플릿인 layout.html를 생성하여 아래와 같이 코드를 작성한다.
<!doctype html>
<html lang="ko">
<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" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css">
<!-- sbb CSS -->
<link rel="stylesheet" type="text/css" th:href="@{/style.css}">
<title>Hello, sbb!</title>
</head>
<body>
<!-- 기본 템플릿 안에 삽입될 내용 시작 -->
<th:block layout:fragment="content"></th:block>
<!-- 기본 템플릿 안에 삽입될 내용 끝 -->
</body>
</html>
- 모든 템플릿이 상속해야하는 layout.html이기 때문에 <head> 태그 아래에 라이브러리나 프레임워크, 기본 스타일시트를 모두 이 곳에서 link 시켜주는 것이 좋다.
- <body> 태그 안의 <th:block layout:fragment="content"></th:block>은 layout.html을 상속한 템플릿에서 개별적으로 구현해야 하는영역이 된다. 즉, layout.html 템플릿을 상속하면 해당 부분만 수정해도 표준 HTML 문서로 작성이 되는것이다.
- 더 쉽게 말하자면 layout.html를 상속받는 다른 템플릿에서는 HTML 구조를 쓰지 않아도 fragment="content" 라는 속성값만 가지고 있으면 layout.html을 그대로 가져와 쓰는 것이랑 똑같다.
[공통 템플릿 상속받기]
- 공통 템플릿인 layout.html를 각 템플릿에 상속하는 방법은 동일한데, 대표적으로 question_list.html에 추가된 모습을 아래 코드를 통해 살펴봅시다.
<html layout:decorate="~{layout}">
<div layout:fragment="content" class="container my-3">
(내용 생략...)
</div>
</html>
- layout.html을 상속하려고 <html layout:decorate="~{layout}">을 작성하였다.
- 타임리프의 layout:decorate 속성은 템플릿의 레이아웃으로 사용할 템플릿을 설정한다. 속성값인 ~{layout}이 바로 위에서 만들어놨던 layout.html 파일을 의미한다.
- 그리고 부모인 layout.html에서 작성했던 layout:fragment="content"도 자식 템플릿의내용으로 적용될 수 있게 사용하였다.
- 다시 로컬 서버를 실행하여 list나 detail 페이지에 접속하게 되면 디자인이 바뀌거나 그렇지 않지만 F12를 통해 확인해보면 기존엔 적용하지 않았던 HTML 구조를 확인할 수 있다.
반응형
'Java > SpringBoot' 카테고리의 다른 글
[SpringBoot] 유효성 체크 및 에러 처리(2) (0) | 2024.05.12 |
---|---|
[SpringBoot] Question 등록 / 유효성 체크 및 에러 처리(1) (0) | 2024.05.12 |
[SpringBoot] Answer 저장 및 출력 / 스타일시트 적용 (0) | 2024.05.09 |
[SpringBoot] Prefix 적용 / Form 만들기 (0) | 2024.05.09 |
[SpringBoot] Detail 링크 및 페이지 추가 / 예외처리 (0) | 2024.05.09 |