WEB 커리큘럼 4주차 - 과제

이은지·2023년 10월 9일
0

GDSC-Web

목록 보기
6/7
post-custom-banner

4주차 과제로 이제 질문 등록 폼과 질문을 등록하는 기능을 만들어야 한다.

🌻 질문 등록 기능 구현

💡‘질문 등록하기’버튼 만들기

  • 링크 엘리먼트(<a> ... </a>)에 부트스트랩의 btn btn-primary 클래스를 적용
  • ‘질문 등록하기’ 버튼을 누르면 /question/create URL이 호출된다.

경로: src\main\resources\templates\question_detail.html

<html layout:decorate="~{layout}">
<div layout:fragment="content" class="container my-3">
    <table class="table">
        (... 생략 ...)
    </table>
    <a th:href="@{/question/create}" class="btn btn-primary">질문 등록하기</a>
</div>
</html>

💡컨트롤러에 URL매핑 추가

  • /question/create 요청은 GET 요청에 해당하므로 @GetMapping 어노테이션 사용
  • questionCreate 메서드는 question_form 템플릿을 렌더링하여 출력
(...생략...)

@RequestMapping("/question")
@RequiredArgsConstructor //questionRepository 속성을 포함하는 생성자를 자동으로 롬복에서 생성
@Controller
public class QuestionController {

  (...생략...)
    
    @GetMapping("/create")
    public String questionCreate(){
        return "question_form";
    }
}

💡question_form.html 템플릿 작성

  • ‘저장하기’ 버튼으로 폼을 전송하면 <form method=”post”>에 의해 POST 방식으로 데이터가 요청됨
<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}" 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>

💡질문 데이터 저장하기

  • 먼저 컨트롤러에 POST 방식으로 요청한 /question/create URL을 처리하기 위해 @PostMapping 어노테이션을 지정한 questionCreate 메서드를 추가
  • questonCreate 메서드는 화면에서 입력한 제목(subject)과 내용(content)을 매개변수로 받아 QuestionService를 이용해 질문을 저장

경로: src\main\java\com\gdsc\webboard\question\QuestionController.java

(...생략...)
@RequestMapping("/question")
@RequiredArgsConstructor //questionRepository 속성을 포함하는 생성자를 자동으로 롬복에서 생성
@Controller
public class QuestionController {

    private final QuestionService questionService;

(...생략...)
    @PostMapping("/create")
    public String questionCreate(@RequestParam String subject, @RequestParam String content) {
        this.questionService.create(subject, content);
        return "redirect:/question/list"; // 질문 저장후 질문목록으로 이동
    }
}

경로: src\main\java\com\gdsc\webboard\question\QuestionService.java

(... 생략 ...)
import java.time.LocalDateTime;
(... 생략 ...)
public class QuestionService {

    (... 생략 ...)

    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);
    }
}

💡입력값 검증 기능 추가하기

  • 질문이나 내용을 빈 값으로 등록이 불가능하도록 기능을 추가한다.
  • 먼저, Spring Boot Validation 라이브러리를 활용해 화면에서 전달받은 입력값을 검증
package com.gdsc.webboard.question;

import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class QuestionForm {
    @NotEmpty(message="제목은 필수항목입니다.")
    @Size(max=200)
    private String subject;

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

그다음, 컨트롤러의 questionCreate 메서드의 매개변수를 QuestionFrom 객체로 변경한다.

(... 생략 ...)
import jakarta.validation.Valid;
import org.springframework.validation.BindingResult;
(... 생략 ...)
public class QuestionController {

    (... 생략 ...)

    @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";
    }
}
  • @Valid: QuestionForm의 @NotEmpty, @Size 등으로 설정한 검증 기능이 동작
  • BindingResult 매개변수는 @Valid 어노테이션으로 인해 검증이 수행된 결과를 의미하는 객체
  • bindResult.hasErrors()를 호출하여 오류가 있는 경우에는 다시 폼을 작성하는 화면을 렌더링하게 했고 오류가 없을 경우에만 질문 등록이 진행

💡오류메시지 화면에 띄우기

검증에 실패한 오류메시지를 보여주기 위해 템플릿을 수정한다.

<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>
        <div class="mb-3">
            <label for="subject" class="form-label">제목</label>

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

        </div>
        <div class="mb-3">
            <label for="content" class="form-label">내용</label>
            <textarea th:field="*{content}"  class="form-control" rows="10"></textarea>
        </div>
        <input type="submit" value="저장하기" class="btn btn-primary my-2">
    </form>
</div>
</html>
  • #fields.hasAnyErrors가 true인 경우는 QuestionForm 검증이 실패한 경우
  • #fields.allErrors() : QuestionForm에서 검증에 실패한 오류 메시지
  • name="subject"name="content"와 같이 사용하던 부분을 위와 같이 th:field 속성을 사용하도록 변경하여 타임리프가 value 속성에 기존 값을 채워 넣어 오류가 발생하더라도 기존에 입력한 값이 유지됨
profile
소통하는 개발자가 꿈입니다!
post-custom-banner

0개의 댓글