점프 투 스프링부트 | 2장 HTML과 JAVA 연결3, 공통템플릿 작성 및 적용

5w31892p·2022년 12월 29일
0

Spring

목록 보기
23/30

:: 질문 등록

질문등록 버튼 HTML

...
<a th:href="@{/question/create}" class="btn btn-primary">질문 등록하기</a>
...

Controller

  • URL 매핑
@GetMapping("/create")
public String questionCreate() {
    return "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" id="subject" class="form-control">
        </div>
        <div class="mb-3">
            <label for="content" class="form-label">내용</label>
            <textarea name="content" id="content" class="form-control" rows="10"></textarea>
        </div>
        <input type="submit" value="저장하기" class="btn btn-primary my-2">
    </form>
</div>
</html>

Controller

  • 저장 후 질문 목록으로 이동
  • 매개변수의 형태가 다른경우 메서드명 동일하게 사용 가능 (오버로딩)
  • 질문등록 템플릿에서 필드 항목으로 사용했던 subject, content 이름 동일하게 적어야 한다.
@PostMapping("/create")
public String questionCreate(@RequestParam String subject, @RequestParam String content) {
	// TODO 질문을 저장한다.
    return "redirect:/question/list"; // 질문 저장후 질문목록으로 이동
}

Service

  • 질문 저장하기
public void create(String subject, String content) {
    Question q = new Question();
    q.setSubject(subject);
    q.setContent(content);
    q.setCreateDate(LocalDateTime.now());
    this.questionRepository.save(q);
}

Controller 저장하기 적용

...
this.questionService.create(subject, content);
...

:: 폼(form)

  • 빈값 등록 불가능하게

:: Spring Boot Validation

  • 전달 받은 입력값 검증
항목설명
@Size문자 길이를 제한
@NotNullNull을 허용하지 않는다.
@NotEmptyNull 또는 빈 문자열("")을 허용하지 않는다.
@Past과거 날짜만 가능
@Future미래 날짜만 가능
@FutureOrPresent미래 또는 오늘날짜만 가능
@Max최대값
@Min최소값
@Pattern정규식으로 검증

폼 클래스 작성

  • @NotEmpty : null or 빈 문자열 놉!
    • message : 검증 실패시 화면에 표시할 메시지
  • @Size(max=200) : 최대길이 200바이트 넘지 말라
...
@NotEmpty(message="제목은 필수항목입니다.")
@Size(max=200)
private String subject;

@NotEmpty(message="내용은 필수항목입니다.")
private String content;
...

Controller 수정

  • questionCreate()의 매개변수 변경
  • if문 추가
  • ~~service.create()의 인자값 변경
  • @Valid : @NotEmpty, @Size 등 검증 기능
  • BindingResult : @Valid 으로 검증이 수행된 결과 의미
    • BindingResult는 항상 @Valid 바로 뒤에 위치해야 한다.
@PostMapping("/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";
}

오류 메시지 보여주기 HTML

  • th:object="${questionForm}" 추가 % 그 아래 div들도 추가
    • 오류 표시하려면 th:object 꼭 필요!
<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>
...

Controller 수정

  • "질문 등록하기" 버튼을 통해 GET 방식으로 요청되더라도 th:object에 의해 QuestionForm 객체가 필요
  • questionCreate의 매개변수 QuestionForm questionForm 으로 변경

오류시 입력한 내용 유지하기 HTML

  • 제목 적는 input태그에 추가
  • 내용 적는 textarea태그에 추가
  • th:field 각 필드 매핑해주는 역할
<input ~~~ th:field="*{subject}" ~~~>
<textarea ~~~ th:field="*{content}" ~~~>

답변등록도 질문등록과 동일


:: 공통 템플릿

  • 오류 메시지 반복
  • 하나의 템플릿에 담아 꺼내쓰기

공통템플릿

  • 출력할 오류 부분에 th:fragment="formErrorsFragment" 추가
    • th:fragment : 반복되는 코드를 재사용
    • th:replace : 현재 태그를 fragment 태그로 교체
<div th:fragment="formErrorsFragment" class="alert alert-danger" 
    role="alert" th:if="${#fields.hasAnyErrors()}">
    <div th:each="err : ${#fields.allErrors()}" th:text="${err}" />
</div>

공통템플릿 적용

  • 원래 에러 적었던 자리에 적용
  • th:replace : 공통 템플릿을 템플릿 내에 삽입, 즉 꺼내쓰기
  • "~{form_errors :: formErrorsFragment}" : form_errors.html의 formErrorsFragment 인 것으로 교체
<div th:replace="~{form_errors :: formErrorsFragment}"></div>

0개의 댓글