💻본 포스팅은 '스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - 김영한'님의 강의를 듣고 작성되었습니다.
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - 인프런 | 강의
웹 애플리케이션 개발에 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. MVC 2편에서는 MVC 1편의 핵심 원리와 구조 위에 실무 웹 개발에 필요한 모든 활용 기술들을 학습할 수 있
www.inflearn.com
[검증]
- 문제 상황
- 사용자가 Form에 넣은 데이터가 우리가 원하지 않는 값을 넣었을 때 이를 '400' 에러 페이지를 보여주게 되면 사용자 입장에선 다시 입력 페이지로 들어가야 하는 것은 매우 귀찮은 일이 될 것이다.
- 문제 해결
- 이를 해결하기 위해서는 400 에러 페이지로 넘어가지 않게하고 아닌 우리가 어떤 부분에서 값을 잘못 넣었는지를 화면에서 보여주어야 한다.
- 즉 검증이란 들어온 데이터가 유효한 값인지(우리가 설정한 범위 내의 값을 의미..)를 확인하는 것을 의미하며 우리는 검증을 직접 검증, 어노테이션을 이용한 검증, 인터페이스 구현을 통한 검증 등의 방식으로 진행할 수 있다.
[직접 검증하는 방식]
[controller]
@PostMapping("/add")
public String addItem(@ModelAttribute Item item, RedirectAttributes redirectAttributes, Model model) {
//검정 오류 결과를 보관
Map<String, String> errors = new HashMap<>();
//검증 로직(특정 필드 검증)
if (!StringUtils.hasText(item.getItemName())){
errors.put("itemName", "상품 이름은 필수입니다.");
}
if (item.getPrice() == null || item.getPrice() <1000 ||item.getPrice() > 1000000){
errors.put("price","가격은 1,000원에서 1,000,000원 사이를 넣어주세요.");
}
if (item.getQuantity() == null || item.getQuantity() >= 9999){
errors.put("quantity","수량은 최소 1개부터 최대 9,999개까지 넣을 수 있습니다.");
}
//특정 필드가 아닌 복합 룰 검증
if (item.getPrice() != null && item.getQuantity() != null) {
int resultPrice = item.getPrice() * item.getQuantity();
if (resultPrice < 10000) {
errors.put("globalError", "가격 * 수량의 합은 10,000원 이상이어야 합니다. 현재 값 = " + resultPrice);
}
}
//검증 실패 시 다시 입력 폼으로
if (!errors.isEmpty()){
model.addAttribute("errors", errors);
return "validation/v1/addForm";
}
//성공 로직
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("itemId", savedItem.getId());
redirectAttributes.addAttribute("status", true);
return "redirect:/validation/v1/items/{itemId}";
}
- Controller를 사용해서 HTTP 요청이 정상인지를 확인하는 작업이다
- Controller의 중요한 기능 중 하나는 HTTP 요청이 정상인지를 확인하는 것에 있다.
- 이때 검증 로직은 매우 어렵다
[html 페이지]
<form action="item.html" th:action th:object="${item}" method="post">
<!--오류 메시지 출력 부분-->
<div th:if ="${errors?.containsKey('globalError')}">
<p class = "field-error" th:text ="${errors['globalError']}">전체 오류 메시지</p>
</div>
<div>
<label for="itemName" th:text="#{label.item.itemName}">상품명</label>
<input type="text" id="itemName" th:field="*{itemName}"
th:class="${errors?.containsKey('itemName')}? 'form-control field-error':'form-control'"
class="form-control" placeholder="이름을 입력하세요">
<div class="field-error" th:if="${errors?.containsKey('itemName')}" th:text="${errors['itemName']}">
상품명 오류
</div>
</div>
<div>
<label for="price" th:text="#{label.item.price}">가격</label>
<input type="text" id="price" th:field="*{price}"
th:class="${errors?.containsKey('price')} ? 'form-control field-error' : 'form-control'"
class="form-control"
placeholder="가격을 입력하세요">
<div class="field-error" th:if="${errors?.containsKey('price')}" th:text="${errors['price']}">
가격 오류
</div>
</div>
<div>
<label for="quantity" th:text="#{label.item.quantity}">수량</label>
<input type="text" id="quantity" th:field="*{quantity}"
th:class="${errors?.containsKey('quantity')} ? 'form-control field-error' : 'form-control'"
class="form-control"
placeholder="수량을 입력하세요">
<div class="field-error" th:if="${errors?.containsKey('quantity')}" th:text="${errors['quantity']}">
수량 오류
</div>
</div>
- ?는 null값이 아닐 땐 무시한다는 문법이다. (null값이 아니면 저 테그는 무시된다.)
- 에러 값을 모아둔 곳에 해당 에러가 들어있다면 그에 맞게 메시지를 보여주며 수정을 할 수 있게 한다.
[검증 처리 성공시]
[검증 처리 실패시]
[클라이언트, 서버 검증]
- 클라이언트 검증은 JS를 이용해서 진행하기에 조작이 가능해 보안에 취약하다.
- 서버 검증은 서버만 검증하게 될 때 즉각적인 고객 사용성이 부족해진다.(즉각적으로 보이지 않으니 효과가 떨어진다는 의미다.)
- 둘을 적적히 잘 섞어 사용하되 최종적으로 서버 검증은 필수다.
- API 방식을 사용해 API 스펙을 잘 정의해 검증 오류를 API 응답 결과에 잘 남겨주어야 한다.
[직접 검증의 장점과 단점]
- 장점
- 검증 오류 발생 시 '400' 에러 페이지로 넘어가지 않아 다시 돌아가야 하는 번거로움이 줄어든다.
- 검증 오류가 발생해도 고객이 입력한 데이터가 유지된다.
- 검증 오류를 고객에게 친절하게 안내해서 다시 입력할 수 있게 한다.
- 직접 검증의 단점
- 뷰 템플릿의 중복 처리가 많다.(위의 코드만 봐도 중복되는 부분이 많음을 알 수 있다.)
- 타입 오류 처리가 안 된다.
- 타입이 안 맞으면 데이터를 안 받아서 컨트롤러 호출이 안 된다;;;
- 그렇게 되면 '400' 에러 페이지로 넘어가버리게 된다.
- 고객이 입력한 값도 어딘가에 별도 관리해야 한다.
- 타입 오류가 발생할 때 고객이 입력한 문자는 바인딩 불가해져 사라지고 고객은 본인이 어떤 내용을 입력해서 오류 발생을 시켰는지 이해하기도 어려워지게 된다.
- 해결 방안
- 추가적인 방법이 더 필요하기에 다음 포스팅에서는 이런 문제를 해결하도록 하자.
- 스프링이 이를 해결하는 많은 방식을 제공하고 있다!!
'Back-End > Spring' 카테고리의 다른 글
[Spring MVC2][검증(Validation)] - FieldError, ObjectError(부제: 사용자 입력 오류에도 입력해 놓은 데이터를 그대로 유지해보자) (0) | 2022.04.27 |
---|---|
[Spring MVC2][검증(Validation)] - BindingResult(부제: 검증 오류 스프링부트와 타임리프가 손쉽게 지원하는 법, 바인딩 오류에도 400오류가 아닌 제대로 컨트롤러를 호출하는 법) (0) | 2022.04.27 |
[Spring MVC2][메시지, 국제화] - 메시지, 국제화 (0) | 2022.04.25 |
[Spring MVC2][타임리프] - 템플릿 레이아웃 (0) | 2022.04.15 |
[Spring MVC2][타임리프] - 템플릿 조각 (0) | 2022.04.15 |