원지의 개발
article thumbnail
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 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 실행 로그

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
profile

원지의 개발

@원지다

250x250