728x90
Valiadation (유효성 검사)
- validation이란 어떤 데이터의 값이 유효한지, 타당한지 확인하는 것을 의미
- UI에서 javascript로 "이메일 양식이 일치하지 않는다"는 것은 UX 측면에서 사용자에게 편의를 주기 위함 (한계 有)
- 보안적인 측면에서 유효성 검사는 UI, 서버에서 둘 다 수행되어야 함
서버에서 secure coding - 스프링, 스프링 부트 모두 적용 가능
@ 어노테이션
- @어노테이션은 VO(DTO) 클래스의 멤버변수에 적용해서 사용
- Import는 javax.validation패키지를 사용
어노테이션 | 설명 | 적용대상 |
@NotNull | null을 제외, 공백 허용 | String, Long, Integer등등 전부 검사 가능 |
@NotBlank | null, 공백을 허용하지 않음 | String만 적용가능 |
@NotEmpty | null을 허용하지 않음 | String, Map, Array에 검사 가능 |
@Pattern | 정규표현식에 맞는 문자열 정의 가능 | String만 적용가능 |
email형식이어야 함 | 공백을 통과 |
컨트롤러에서 적용
- 컨트롤러에서는 데이터를 받을 때 @Valid와 Error객체를 사용
- 꼭 사용해야 한다는 아님
설명 | 표현식 | 클래스명 |
hasErrors() | 바인딩된 에러 있으면 true | Errors |
getFieldErrors() | 유효성 검사에 실패한 필드 목록 확인 | Errors |
getField() | 유효성 검사에 실패한 변수명 확인 | FieldErrors |
getDefaultMessage() | 유효성 검사에 실패한 변수의 에러 메시지 확인 | FieldErrors |
isBindingFailure() | 유효성 검사에 바인딩이 안 된 경우 false | FieldErrors |
라이브러리 다운
build.gradle
//유효성 검사
implementation 'org.springframework.boot:spring-boot-starter-validation'
- 유효성 검사 모듈 다운로드
사용 방법
- VO(DTO) 클래스의 멤버변수에 적용하여 사용하기 때문에 VO 생성
ValidVO.java
package com.example.basic.command;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ValidVO {
/*
* @NotNull - null값만 허용하지 않음, 공백으로 주면 에러 안남 (wrapper의Integer, Long, String 등)
* @NotBlank - null값과 공백 허용하지 않음 (String에만 적용)
* @NotEmpty - null값을 허용하지 않음 (Array, list 적용)
* @Pattern - 정규표현식에 맞는 문자열을 정의할 수 있음 (String에만 적용)
*
* @Email - 이메일 형식 검증 (공백은 통과)
* @Min - 최소값
* @Max - 최대값
*/
//데이터가 넘어올 때 값이 공백이면 자동으로 에러가 넘어오게끔
@NotBlank(message = "이름은 필수 입니다")
private String name;
//숫자, 실수형의 원시타입은 기본값이 0이라서 공백 맵핑이 불가능하기 때문에 래퍼타입으로 선언하는 편이 좋습니다.
@NotNull(message = "급여는 필수 입니다")
private Integer salary; //int는 null을 가질 수 없음
//원시타입으로 하면 기본값 0, wrapper타입으로 하면 기본값 null
@Pattern(regexp = "[0-9]{3}-[0-9]{4}-[0-9]{4}", message = "전화번호 형식은 000-0000-0000 입니다")
private String phone;
@NotBlank //동시에 적용
@Email(message = "email 형식이어야 합니다") //email 형식 이어야 함, 단 공백은 통과
private String email;
}
- int 타입에 @NotNull을 넣으면 parsing error 일어남
여기서 잡히는 getErrorcount는 유효성 검사 error가 아님
int는 null을 가질 수가 없으므로 " "으로 들어오는 값을 0으로 치환시킬 때 내부적으로 parsing error가 일어남 - converting error - 값 치환 과정에서 생기는 에러
ex01.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"> <!-- 타임리프 명시적 선언 -->
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3>유효성 검사</h3>
<form action="actionForm" method="post">
<!-- 처음 화면에 진입할 때는 vo가 없음 -->
<!-- null인 경우 에러가 발생하게 되기 때문에 null처리를 진행해야 합니다. -->
이름: <input type="text" name="name" th:value="${vo != null ? vo.name : ''}"/>[[${valid_name}]]
급여: <input type="text" name="salary" th:value="${vo != null ? vo.salary : ''}"/>[[${valid_salary}]]
전화번호: <input type="text" name="phone" th:value="${vo != null ? vo.phone : ''}"/>[[${valid_phone}]]
이메일: <input type="text" name="email" th:value="${vo != null ? vo.email : ''}"/>[[${valid_email}]]
<input type="submit" value="확인"/>
</form>
</body>
</html>
- 처음 화면에 진입할 때는 유효성 검사를 할 VO가 없기 때문에 null 처리를 해야 함
th: 타임리프 사용할 명시적 선언 후 value에 사용
ValidController.java
package com.example.basic.controller;
import java.util.List;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.example.basic.command.ValidVO;
@Controller
@RequestMapping("/valid")
public class ValidController {
//ex01 화면구현
@GetMapping("/ex01")
public String ex01() {
return "/valid/ex01";
}
//폼태그로 넘김
//@Valid - 유효성 검사를 진행, Errors - 유효성 검사에 실패하면 에러 객체로 바인딩
@PostMapping("/actionForm")
public String actionForm(@Valid ValidVO vo, Errors error, Model model) {
//System.out.println(error.getErrorCount()); //에러 개수
if(error.hasErrors()) { //에러가 있다면 true, 에러가 없다면 false
List<FieldError> list = error.getFieldErrors(); //에러가 발생된 목록
//System.out.println(list.toString());
for(FieldError err : list) {
//System.out.println(err.getField()); //에러 필드명
//System.out.println(err.getDefaultMessage()); //에러 메세지
if(err.isBindingFailure()) { //유효성 검사의 실패가 아니라, 자바 내부의 에러라면 true 반환
//System.out.println("자바 내부 에러 발생"); //에러 필드명
model.addAttribute("valid_" + err.getField(), "형식이 올바르지 않습니다");
} else { //유효성 검사에 실패한 목록
model.addAttribute("valid_" + err.getField(), err.getDefaultMessage());
}
} //end
//사용자가 적은 값은 VO에 담김
model.addAttribute("vo", vo); //사용자가 작성한 값을 화면으로
return "valid/ex01"; //원래 화면으로
}
System.out.println(vo.toString());
return "/valid/ex01_result";
}
}
- 컨트롤러로 ex01.html 파일 화면 구현
- 폼 태그의 action으로 post 방식으로 넘김 = actionForm
- 매개변수로 @Value ValidVO - 유효성 검사 진행
Errors - 유효성 검사 실패시 에러 객체로 바인딩
Model - 꺼내 올 model 객체
를 담을 수 있음 - 조건문으로 error.hasError() 에러가 존재하면 에러 발생 목록들을 담고,
반복 돌려서 처리 ▶ Model 객체에 에러 메세지를 담음
isBindingFailure - 자바 내부의 에러
getField() - 에러 필드명, 에러난 멤버변수 이름
getDefaultMessage - 에러 메세지 - model.addAttribtue로 넘어온 값을 Model 객체에 담아서 던지기
- [[${변수명}]] 사용하면 HTML 태그 내에서 태그 사이의 값 출력 가능
valid_ + err.getField() ▷ valid_name, valid_salary 등
ex01_result.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3>결과 화면</h3>
</body>
</html>
mybatis
Mybatis-boot-start
- 스프링부트의 autoconfig 기능은 데이터베이스 연결과 커넥션 풀 연결을 자동처리
아래의 Mybatis-boot-start 이용 - 스프링 부트 위에 MyBatis 애플리케이션을 빠르게 빌드 가능
@Mapper만 사용하여 인터페이스 위치만 알려주면 자동 처리
자동처리 부분
1. DataSource를 자동감지
2. SqlSessionFactory 자동 생성
3. @Mapper 어노테이션을 스캔하고 자동 연결 (MyBatis의 인터페이스로 사용)
build.gradle
//마이바티스
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2'
- 버전 명시 필수 - 스프링부트 버전 레벨에 맞춰줌
Boot 2 버전 = mybatis 2 버전
application.properties - 추가 설정
################### 마이바티스 관련 설정 ###################
# 매퍼 xml의 위치 - classpath:/ 리소스 폴더의 하위를 나타냄
mybatis.mapper-locations=classpath:/mapper/*.xml
# 단축명으로 사용할 클래스의 패키지명
mybatis.type-aliases-package=com.example.basic.command
- classpath:/ - 리소스 폴더 아래의 경로 의미
- vo의 경로를 지정하면 mapper에서 클래스 이름으로 단축명을 사용 가능
원래는 type 쓸 때 풀 경로로 들어가야하는데 이름만 적어도 되게끔
SQL 실행 로그
- sql 로그를 출력해서 개발 환경(개발 끝나면 없애기)에서 보기 쉽게 하려고 사용
- 블로그 참고
https://huelet.tistory.com/entry/Spring-boot-SQL-%EB%A1%9C%EA%B7%B8-%EC%84%A4%EC%A0%95Log4j2
build.gradle
//slq로그
implementation 'org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16'
application.properties
spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
spring.datasource.url=jdbc:log4jdbc:mysql://localhost:3306/spring?serverTimezone=Asia/Seoul
spring.datasource.username=spring
spring.datasource.password=spring
- Database config 변경된 부분
net.sf.log4jdbc.sql.jdbcapi.DriverSpy
jdbc:log4jdbc
- 아래 두개 파일 resource 폴더 아래에 생성
log4jdbc.log4j2.properties
log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
log4jdbc.dump.sql.maxlinelength=0
logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyyMMdd HH:mm:ss.SSS} [%thread] %-3level %logger{5} - %msg %n</pattern>
</encoder>
</appender>
<logger name="jdbc" level="OFF"/>
<logger name="jdbc.sqlonly" level="OFF"/>
<logger name="jdbc.sqltiming" level="DEBUG"/>
<logger name="jdbc.audit" level="OFF"/>
<logger name="jdbc.resultset" level="OFF"/>
<logger name="jdbc.resultsettable" level="DEBUG"/>
<logger name="jdbc.connection" level="OFF"/>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
- 선택적으로 골라서 체크 가능 - 불필요한 부분 제거
- 옵션설명
- jdbc.sqlonly : SQL문만을 로그로 남기며, PreparedStatement일 경우 관련된 argument 값으로 대체된 SQL문이 보임
- jdbc.sqltiming : SQL문과 해당 SQL을 실행시키는데 수행된 시간 정보(milliseconds)를 포함
- jdbc.audit : ResultSet을 제외한 모든 JDBC 호출 정보를 로그로 남김
- jdbc.resultset : ResultSet을 포함한 모든 JDBC 호출 정보를 로그로 남김
- jdbc.resultsettable : SQL 결과 조회된 데이터의 table을 로그로 남김
Mapper와 인터페이스
- 나머지는 Spring과 같음
2023.02.06 - [Server/Spring] - [Spring] ??? MyBatis, Mapper, Board 실습
728x90
'Server > Spring boot' 카테고리의 다른 글
[Spring Boot] BootMyweb (3) - 파일 업로드, 불러오기, selectKey (0) | 2023.02.21 |
---|---|
[Spring Boot] RestAPI, 부메랑, @RestController, @RequestBody, CrossOrigin (0) | 2023.02.16 |
[Spring Boot] BootMyweb (1) - 관리자 홈페이지 / 기본 연결, 검색&Paging (0) | 2023.02.15 |
[Spring Boot] thymeleaf, ${#내장함수}, inline, 템플릿 (0) | 2023.02.13 |
[Spring Boot] 설치 및 개발환경 구축, 기본, gradle, 개별적인 bean설정, TestCode, Builder(Lombok) (0) | 2023.02.10 |