본문 바로가기
스프링/스프링 웹 개발 활용

[Spring] 검증(Validation) 오류처리 및 프로젝트 생성 + 프로젝트 명칭 바꾸기

by drCode 2023. 7. 27.
728x90
반응형

상품 관리 시스템에 새로운 요구사항을 추가해보자.

 

요구사항: 검증 로직 추가

 - 타입 검증

   : 가격, 수량에 문자가 들어가면 검증 오류 처리

 - 필드 검증

   : 상품명: 필수, 공백X

   : 가격: 1000원 이상, 1백만원 이하

   : 수량: 최대 9999

 - 특정 필드의 범위를 넘어서는 검증

   : 가격 * 수량의 합은 10,000원 이상

 

지금까지 만든 웹 애플리케이션은 폼 입력시 숫자를 문자로 작성하거나해서

검증 오류가 발생하면 오류 화면으로 바로 이동한다.

 

이렇게 되면 사용자는 처음부터 해당 폼으로 다시 이동해서 입력을 해야 한다.

아마도 이런 서비스라면 사용자는 금방 떠나버릴 것이다.

 

웹 서비스는 폼 입력시 오류가 발생하면,

고객이 입력한 데이터를 유지한 상태로 어떤 오류가 발생했는지 친절하게 알려주어야 한다.

 

컨트롤러의 중요한 역할중 하나는 HTTP 요청이 정상인지 검증하는 것이다.

그리고 정상 로직보다 이런 검증 로직을 잘 개발하는 것이 어쩌면 더 어려울 수 있다.

 

참고: 클라이언트 검증, 서버 검증

 - 클라이언트 검증은 조작할 수 있으므로 보안에 취약하다.

 - 서버만으로 검증하면, 즉각적인 고객 사용성이 부족해진다.

 - 둘을 적절히 섞어서 사용하되, 최종적으로 서버 검증은 필수 API 방식을 사용하면 API 스펙을 잘 정의해서

   검증 오류를 API 응답 결과에 잘 남겨주어야 함

 

먼저 검증을 직접 구현해보자

 

검증을 직접 구현할 프로젝트를 설정해야하는데,

 

https://drcode-devblog.tistory.com/533

 

[Spring] 스프링 메시지, 국제화를 위한 프로젝트 설정

https://drcode-devblog.tistory.com/524 [Spring] 스프링 타임리프 통합을 위한 프로젝트 설정 https://drcode-devblog.tistory.com/504 drcode-devblog.tistory.com 위 포스팅과 비슷하게 패키지와 url 이름만 바꿔서 사용할 것

drcode-devblog.tistory.com

 

위 포스팅처럼 똑같이 하면 된다.

 

위 포스팅과 비슷하게 패키지와 url 이름만 바꿔서 사용할 것이다.

 

item-service\src\main\resources\templates\basic

→ validation\src\main\resources\templates\form

 

프로젝트명 변경

item-service → validation

 

templates 하위 폴더

basic → validation

 

컨트롤러 상위 패키지 명 변경

basic → validation

 

validation\src\main\java\hello\itemservice\web\validation\ValidationItemControllerV1.java

package hello.itemservice.web.validation;

import hello.itemservice.domain.item.Item;
import hello.itemservice.domain.item.ItemRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import java.util.List;

@Controller
@RequestMapping("/validation/v1/items")
@RequiredArgsConstructor
public class ValidationItemControllerV1 {

    private final ItemRepository itemRepository;

    @GetMapping
    public String items(Model model) {
        List<Item> items = itemRepository.findAll();
        model.addAttribute("items", items);
        return "validation/v1/items";
    }

    @GetMapping("/{itemId}")
    public String item(@PathVariable long itemId, Model model) {
        Item item = itemRepository.findById(itemId);
        model.addAttribute("item", item);
        return "validation/v1/item";
    }

    @GetMapping("/add")
    public String addForm(Model model) {
        model.addAttribute("item", new Item());
        return "validation/v1/addForm";
    }

    @PostMapping("/add")
    public String addItem(@ModelAttribute Item item, RedirectAttributes redirectAttributes) {
        Item savedItem = itemRepository.save(item);
        redirectAttributes.addAttribute("itemId", savedItem.getId());
        redirectAttributes.addAttribute("status", true);
        return "redirect:/validation/v1/items/{itemId}";
    }

    @GetMapping("/{itemId}/edit")
    public String editForm(@PathVariable Long itemId, Model model) {
        Item item = itemRepository.findById(itemId);
        model.addAttribute("item", item);
        return "validation/v1/editForm";
    }

    @PostMapping("/{itemId}/edit")
    public String edit(@PathVariable Long itemId, @ModelAttribute Item item) {
        itemRepository.update(itemId, item);
        return "redirect:/validation/v1/items/{itemId}";
    }
}

 

 

validation\src\main\resources\templates\validation\v1\addForm.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <link th:href="@{/css/bootstrap.min.css}"
          href="../../css/bootstrap.min.css" rel="stylesheet">
    <style>
        .container {
            max-width: 560px;
        }
    </style>
</head>
<body>

<div class="container">

    <div class="py-5 text-center">
        <h2>상품 등록 폼</h2>
    </div>

    <form action="item.html" th:action th:object="${item}" method="post">
        <div>
            <label for="itemName">상품명</label>
            <input type="text" id="itemName" th:field="*{itemName}" class="form-control" placeholder="이름을 입력하세요">
        </div>
        <div>
            <label for="price">가격</label>
            <input type="text" id="price" th:field="*{price}" class="form-control" placeholder="가격을 입력하세요">
        </div>
        <div>
            <label for="quantity">수량</label>
            <input type="text" id="quantity" th:field="*{quantity}" class="form-control" placeholder="수량을 입력하세요">
        </div>

        <hr class="my-4">

        <div class="row">
            <div class="col">
                <button class="w-100 btn btn-primary btn-lg" type="submit">상품 등록</button>
            </div>
            <div class="col">
                <button class="w-100 btn btn-secondary btn-lg"
                        onclick="location.href='items.html'"
                        th:onclick="|location.href='@{/validation/v1/items}'|"
                        type="button">취소</button>
            </div>
        </div>

    </form>

</div> <!-- /container -->
</body>
</html>

 

 

validation\src\main\resources\templates\validation\v1\editForm.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <link th:href="@{/css/bootstrap.min.css}"
          href="../../css/bootstrap.min.css" rel="stylesheet">
    <style>
        .container {
            max-width: 560px;
        }
    </style>
</head>
<body>

<div class="container">

    <div class="py-5 text-center">
        <h2>상품 수정 폼</h2>
    </div>

    <form action="item.html" th:action th:object="${item}" method="post">
        <div>
            <label for="id">상품 ID</label>
            <input type="text" id="id" th:field="*{id}" class="form-control" readonly>
        </div>
        <div>
            <label for="itemName">상품명</label>
            <input type="text" id="itemName" th:field="*{itemName}" class="form-control">
        </div>
        <div>
            <label for="price">가격</label>
            <input type="text" id="price" th:field="*{price}" class="form-control">
        </div>
        <div>
            <label for="quantity">수량</label>
            <input type="text" id="quantity" th:field="*{quantity}" class="form-control">
        </div>

        <hr class="my-4">

        <div class="row">
            <div class="col">
                <button class="w-100 btn btn-primary btn-lg" type="submit">저장</button>
            </div>
            <div class="col">
                <button class="w-100 btn btn-secondary btn-lg"
                        onclick="location.href='item.html'"
                        th:onclick="|location.href='@{/validation/v1/items/{itemId}(itemId=${item.id})}'|"
                        type="button">취소</button>
            </div>
        </div>

    </form>

</div> <!-- /container -->
</body>
</html>

 

 

validation\src\main\resources\templates\validation\v1\item.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <link th:href="@{/css/bootstrap.min.css}"
            href="../../css/bootstrap.min.css" rel="stylesheet">
    <style>
        .container {
            max-width: 560px;
        }
    </style>
</head>
<body>

<div class="container">

    <div class="py-5 text-center">
        <h2>상품 상세</h2>
    </div>

    <!-- 추가 -->
    <h2 th:if="${param.status}" th:text="'저장 완료'"></h2>

    <div>
        <label for="itemId">상품 ID</label>
        <input type="text" id="itemId" name="itemId" class="form-control" value="1" th:value="${item.id}" readonly>
    </div>
    <div>
        <label for="itemName">상품명</label>
        <input type="text" id="itemName" name="itemName" class="form-control" value="상품A" th:value="${item.itemName}" readonly>
    </div>
    <div>
        <label for="price">가격</label>
        <input type="text" id="price" name="price" class="form-control" value="10000" th:value="${item.price}" readonly>
    </div>
    <div>
        <label for="quantity">수량</label>
        <input type="text" id="quantity" name="quantity" class="form-control" value="10" th:value="${item.quantity}" readonly>
    </div>

    <hr class="my-4">

    <div class="row">
        <div class="col">
            <button class="w-100 btn btn-primary btn-lg"
                    onclick="location.href='editForm.html'"
                    th:onclick="|location.href='@{/validation/v1/items/{itemId}/edit(itemId=${item.id})}'|"
                    type="button">상품 수정</button>
        </div>
        <div class="col">
            <button class="w-100 btn btn-secondary btn-lg"
                    onclick="location.href='items.html'"
                    th:onclick="|location.href='@{/validation/v1/items}'|"
                    type="button">목록으로</button>
        </div>
    </div>

</div> <!-- /container -->
</body>
</html>

 

 

validation\src\main\resources\templates\validation\v1\items.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <link th:href="@{/css/bootstrap.min.css}"
            href="../../css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

<div class="container" style="max-width: 600px">
    <div class="py-5 text-center">
        <h2>상품 목록</h2>
    </div>

    <div class="row">
        <div class="col">
            <button class="btn btn-primary float-end"
                    onclick="location.href='addForm.html'"
                    th:onclick="|location.href='@{/validation/v1/items/add}'|"
                    type="button">상품 등록</button>
        </div>
    </div>

    <hr class="my-4">
    <div>
        <table class="table">
            <thead>
            <tr>
                <th>ID</th>
                <th>상품명</th>
                <th>가격</th>
                <th>수량</th>
            </tr>
            </thead>
            <tbody>
            <tr th:each="item : ${items}">
                <td><a href="item.html" th:href="@{/validation/v1/items/{itemId}(itemId=${item.id})}" th:text="${item.id}">회원id</a></td>
                <td><a href="item.html" th:href="@{|/validation/v1/items/${item.id}|}" th:text="${item.itemName}">상품명</a></td>
                <td th:text="${item.price}">10000</td>
                <td th:text="${item.quantity}">10</td>
            </tr>
            </tbody>
        </table>
    </div>

</div> <!-- /container -->

</body>
</html>

 

http://localhost:8080/

 

http://localhost:8080/validation/v1/items/1
http://localhost:8080/validation/v1/items/1/edit
http://localhost:8080/validation/v1/items/add

프로젝트 명칭 바꾸기

 

1. 

File > Project Structure

2.

Project name 수정 후&nbsp; &nbsp;OK 버튼

3. 인텔리제이 종료 후 재시작

 

4.  settings.gradle 클릭

validation\settings.gradle

settings.gradle

5. 내용 수정

rootProject.name = 'validation'

 

6. 빌드 실행

※ 빌드하고 제대로 실행이 안된다면 인텔리제이를 다시 실행해볼것

 

 

참고 블로그 : https://sudong.tistory.com/28

728x90
반응형

댓글