공부/Spring
[Spring] 회원관리 연습 웹 프로젝트 - 1 / 스프링 폼, 스프링 유효성 @Valid
thegreatjy
2023. 12. 30. 02:15
728x90
spring framework를 사용하여 회원 관리 웹 프로젝트를 만들겠다.
공부한 내용을 정리하며 프로젝트를 진행하겠다.
기능 목표
- 스프링 폼 사용
- 스프링 시큐리티 적용
- 스프링 유효성 검사
- 에러 화면 처리
회원가입 폼 화면 (스프링 폼)
- 폼에서 전달되는 파라미터 이름으로 setter()메서드를 작성한 클래스의 프로퍼티(멤버변수)에 접근할 수 있다.
- jsp 위에 스프링 폼 태그 라이브러리를 선언해야 한다.
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
- modelAttribute 속성은 input, hidden 태그들이 커맨드 객체의 프로퍼티(멤버변수)에 접근할 수 있게 한다.
- action 속성의 기본값은 현재 요청 url, method 속성 기본값은 post.
- 자바 클래스 생성
@Getter
@Setter
@ToString
public class MemberDTO {
private String email;
private String password;
private String name;
private Integer age;
private String mobile;
}
- setter()가 있어야 한다.
- jsp - 스프링 폼 태그
<%--@elvariable id="memberDTO" type="kr.co.chunjae.domain.MemberDTO"--%>
<form:form modelAttribute="memberDTO" method="post" action="/save">
<p>아이디 : <form:input path="email" name="email" /></p>
<p>비밀번호 : <form:password path="password" name="password"/></p>
<p>이름 : <form:input path="name" name="name" /></p>
<p>나이 : <form:input path="age" name="age" /></p>
<p>전화번호 : <form:input path="mobile" name="mobile" /></p>
<input type="submit" value="가입하기"/>
<input type="reset" value="다시작성"/>
</form:form>
- 위에 애너테이션 주석을 작성하면 path를 입력할 때, modelAttribute의 커맨드 객체에 빨간색이 지워지며, 자동완성이 된다! (intellij)
- modelAttribute 속성값은 controller의 model.addAttribute(”키”, “밸류”);에서 키 값이랑 일치시켜야 한다.
- submit 버튼은 일반 input 태그를 사용한다.
- controller
@GetMapping("/save")
public String saveForm(Model model){
MemberDTO memberDTO = new MemberDTO();
model.addAttribute("memberDTO", memberDTO);
return "save";
}
- 객체를 생성해준 후, model에 추가하여 jsp 화면을 출력해야 한다.
- model.addAttribute(”키”, “값”);에서 키 값과 jsp 스프링 폼 태그에서의 modelAttribute 값이 동일해야 커맨드 객체에 맵핑이 된다.
회원가입 처리
@ModelAttribute
- 폼 페이지에서 전달된 파라미터 값이 @ModelAttribute가 설정한 커맨드 객체에 자동으로 할당된다. (= 데이터 바인딩) 그리고 해당 커맨드 객체의 프로퍼리 값을 뷰 페이지에 출력한다.
- 파라미터 @ModelAttribute
- 커맨드 객체에 맵핑되어 프로퍼티에 데이터를 채운다.
@PostMapping("/save")
public String save(@ModelAttribute("member") MemberDTO memberDTO, Model model){
log.info(memberDTO);
int result = memberService.save(memberDTO);
log.info(result);
if(result == 1){ // 회원가입 성공
return "redirect:/";
}else{
model.addAttribute("msg", "다시 시도해 주세요.");
return "save";
}
}
- @ModelAttribute(”키”)의 키 값은 jsp form 태그의 modelAttribute 값과 동일해야 한다.
- 메서드 @ModelAttribute
- 뷰 페이지에서 공통으로 사용할 수 있는 커맨드 객체의 프로퍼티(멤버변수, 필드)를 설정하여 뷰 페이지에 출력한다.
- 웹 요청 url을 처리할 수 없지만, 먼저 호출되며 컨트롤러 안에 여러 개 만들 수 있다.
- controller
@ModelAttribute("msg")
public String setMsg(){
return "회원가입 페이지";
}
- jsp
${msg}
선택적 바인딩
@InitBinder
- setAllowedFields() 메서드 : 폼 파라미터 이름들을 선택하여 데이터 바인딩을 허용한다.
- setDisallowedFields() 메서드 : 데이터 바인딩을 허용하지 않는다.
- setAllowedFields
@InitBinder
public void initBinder(WebDataBinder binder){
binder.setAllowedFields("id", "password", "city");
// 폼 태그의 파라미터를 선택적 바인딩 허용
}
- setDisallowedFields
@InitBinder
public void initBinder(WebDataBinder binder){
binder.setDisallowedFields("hobby");
// 폼 태그의 파라미터를 선택적 바인딩 방지
}
스프링 유효성 검사
- 자바 유효성 검사 : JSR-380
- 필드에 대한 유효성 검사 제약사항 애너테이션을 선언
- Validator 인터페이스 구현
- Validator 인스턴스를 사용하여 해당 속성값의 유효성 검사를 수행
- Validator 인터페이스는 애플리케이션의 모든 계층에서 유효성 검증을 위해 사용할 수 있다.
- pom.xml 의존성 라이브러리 등록
<!-- Validation -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.3.Final</version>
</dependency>
- servlet-context.xml에 컨트롤러 맵핑 등록
<annotation-driven />
제약 사항을 위반하여 오류가 발생하면 오류 메세지를 출력하도록 만든다.
JSR-380 사용한 유효성 검사
- 도메인 클래스의 프로퍼티(멤버 변수), 즉 필드에 대해 유효성 검사의 제약 사항 애너테이션을 선언하여 해당 필드 값이 올바른지 검사한다.
- JSR-380 애너테이션은 Hibernate Validator가 제공하는 애너테이션을 그대로 따른다. 즉, 개발자는 이미 정해진 규격에 맞춰서 유효성 검사를 수행해야 한다.
- 방법
- 도메인 객체에 @JSR-380 제약 사항 애너테이션 선언, 오류 메세지 설정.
- 요청 처리 메서드(컨트롤러 내 메서드)의 매개변수에 @Valid 선언
- 뷰 페이지에 폼 태그 라이브러리 중 form:errors 태그를 사용하여 오류 메세지 출력
회원 객체의 필드에 제약 사항 애너테이션 적용
@Getter
@Setter
@ToString
public class MemberDTO {
@NotNull
@Size(min=1, max=50)
@Pattern(regexp = "^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$")
private String email;
@Size(min=1, max=50, message = "1자 이상 50자 이하를 입력해 주세요.")
private String password;
@Size(min=1, max=30)
private String name;
@Min(value=1)
private Integer age;
@Size(max=30)
private String mobile;
}
참고 : https://www.baeldung.com/java-validation
- DecimalMin 과 Min 의 차이
- @DecimalMin(value=”99999”) : 문자열로 값을 제한 → 더 큰 값을 지정할 수 있다.
- @Min(value=9999) : int 숫자로 값을 제한
- @Size(min=0, max=10) : 문자열의 길이를 제한
- @NotNull, @NotEmpty
- @NotNull : null이 아니어야 한다.
- @NotEmpty : null 혹은 빈 문자열이 아니어야 한다.String, Collection, Map or Array 에 적용할 수 있다.
- Pattern(regexp=”정규식”) : 정규 표현식 패턴과 일치해야 한다.
사용자 정의 오류 메세지 설정
- message 속성을 사용
@Size(min=1, max=50, message = "1자 이상 50자 이하를 입력해 주세요.")
private String password;
- 메세지 리소스 파일(messages.properties)를 만듦
- 애너테이션.커맨드객체이름.필드이름 = 오류 메세지
- 커맨드 객체 이름은 modelAttribute(”커맨드객체이름”)과 동일해야 한다.
- 즉, jsp에서 <form:form modelAttribute=”여기”>
- 컨트롤러 내 메서드의 파라미터 @Valid @ModelAttribute(”여기”)
- 메세지 리소스 파일의 애너테이션이름.여기.필드명
- 이 동일해야 한다.
- 애너테이션 = 오류메세지
// src/main/resources/messages.properties 파일 생성
NotNull.member.email = \uC774\uBA54\uC77C\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694.
Size.member.email = 1\uC790 \uC774\uC0C1 50\uC790 \uC774\uD558\uB85C \uC785\uB825\uD574 \uC8FC\uC138\uC694.
Pattern.member.email = \uC774\uBA54\uC77C \uD615\uC2DD\uC744 \uB9DE\uCD94\uC5B4 \uC8FC\uC138\uC694.
Size.member.password = 1\uC790 \uC774\uC0C1 50\uC790 \uC774\uD558\uB85C \uC785\uB825\uD574 \uC8FC\uC138\uC694.
Size.member.name = 1\uC790 \uC774\uC0C1 30\uC790 \uC774\uD558\uB85C \uC785\uB825\uD574 \uC8FC\uC138\uC694.
Min.member.age = 1 \uC774\uC0C1\uC758 \uC22B\uC790\uB97C \uC785\uB825\uD574 \uC8FC\uC138\uC694.
Size.member.mobile = 1\uC790 \uC774\uC0C1 50\uC790 \uC774\uD558\uB85C \uC785\uB825\uD574 \uC8FC\uC138\uC694.
- 인텔리제이 setting>plugin>unicode escaper 설치하면 편리하게 한글을 유니코드로 변경할 수 있다.
- servlet-context.xml에 빈 등록을 해야 한다.
// servlet-context.xml
<!-- 유효성 검사 메세지 -->
<beans:bean id= "messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<beans:property name="basename" value="messages"/>
<beans:property name="defaultEncoding" value="UTF-8" />
</beans:bean>
컨트롤러의 요청 처리 메서드 내 매개변수 @Valid
- 폼 페이지에서 입력된 값이 커맨드 객체로 맵핑될 때, 유효성 검사가 실행된다.
- 오류가 발생하면 Errors 객체에 오류 결과 값이 저장된다.
- Errors 혹은 BindingResult 는 바인딩 받는 객체 바로 다음에 선언!!!!!!
import org.springframework.validation.Errors;
import javax.validation.Valid;
@PostMapping("/save")
public String save(@Valid @ModelAttribute("member") MemberDTO memberDTO, Errors errors, Model model){
// 유효성 검사 오류 결과 확인
if(errors.hasErrors()){
return "save";
}
}
form:errors 태그로 오류 메세지 출력
- message 속성에 개발자가 정의한 오류 메세지를 출력하거나, 기본 메세지를 출력한다.
- <form:errors path = “필드이름” /> : 해당 필드 이름에 연관된 모든 오류를 출력한다.
- <form:errors path = “*” /> : 모든 오류를 출력한다.
// save.jsp
<%--@elvariable id="member" type="kr.co.chunjae.domain.MemberDTO"--%>
<form:form modelAttribute="member" method="post" action="/member/save">
<p>아이디 : <form:input path="email" name="email" /> <form:errors path="email"/></p>
</form:form>
= trouble shooting =
- 에러 1
Resolved [org.springframework.validation.BindException 에러 발생
- 해결방법
- 버전 변경 (이건 영향이 없어보인다.)
<groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>7.0.4.Final</version>
- 컨트롤러의 요청 처리 메서드의 파라미터에서 Errors 혹은 BindingResult 순서를 모델 바로 뒤로 바꾼다. (어이없네 예민하네)
@PostMapping("/save") public String save(@Valid @ModelAttribute("member") MemberDTO memberDTO, Errors errors, Model model){} // 원래 MemberDTO memberDTO, Model model, Errors errors 순서이었다.
[https://dmaolon00.tistory.com/entry/Error-해결-orgspringframeworkvalidationBindException-orgspringframeworkval](https://dmaolon00.tistory.com/entry/Error-%ED%95%B4%EA%B2%B0-orgspringframeworkvalidationBindException-orgspringframeworkval)
- 에러 2
- 메세지 리소스 파일의 오류 메세지가 나오지 않고 기본 메세지가 출력됨.
- 해결방법
- jsp, 컨트롤러, 메세지 리소스 파일의 커맨드 객체 이름을 동일하게 맞춘다.
- 즉, jsp에서 <form:form modelAttribute=”여기”>
- 컨트롤러 내 메서드의 파라미터 @Valid @ModelAttribute(”여기”)
- 메세지 리소스 파일의 애너테이션이름.여기.필드명
- 이 동일해야 한다.
= 깃허브에서 전체 코드 확인하기 =
https://github.com/thegreatjy/ChunjaeFullStack/tree/main/Spring_Study/memberFrameWorkPractice
728x90