본문 바로가기
Spring

[Boot] 스프링부트 기초 + 데이터 유효성 검증

by amoomar 2022. 4. 25.
반응형

해당 포스팅의 목차는 아래와 같다.

1. 파일 생성
   1) html
   2) jsp
   3) Controller

2. 데이터 유효성 검증_Validator
   1) Validator(공통)
   2) ver01_결합도 높음
   3) ver02_결합도 감소

 

1. 파일 생성(복습)

 

1) html

.html이라는 확장자를 가질 수 있는 파일 형식이 존재하지 않기 때문에, file형식으로 선택한 후 뒤에 확장자를 수기로 작성해주어야한다. 또한 file생성시 내부가 비어있으므로, 양식을 채워줄 필요가 있다.

 

채워주어야할 양식 코드를 첨부하였다.

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

</body>
</html>

 

 

 

처음 프로그램을 실행하면 아래와 같은 에러가 발생하는 것을 확인할 수 있다. 요청 경로가 명확하지 않음으로 인해 발생한 에러이다. 최초 브라우저를 동작시키게 되면 아래와 같이 출력된다. 정상 출력을 위해 요청 경로를 확인할 필요가 있다.

 

 

 

 

설명을 위해 본인이 생성한 각 html파일의 생성 위치를 공유하자면 아래와 같다.

html 파일 위치

 

 

 

 

 

url을 주의깊게 살필 필요가 있다.

page01.html

 

 

 

 

마찬가지로 url을 주의깊게 살필 필요가 있다.

page02.html

 

 


 

 

2) jsp

 

 

① build.gradle

jsp사용을 위해 미리 DI해야할 자원들이 필요하다. 다음은 build.gradle의 내용이다. 주석을 통해 기존 내용과 추가된 내용을 분리하여 확인할 수 있다.

plugins {
	id 'org.springframework.boot' version '2.6.7'
	id 'io.spring.dependency-management' version '1.0.11.RELEASE'
	id 'java'
	id 'war'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'//JSP를 해석할 수 있는 파서
	implementation 'javax.servlet:jstl'//JSTL사용을 위한 자원
}

tasks.named('test') {
	useJUnitPlatform()
}

 


 

 

② application.properis

해당 파일에 경로 설정에 대한 내용을 기술한다.

# JSP관련 설정 -> prefix의 경로에 suffix라는 형식의 파일이 생성할 것이라는 명령
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

 

 

 

위에서 지정한대로 main폴더 하위에 추가적으로 폴더들이 필요하므로 생성해준다.

폴더 생성

 

 


 

 

 

③ jsp파일

마찬가지로 file형식을 생성하고, 확장자를 .jsp로 지정한다. 그리고 내부의 양식을 붙여넣는다. 양식에 대한 코드를 하단에 첨부하였다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

</body>
</html>

 

 

 

 


 

 

3) Controller

실습을 복습 개념으로 진행하며 코드에 대한 내용을 주석으로 확인할 수 있도록 하였다.

package com.example.demo;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class MyController {

	// 루트 기능을 하는 메서드 : 디폴트 경로를 찍어주는 메서드
	@RequestMapping("/")
	public @ResponseBody String root() { // 어노테이션으로 인해 "value값이니까 화면에 그냥 보여주기만 해"라는 의미로 해석된다.
		return "JSP 실습"; //viewResolver에 의해 /WEB-INF/views/return값.jsp로 될테니 그걸 바꿔주자
	}
	
	@RequestMapping("/page03") // localhost:8080/page03이라고 될 예정(이때 suffix를 설정하였으므로 jsp를 생략해도 무관하다)
	public String page03(MemberVO memberVO) { // command 객체이다. -> page03.jsp에서 ${vo.멤버변수명}으로 호출이 가능하다.
		return "page03"; //viewResolver에 의해 /WEB-INF/views/page03.jsp을 호출할 예정
	}
	
	@RequestMapping("/page04") // "/page04/{name}/{phone}"이런 식으로도 많이 사용한다. 경로 자체에 변수를 넣는다.
	public String page04(MemberVO vo, Model model) {
		
		model.addAttribute("name", vo.getName());
		model.addAttribute("name", vo.getPhone());
		
		return "page04";
	}
	
//  아래의 메서드와 위의 메서드의 기능은 동일하다.	
//	@RequestMapping("/page04/{name}/{phone}") // 경로 자체에 변수를 넣는다.
//	public String page04(@PathVariable String name, @PathVariable String phone, Model model) {
//		
//		model.addAttribute("name", name);
//		model.addAttribute("phone", phone);
//		
//		return "page04";
//	}
	
}

 

 

 

 

각 페이지에서 ulr을 활용한 파라미터 값 전달로 출력이 잘 되는 것을 확인할 수 있다.

page03

page03의 경우 처음에 command객체명(MemberVO)을 vo로 설정했었는데, controller에서 값이 잘 넘어오는 것은 확인되나 ${vo.name}등으로 호출했을때 출력이 안되는 상황이 발생했다. 혹시나 하는 마음에 vo를 memberVO로 변경해서 사용하니 출력이 잘 되는 기적..... 이거 왜인지 아시는 분, 댓글로 공유해주시면 감사하겠습니다......

 

 

 

page04

 

 

 

 


 

 

 

2. 데이터 유효성 검증_Validator

Validator(관리데이터 클래스)를 활용하여 객체가 제대로 요구사항에 맞추어 생성 됐는지 검증할 수 있다.

유효성 검증에 대하여

1) html 페이지, 클라이언트, 브라우저 : JS를 통해서 검증을 진행, 즉 서버의 부하를 줄일 수 있다.(유효한 요청만이 들어온다.)
2) 서버, jsp페이지, servlet : 악의적인 접근(url)에 대해 파라미터로 넘어온 값을 검증한다.

 

 

 

1) Validator(공통)

실습 순서에 따라 단계별로 나누어 정리하였다.

 

 

① validator클래스 추가를 위한 자원 생성

 

build.gradle파일의 dependencies영역 최하단의 아래의 코드를 붙여넣음으로써 자원을 생성해준다.

implementation 'org.springframework.boot:spring-boot-starter-validation'//데이터 유효성 검증을 위한 자원

 

 


 

② 실습을 위한 JSP페이지 생성

 

val01.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>01</title>
</head>
<body>

	<form action="/val">
		<input type="text" name="name" value="${memberVO.name}">
		<input type="text" name="phone" value="${memberVO.phone}">
		<input type="submit" value="검증하기">
	</form>

</body>
</html>

 

 

val02.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>02</title>
</head>
<body>

	완료!

</body>
</html>

 

val01.jsp에서 input에 유효성 검사에 따른 값이 입력 되었을때 비로소 val02.jsp로 이동될 수 있도록 할 예정이다.

 

 

 


 

 

③ 유효성 로직 클래스(Validator클래스)

유효성 검사 진행을 위한 로직 파일을 생성한다. 이때 해당 클래스파일이 Validator클래스의 기능을 수행할 수 있도록 하기 위해 implements를 진행하고, 각 추상메서드들에 알맞은 내용들을 기입한다. 자세한 내용은 주석을 통해 파악이 가능하다.

package com.example.demo;

import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

public class MemberValidator implements Validator{

	@Override
	public boolean supports(Class<?> clazz) { //어떤 클래스를 검증할 것인지를 설정
		return MemberVO.class.isAssignableFrom(clazz); // 검증할 객체의 클래스 기입
	}

	@Override
	public void validate(Object target, Errors errors) { 
		// target : 검증대상, errors: 어떤 에러가 발생했는지를 알려줌
		// ★return타입이 void인것으로 보아, 에러에 해당하는 객체를 보내줘야 참조가 가능할 것을 예상할 수 있다.
		MemberVO vo=(MemberVO)target; //캐스팅
		
		String name=vo.getName();
		String phone=vo.getPhone();
		
		// 검증을 위한 로직, 다양한 로직으로 체크할 수 있다.
		if(name==null || name.trim().isEmpty()) { //trim()->스페이스바 여러개를 입력한 공백을 잡아줌
			System.out.println("로그: name 값이 비어있습니다.");
			errors.rejectValue("name", "error");
		}
		if(phone==null || phone.isEmpty()) {
			System.out.println("로그: phone 값이 비어있습니다.");
			errors.rejectValue("phone", "error");
		}
	
	}

}​

 

 


 

 

2) ver01_결합도 높음

위 객체의 메서드 결과에 따라 페이지를 다르게 반환할 수 있도록 controller로 작업한다.

	@RequestMapping("/val")
	public String val(@Valid MemberVO vo, BindingResult result) { 
		// @Valid: 해당 객체를 검증하겠다는 의미의 어노테이션, 유효성 검사를 수행하라는 요청이다.
		System.out.println("c: "+vo); // 컨트롤러에 넘어온 결과를 확인할 목적의 로그
		
		MemberValidator validator = new MemberValidator(); //결합도가 높음
		validator.validate(vo, result); // ☆result객체를 인자로 전달해줬기 때문에 참조가 가능하다.
		
		String viewName = "val01";
		if(!result.hasErrors()) { // 성공했다면, (hasErrors: 에러가 발생했을때 true반환)
			viewName="val02";
		}
		
		return viewName;
	}

 

result와 vo가 MemberValidator객체의 validator메서드에 전달되어 반환값은 void임에도 result라는 객체를 통해 메서드 수행 결과를 참조할 수 있게 된다.

 

 

이때 위의 코드에서 객체가 초기화 되고 있는 모습을 확인할 수 있는데, 이는 /val요청이 있을때마다 객체의 초기화가 진행될 것이기 때문에 비효율적이라고 할 수 있다. 아래의 단계에서 이 코드의 결합도를 낮춤으로써 유지보수를 용이하도록 수정할 수 있다.

 

 

 


 

 

3) ver02_결합도 감소

위의 코드에서 수정작업을 진행하여 결합도를 낮추는 코드로 작업할 예정이다. 프로젝트 실행시 최초로 한 번 동작할 수 있도록 코드를 변경한다.

	@RequestMapping("/val")
	public String val(@Valid MemberVO memberVO, BindingResult result) { 
		// @Valid: 해당 객체를 검증하겠다는 의미의 어노테이션, 유효성 검사를 수행하라는 요청이다.
		System.out.println("c: "+memberVO); // 컨트롤러에 넘어온 결과를 확인할 목적의 로그
		
//		MemberValidator validator = new MemberValidator(); //결합도가 높음
//		validator.validate(memberVO, result); // ☆result객체를 인자로 전달해줬기 때문에 참조가 가능하다.
		
		String viewName = "val01";
		if(!result.hasErrors()) { // 성공했다면, (hasErrors: 에러가 발생했을때 true반환)
			viewName="val02";
		}
		
		return viewName;
	}
	
	@InitBinder // 프로젝트가 시작할때 먼저 수행할 메서드임을 선언
	protected void initBinder(WebDataBinder binder) {
		binder.setValidator(new MemberValidator()); // 한 번 new 한 것을 주입받아서 계속 사용할 수 있도록하여 결합도를 낮춤
	}

위의 코드를 통해 MemberValidator객체가 요청시마다 계속해서 초기화 되지 않고, 프로젝트 시행시 한 번 초기화 된 후 재사용됨으로써 결합도가 감소한 모습을 확인할 수 있다.

 

 

 

콘솔을 통해 값이 입력되지 않아 에러로 인식한 경우 로그를 통해 어떤 파라미터의 값이 비어있는지를 확인할 수 있다.

콘솔 로그

 

 

정상적으로 입력된 경우 val02.jsp페이지로 이동되는 모습을 확인할 수 있다.


 

반응형