Hibernate-Validator
bean(자바 빈 클래스, VO)의 유효성 검사의 기능을 이용해 서버측에서 입력값 검증을 하는 라이브러리
빈 클래스 앞에
@Validated
를 붙이면 활성화 된다
Bean Validation이 제공하는 제약 애너테이션
- NotNull : 빈 값 체크(int타입)
- NotBlank : null 체크, trim후 길이가 0인지 체크(String타입)
- Size : 글자 수 체크
- Email : 이메일 주소 형식 체크
- Past : 오늘보다 과거 날짜(ex. 생일)
- Future : 미래 날짜 체크(ex. 예약일)
- DateTimeFormat(pattern="yyyy-MM-dd") : 해당하는 패턴의 날짜 String을 Date로 변환
- AssertFalse : false 값만 통과 가능
- AssertTrue : true 값만 통과 가능
- DecimalMax(value=) : 지정된 값 이하의 실수만 통과 가능
- DecimalMin(value=) : 지정된 값 이상의 실수만 통과 가능
- Digits(integer=,fraction=) : 대상 수가 지정된 정수와 소수 자리수보다 적을 경우 통과 가능
- Future : 대상 날짜가 현재보다 미래일 경우만 통과 가능
- Past : 대상 날짜가 현재보다 과거일 경우만 통과 가능
- Max(value) : 지정된 값보다 아래일 경우만 통과 가능
- Min(value) : 지정된 값보다 이상일 경우만 통과 가능
- NotNull : null 값이 아닐 경우만 통과 가능
- Null : null일 겨우만 통과 가능
- Pattern(regex=, flag=) : 해당 정규식을 만족할 경우만 통과 가능
- Size(min=, max=) : 문자열 또는 배열이 지정된 값 사이일 경우 통과 가능
- Valid : 대상 객체의 확인 조건을 만족할 경우 통과 가능
@NotBlank
@Size(max = 100)
private String prodName;
초기 설정
pom.xml
<!-- 입력값을 검증하기 위한 라이브러리 의존 관계 정의 시작
스프링
M(Model) : Service, ServiceImple, Mapper
V(View) : JSP
C(Controller) : Controller
Bean(자바빈 클래스, ArticleVO) Validation(유효성검사) 기능을 이용해
요청 파라미터 값이 바인딩된(멤버변수에 세팅된) 도메인 클래스(ArticleVO)의 입력값 검증을 함
요청 파라미터 : ?articleNo=112&title=개똥이
public String write(골뱅이ModelAttribute ArticleVO articleVO)
-->
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.5.Final</version>
</dependency>
예제
ProdController.java (부분)
@PostMapping("/registPost")
// 입력값 검증 기능을 활성화
public String registPost(@Validated ProdVO prodVo) {
log.info("vo >>"+prodVo);
int result = this.service.registPost(prodVo);
log.info("result >> "+result);
// return "redirect: /prod/list";
return "redirect: /prod/detail?prodId="+prodVo.getProdId();
}
// error처리를 위해 form에
// ModelAttribute를 넣었으므로 해당 메서드도 prodVO 추가
@GetMapping("/regist")
public String regist(Model model,@ModelAttribute ProdVO prodVO) {
return "prod/regist";
}
// forward를 위해 비동기화로 변경
@ResponseBody
@GetMapping("/lprod")
public List<LprodVO> lprodList(){
List<LprodVO> lprod = this.service.lprodList();
return lprod;
}
ProdVO.java
package kr.or.ddit.vo;
import java.util.Date;
import java.util.List;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.multipart.MultipartFile;
import lombok.Data;
@Data
public class ProdVO {
private int rnum;
private String prodId;
// message는 내가 지정하고 싶은 오류 메세지
// 기본값 : 반드시 값이 존재하고 공백 문자를 제외한 길이가 0보다 커야 합니다.
@NotBlank(message = "상품명은 필수 입력 항목입니다.")
private String prodName;
private String prodLgu;
private String prodBuyer;
private int prodCost;
private int prodPrice;
@NotNull(message = "판매가는 필수 입력 항목입니다.")
private int prodSale;
private String prodOu;
private String prodDetail;
private String prodImg;
private int prodTotalstock;
//String 타입인 "2024-08-09" -> Date타입으로 변환
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date prodInsdate; // 최근 입고일자
private String prodOutline;
private int prodProperstock;
private String prodSize;
private String prodColor;
private String prodDelivery;
private String prodUnit;
private int prodQtyin;
private int prodQtysale;
private int prodMileage;
private long fileGroupNo;
private String prodDelYn;
private MultipartFile[] uploadFile;
private List<CartVO> cartVoList;
private FileGroupVO fgvo;
}
regist.jsp
redirect로 보낼 수 없기때문에 lprod 비동기화로 변경
error 코드 추가, form태그로 변경 (error띄울 input은 form:input으로 변환 id, name > path)
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<link type="text/css" href="/resources/ckeditor5/sample/css/sample.css" rel="stylesheet" media="screen"/>
<script type="text/javascript" src="/resources/ckeditor5/ckeditor.js"></script>
<script type="text/javascript" src="/resources/js/jquery.min.js"></script>
<div class="card card-success">
<div class="card-header">
<h2>상품 등록</h2>
</div>
<form:form modelAttribute="prodVO"
id="frm" action="/prod/registPost" method="post" enctype="multipart/form-data">
<div class="card-body">
<div class="row">
<div class="col-sm-6">
<!-- text input -->
<div class="form-group">
<label for="prodId">상품 코드</label> <input required type="text"
class="form-control" readonly placeholder="상품분류를 선택해주세요"
name="prodId" id="prodId">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="prodLgu">상품 분류 코드</label> <select required="required" class="form-control"
id="prodLgu" name="prodLgu">
<option value="" selected="selected" disabled="disabled">선택해주세요</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<!-- text input -->
<div class="form-group">
<label for="prodName">상품 명</label>
<form:input class="form-control" placeholder="상품명" path="prodName" />
<code style="color:red;">
<form:errors path="prodName"></form:errors>
</code>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="prodBuyer">거래처</label> <select required class="form-control"
id="prodBuyer" name="prodBuyer">
<option value="" selected="selected" disabled="disabled">상품 분류를 선택해주세요</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<!-- textarea -->
<div class="form-group">
<label for="prodSale">상품 판매가</label>
<input type="number" class="form-control" placeholder="판매가" name="prodSale"/>
<code style="color:red;">
${errors.prodSale}
</code>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label>업로드 파일</label>
<div class="custom-file">
<input required type="file" class="custom-file-input" name="uploadFile"
id="uploadFile" multiple="multiple"> <label class="custom-file-label" id="fileLabel"
for="uploadFile">파일을 선택해주세요</label>
</div>
</div>
<div id="pImg">
<!-- <img src="/resources/images/P1234.jpg" style="width: 100px; border:1px solid #ced4da;border-radius: .25rem;" /> -->
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<!-- textarea -->
<div class="form-group">
<label for="prodSale">최근 입고일자</label>
<form:input placeholder="ex)2024-08-09" class="form-control" path="prodInsdate"/>
<code style="color:red;">
<form:errors path="prodInsdate"></form:errors>
</code>
</div>
</div>
</div>
<!-- input states -->
<div class="form-group">
<label class="col-form-label" for="prodDetail">상품 상세 설명</label>
<div id="prodDetailTemp"></div>
<textarea hidden class="form-control" rows="3" id="prodDetail"
placeholder="상품 상세 설명" name="prodDetail" cols=""></textarea>
</div>
</div>
<!-- /.card-body -->
<div class="card-footer row" style="justify-content: space-between;">
<div>
<a href="/prod/list" class="btn btn-info">목록</a>
</div>
<div>
<button type="submit" class="btn btn-warning">등록</button>
</div>
<div>
<button type="reset" id="reset" class="btn btn-secondary">초기화</button>
</div>
</div>
</form:form>
</div>
<script>
// uploadUrl => 이미지 업로드 시 요청할 요청URI
// editor => CKEditor가 생성된 후 바로 그 객체
// window.editor : 그 객체를 이렇게 부르겠다 정의
ClassicEditor.create( document.querySelector('#prodDetailTemp'),{ckfinder:{uploadUrl:'/image/upload'}})
.then(editor=>{window.editor=editor;})
.catch(err=>{console.error(err.stack);});
document.getElementById('uploadFile').addEventListener('change', function(event) {
const files = event.target.files; // 업로드된 파일 목록
const imagePreviewContainer = document.getElementById('pImg');
imagePreviewContainer.innerHTML = ''; // 기존 미리보기 초기화
// 업로드된 파일을 미리보기로 표시
for (let i = 0; i < files.length; i++) {
const file = files[i];
const reader = new FileReader();
reader.onload = function(e) {
const imgContainer = document.createElement('div');
imgContainer.className = 'image-container';
imgContainer.style.position = 'relative';
imgContainer.style.display = 'inline-block';
imgContainer.style.margin = '5px';
// 이미지 생성
const img = document.createElement('img');
img.src = e.target.result; // FileReader로 읽은 이미지 URL
img.style.width = '100px';
img.style.border = '1px solid #ced4da';
img.style.borderRadius = '.25rem';
// 삭제 버튼 생성
const button = document.createElement('button');
button.type = 'button';
button.className = 'btn btn-tool';
button.style.position = 'absolute';
button.style.top = '5px';
button.style.right = '-1px';
button.style.background = 'rgba(255, 255, 255, 0.8)';
button.style.border = 'none';
button.style.borderRadius = '50%';
button.style.padding = '2px';
button.style.cursor = 'pointer';
button.innerHTML = '<i class="fas fa-times"></i>';
// 삭제 버튼 클릭 시 동작
button.onclick = function() {
// 이미지 컨테이너 삭제
imgContainer.remove();
// 업로드된 파일 목록에서 해당 파일 삭제
const fileList = Array.from(document.getElementById('uploadFile').files);
const newFileList = fileList.filter((_, index) => index !== i); // 현재 인덱스 제외
const dataTransfer = new DataTransfer(); // 새로운 파일 리스트 생성
newFileList.forEach(file => dataTransfer.items.add(file)); // 새로운 파일 리스트 추가
document.getElementById('uploadFile').files = dataTransfer.files; // 파일 입력 필드 업데이트
};
// 이미지 컨테이너에 이미지와 버튼 추가
imgContainer.appendChild(img);
imgContainer.appendChild(button);
imagePreviewContainer.appendChild(imgContainer);
};
reader.readAsDataURL(file); // 파일을 데이터 URL로 읽기
}
});
function removeImage(button) {
// Find the parent container of the button
const imageContainer = button.closest('.image-container');
// Remove the image container from the DOM
if (imageContainer) {
imageContainer.remove();
}
}
$(function(){
$.ajax({
url : "/prod/lprod",
success : function(res){
let str = ""
console.log(res);
$.each(res, function(){
str+= "<option value='"+this.lprodGu+"'>"+this.lprodNm+"</option>";
})
$('#prodLgu').append(str);
}
})
$(".ck-blurred").keydown(function(){
console.log("str : " + window.editor.getData());
$("#prodDetail").val(window.editor.getData());
});
$(".ck-blurred").on("focusout",function(){
$("#prodDetail").val(window.editor.getData());
});
$('#prodLgu').on('change',function(){
let lgu = $('#prodLgu').val();
// prodId 생성
$.ajax({
url : "/prod/createProdId",
data : JSON.stringify({"prodLgu" : lgu}),
contentType : 'application/json',
type : "post",
success : function(res){
$('#prodId').val(res);
}
})
// buyer select 설정
$.ajax({
url : "/prod/buyer",
data : JSON.stringify({"buyerLgu" : lgu}),
contentType : 'application/json',
type : "post",
success : function(res){
console.log(res);
let str="<option value='' selected disabled>선택해주세요</option>";
$.each(res, function(){
str += "<option value='"+this.buyerId+"'>"+this.buyerName+"</option>";
})
if(res.length==0){
str = "<option value='' selected disabled>해당하는 거래처가 없습니다</option>"
}
$('#prodBuyer').html(str);
}
})
})
// reset시 buyer select도 리셋
$('#reset').on('click',function(){
let str="<option value='' selected disabled>상품 분류를 선택해주세요</option>";
$('#prodBuyer').html(str);
})
})
</script>
'Spring' 카테고리의 다른 글
[Spring] JDBC 이용한 인증 (0) | 2024.08.12 |
---|---|
[Spring] 시큐리티 (0) | 2024.08.09 |
[Spring] 트랜잭션 관리 (0) | 2024.08.09 |
[Spring] 상품 상세 - 수정,삭제 (0) | 2024.08.09 |
[Spring] 상품 상세 정보 (Detail) (0) | 2024.08.08 |