본문 바로가기
웹/Spring, Django

[Spring]Application Layer

by stubborngastropod 2023. 5. 16.
728x90

퀴즈를 CRUD하고 문제를 풀 수 있는 어플리케이션 레이어를 구현한다.

//QuizForm
package com.example.quiz.form;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotBlank;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class QuizForm {
    private Integer id;

    @NotBlank
    private String question;

    private Boolean answer;

    @NotBlank
    private String author;

    private Boolean newQuiz;
}

@NotBlank는 빈 값을 허용하지 않는 유효성 검사 어노테이션이고 퀴즈 생성/변경을 확인할 수 있도록 boolean값인 newQuiz 변수도 만들어준다.

//QuizController
package com.example.quiz.controller;

import com.example.quiz.entity.Quiz;
import com.example.quiz.form.QuizForm;
import com.example.quiz.service.QuizService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/quiz")
public class QuizController {
    @Autowired
    QuizService service;

    @ModelAttribute
    public QuizForm setUpForm() {
        QuizForm form = new QuizForm();
        form.setAnswer(true);
        return form;
    }

    @GetMapping
    public String showList(QuizForm quizForm, Model model) {
        quizForm.setNewQuiz(true);

        Iterable<Quiz> list = service.selectAll();

        model.addAttribute("list", list);
        model.addAttribute("title", "Registration form");

        return "crud";
    }
}

/quiz로 끝나는 url에 대해 해당 클래스의 요청 핸들러 메서드를 호출한다.

위의 컨트롤러에 의해 다음과 같은 페이지가 반환된다.

<!--crud.html-->
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>CRUD</title>
</head>
<body>
<h1>OX QUIZ APPLICATION</h1>
<h3 th:text="${title}"></h3>
<p th:if="${complete}" th:text="${complete}" style="color:blue"></p>

<form method="POST"
    th:action="${quizForm.newQuiz}? @{/quiz/insert} : @{/quiz/update}"
    th:object="${quizForm}">
    <label>QUIZ: </label>
    <br>
    <textarea rows="5" cols="50" th:field="*{question}"></textarea>
    <br>
    <div th:if="${#fields.hasErrors('question')}" th:errors="*{question}"
        style="color:red"></div>
    <label>ANSWER: </label>
    <br>
    <input type="radio" value="true" th:field="*{answer}"> O
    <input type="radio" value="false" th:field="*{answer}"> X
    <br>
    <div th:if="${#fields.hasErrors('answer')}" th:errors="*{answer}"
        style="color:red"></div>
    <label>AUTHOR: </label>
    <input type="text" th:field="*{author}" />
    <br>
    <div th:if="${#fields.hasErrors('author')}" th:errors="*{author}"
         style="color:red"></div>
    <input th:if="${id}" type="hidden" th:field="*{id}">
    <input type="submit" value="SUBMIT">
</form>
<br>
<hr>
<div th:if="${quizForm.newQuiz}" style="margin:10px">
    <h3>REGISTERED QUIZ LIST <a th:href="@{/quiz/play}">PLAY</a><br></h3>
    <p th:if="${delcomplete}" th:text="${delcomplete}" style="color:blue"></p>
    <p th:if="${msg}" th:text="${msg}" style="color:red"></p>
    <table border="1" th:unless="${#lists.isEmpty(list)}" style="table-layout:fixed">
        <tr>
            <th>ID</th>
            <th>CONTENT</th>
            <th>ANSWER</th>
            <th>AUTHOR</th>
            <th>UPDATE</th>
            <th>DELETE</th>
        </tr>
        <tr th:each="obj : ${list}" align="center">
            <td th:text="${obj.id}"></td>
            <td th:text="${obj.question}" align="left"></td>
            <td th:text="${obj.answer} == true?'O':'X'"></td>
            <td th:text="${obj.author}"></td>
            <td>
                <form th:action="@{/quiz/{id}(id=${obj.id})}" method="get">
                    <input type="submit" value="UPDATE">
                </form>
            </td>
            <td>
                <form th:action="@{/quiz/delete}" method="post">
                    <input type="hidden" name="id" th:value="${obj.id}">
                    <input type="submit" value="DELETE">
                </form>
            </td>
        </tr>
    </table>
    <p th:if="${#lists.isEmpty(list)}">NO REGISTERED QUIZ</p>
</div>
<p th:unless="${quizForm.newQuiz}">
    <a href="#" th:href="@{/quiz}">BACK TO CRUD</a>
</p>
</body>
</html>

퀴즈 CRUD 페이지

다음은 퀴즈를 CUD 하는 기능을 작성한다.

// QuizController
@PostMapping("/insert")
public String insert(@Validated QuizForm quizForm, BindingResult bindingResult,
                     Model model, RedirectAttributes redirectAttributes) {
    Quiz quiz = new Quiz();
    quiz.setQuestion(quizForm.getQuestion());
    quiz.setAnswer(quizForm.getAnswer());
    quiz.setAuthor(quizForm.getAuthor());

    if (!bindingResult.hasErrors()) {
        service.insertQuiz(quiz);
        redirectAttributes.addFlashAttribute("complete", "QUIZ REGISTERED");
        return "redirect:/quiz";
    } else {
        return showList(quizForm, model);
    }
}

퀴즈 생성

//QuizController
@GetMapping("/{id}")
public String showUpdate(QuizForm quizForm, @PathVariable Integer id, Model model) {
    Optional<Quiz> quizOpt = service.selectOneById(id);

    Optional<QuizForm> quizFormOpt = quizOpt.map(t -> makeQuizForm(t));

    if (quizFormOpt.isPresent()) {
        quizForm = quizFormOpt.get();
    }

    makeUpdateModel(quizForm, model);
    return "crud";
}

private void makeUpdateModel(QuizForm quizForm, Model model) {
    model.addAttribute("id", quizForm.getId());
    quizForm.setNewQuiz(false);
    model.addAttribute("quizForm", quizForm);
    model.addAttribute("title", "UPDATE FORM");
}

@PostMapping("/update")
public String update(
        @Validated QuizForm quizForm,
        BindingResult result,
        Model model,
        RedirectAttributes redirectAttributes) {
    Quiz quiz = makeQuiz(quizForm);
    if (!result.hasErrors()) {
        service.updateQuiz(quiz);
        redirectAttributes.addFlashAttribute("complete", "QUIZ UPDATED");
        return "redirect:/quiz" + quiz.getId();
    } else {
        makeUpdateModel(quizForm, model);
        return "crud";
    }
}

private Quiz makeQuiz(QuizForm quizForm) {
    Quiz quiz = new Quiz();
    quiz.setId(quizForm.getId());
    quiz.setQuestion(quizForm.getQuestion());
    quiz.setAnswer(quizForm.getAnswer());
    quiz.setAuthor(quizForm.getAuthor());
    return quiz;
}

private QuizForm makeQuizForm(Quiz quiz) {
    QuizForm form = new QuizForm();
    form.setId(quiz.getId());
    form.setQuestion(quiz.getQuestion());
    form.setAnswer(quiz.getAnswer());
    form.setAuthor(quiz.getAuthor());
    form.setNewQuiz(false);
    return form;
}

퀴즈 변경
변경된 퀴즈 목록

//QuizController
@PostMapping("/delete")
public String delete(
        @RequestParam("id") String id,
        Model model,
        RedirectAttributes redirectAttributes) {
    service.deleteQuizById(Integer.parseInt(id));
    redirectAttributes.addFlashAttribute("delcomplete", "QUIZ DELETED");
    return "redirect:/quiz";
}

퀴즈 삭제

이제 OX 퀴즈를 즐길 수 있는 화면과 기능을 구성한다.

<!--play.html-->
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>OX QUIZ PLAY</title>
</head>
<body>
<h1>OX QUIZ APPLICATION:PLAY</h1>
<h3>QUIZ</h3>
<th:block th:if="${msg}">
    <p th:text="${msg}" style="color:red"></p>
    <a th:href="@{/quiz/}">BACK TO CRUD</a>
</th:block>
<th:block th:unless="${msg}">
    <p th:text="${quizForm.question}">QUIZ</p>
    <form th:action="@{/quiz/check}" th:object="${quizForm}" method="POST">
        <input type="hidden" th:field="*{id}">
        <button name="answer" value="true">O</button>
        <button name="answer" value="false">X</button>
    </form>
</th:block>
</body>
</html>

msg의 유무에 따라 문제의 표시 여부를 결정한다.

<!--answer.html-->
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>RESULT</title>
</head>
<body>
<h1>RESULT</h1>
<h2 th:text="${msg}" th:style="'color:' + (${msg} == 'CORRECT' ? 'green' : 'red')"></h2>
<a th:href="@{/quiz/play}">REPLAY</a>
<a th:href="@{/quiz}">BACK TO CRUD</a>
</body>
</html>

msg의 내용에 따라 글자의 색을 결정하는 삼항연산자를 이용해봤다.

//QuizController
@GetMapping("/play")
public String showQuiz(QuizForm quizForm, Model model) {
    Optional<Quiz> quizOpt = service.selectOneRandomQuiz();

    if(quizOpt.isPresent()) {
        Optional<QuizForm> quizFormOpt = quizOpt.map(t -> makeQuizForm(t));
        quizForm = quizFormOpt.get();
    } else {
        model.addAttribute("msg", "NO REGISTERED QUIZ");
        return "play";
    }

    model.addAttribute("quizForm", quizForm);

    return "play";
}

@PostMapping("/check")
public String checkQuiz(QuizForm quizForm, @RequestParam Boolean answer, Model model) {
    if (service.checkQuiz(quizForm.getId(), answer)) {
        model.addAttribute("msg", "CORRECT");
    } else {
        model.addAttribute("msg", "WRONG");
    }
    return "answer";
}

문제 화면
오답
정답

 

728x90

' > Spring, Django' 카테고리의 다른 글

[Spring]Service  (1) 2023.05.15
[Spring]Application-CRUD  (0) 2023.05.12
[Spring]Request Parameter/Validation  (0) 2023.05.11
[Spring]Thymeleaf 문법, Layout  (0) 2023.05.10
[Spring]JDBC/MVC Model  (0) 2023.05.09

댓글