템플릿 상속

suhan cho·2022년 7월 20일
0
  • HTML 구조에 맞게 수정하면 템플릿 파일들의 body 엘리먼트 바깥 부분(head 엘리먼트 등)은 모두 같은 내용으로 중복된다.

layout.html

<!doctype html>
<html lang="ko" xmlns:layout="">
<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" th:href="@{/bootstrap.min.css}">
  <!-- sbb CSS -->
  <link rel="stylesheet" type="text/css" th:href="@{/style.css}">
  <title>Hello, sbb!</title>
</head>
<body>
<!-- 기본 템플릿 안에 삽입될 내용 Start -->
<th:block layout:fragment="content"></th:block>
<!-- 기본 템플릿 안에 삽입될 내용 End -->
</body>
</html>
  • layout.html템플릿은 모든 템플릿이 상속해야 하는 템플릿으로 html문서의 기본 틀이된다.
  • <th:block layout:fragment="content"></th:block>영역에 해당되는 부분만 작성해도 표준 HTML문서가 된다.

question_list.html

<html layout:decorate="~{layout}" xmlns:layout="http://www.w3.org/1999/xhtml">
<div layout:fragment="content" class="container my-3">
    <table class="table">
        <thead class="table-dark">
        <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>
</html>
  • 기존의 스타일 링크를 삭제
  • 상속을 위해 사용
  • ~{layout}은 layout.html파일을 의미
  • 부모 템플릿의 위 부분을 자식 템플릿의 내용으로 바꾸기 위해
<div layout:fragment="content" class="container my-3">
    (... 생략 ...)
</div>
  • 위와 같이 바꾸면 부모 템플릿의 th:block엘리먼트의 내용이 자식 템플릿의 div엘리먼트의 내용으로 교체

질문 등록

<a th:href="@{/question/create}" class="btn-primary">질문 등록하기</a>
  • question_list에 위 코드를 추가
  • a태그지만 부트스트랩 btn사용
@GetMapping("/crate")
    public String questionCreate(){
        return "question_form";
    }
  • questioncontroller에 create메핑 question_form템플릿에 렌더링

question_form.html

<html layout:decorate="~{layout}">
<div layout:fragment="content" class="container">
    <h5 class="my-3 border-bottom pb-2">질문등록</h5>
    <form th:action="@{/question/create}" method="post">
        <div class="mb-3">
            <label for="subject" class="form-label">제목</label>
            <input type="text" name="subject" class="form-control">
        </div>
        <div class="mb-3">
            <label for="content" class="form-label">내용</label>
            <textarea name="content" class="form-control" rows="10"></textarea>
        </div>
        <input type="submit" value="저장하기" class="btn btn-primary my-2">
    </form>
</div>
</html>

Post전송 할 수 있도록 수정

@PostMapping("/create")
    public String questionCreate(@RequestParam String subject, @RequestParam String content) {
        // TODO 질문을 저장한다.
        return "redirect:/question/list"; // 질문 저장후 질문목록으로 이동
    }
  • 매서드 오버로딩을 통해 post함수 생성

서비스

  • 질문을 저장하기 위해 서비스에 추가
public Question getQuestion(Integer id){
        Optional<Question> question = this.questionRepository.findById(id);
        if(question.isPresent()){
            return question.get();
        }
        else{
            throw new DataNotFoundException("question not fund");
        }
  • 컨트롤러에서 이 서비스를 사용할 수 있도록 수정

Form

  • 등록시 비어 있어도 등록이 가능한데 이걸 체크하는 방법 구현

Spring Boot Vaildation

  • 화면에서 전달받은 입력값을 검증하려면 이 라이브러리가 필요

폼 클래스

  • 화면에서 전달되는 입력 값을 검증 위해서 필요
@Getter
@Setter
public class QuestionForm {
    @NotEmpty(message="제목은 필수항목입니다.")
    @Size(max=200)
    private String subject;

    @NotEmpty(message="내용은 필수항목입니다.")
    private String content;
}
  • @NotEmpty는 빈 문자열 안된다
  • @Size는 바이트 크기 제한

컨트롤러

@PostMapping("/question/create")
    public String questionCreate(@Valid QuestionForm questionForm, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return "question_form";
        }
        this.questionService.create(questionForm.getSubject(), questionForm.getContent());
        return "redirect:/question/list";
    }
  • queestionCreate 매개변수를 subject, content대신 QuestionForm객체로 변경
    • QuestionForm의 subject, content속성이 자동으로 바인딩
    • Vaild는 QuestionForm의 @NotEmpty와 @Size설정 검증
    • BindingResult는 @Vaild인해 검증이 수행된 결과
    • BindingResult는 @Vaild바로 뒤에 위치
<html layout:decorate="~{layout}" xmlns:layout="http://www.w3.org/1999/xhtml">
<div layout:fragment="content" class="container">
    <h5 class="my-3 border-bottom pb-2">질문등록</h5>
    <form th:action="@{/question/create}" th:object="${questionForm}" method="post">
        <div class="alert alert-danger" role="alert" th:if="${#fields.hasAnyErrors()}">
            <div th:each="err : ${#fields.allErrors()}" th:text="${err}" />
        </div>
        <div class="mb-3">
            <label for="subject" class="form-label">제목</label>
            <input type="text" name="subject" class="form-control">
        </div>
        <div class="mb-3">
            <label for="content" class="form-label">내용</label>
            <textarea name="content" class="form-control" rows="10"></textarea>
        </div>
        <input type="submit" value="저장하기" class="btn btn-primary my-2">
    </form>
</div>
</html>
  • #fields.allErrors() 실패한 오류 메시지 구할 수 있다
  • alert alert-danger 부트스트랩 클래스로 오류 붉은색
  • th:object 사용하여 폼의 속성들이 QuestionForm의 속성들로 구성된다는 점을 타임리프 엔진에 알림
@GetMapping("/create")
    public String questionCreate(QuestionForm questionForm) {
        return "question_form";
  • get메핑에서 매개변수로 QuestionForm 객체 추가 필요

오류 발생이 입력 내용 유지

    <input type="text" name="subject" class="form-control">
 
 -> 
  <input type="text" th:field="*{subject}" class="form-control">

profile
안녕하세요

0개의 댓글