본문 바로가기
학원/spring

1.4 (SpringMVC-로그인처리,회원가입,파일업로드,파일다운로드)

by 쿠룽지 2024. 1. 4.
728x90
반응형



복습

 

<설정파일 - 3개>

1. web.xml
dispatcherservlet를 호출
root-context, servlet-context 를 실행시켜서 서버가 동작할 수 있게끔 도와줌
2. root-context -- db연동
3. servlet-context -- 빈 설정


 

ModelAndView
--> 객체 생성 후 데이터 자동 저장
jsp할때 request, response등을 사용할때는 계속 수작업으로 하나하나 했어야 하는데 스프링에서는 ModelAndView 객체를 이용해서 보다 쉽게 사용할 수 있음


@RequestParam
--> 전송된 데이터를 request에서 빼내는 역할 (요청파라미터와 이름이 같으면 생략가능)


 

kr.spring.ch02.controller
SearchController

searchExternal메서드 -> 타입이 다른 인자 2개를 get방식으로 보낼 때

index.jsp 에서
/search/external.do?query=seoul&p=5 라고 바로 명시 가능

 


만약 값을 안넣고 싶을 때에는
Integer  : required=false
@RequestParam(value="p", required=false) Integer pageNumber

int : defaultValue
@RequestParam(value="p" defaultValue="1") int pageNumber 사용


kr.spring.ch03.controller
NewArticleController

@ModelAttribute 생략한 구문
public String submit(NewArticleVO newArticleVO) {
--> 생략하게 되면 request에 저장되는 속성명은 클래스명의 첫글자를 소문자로 자동 생성

 

+++
@Autowired를 사용하게 된다면
설정파일에도
<context:annotation-config/>를 넣어야함


 

kr.spring.ch04.controller
CookieController

쿠키는 생성되지 않으면 바로 에러가 나기 때문에
@CookieValue(value="쿠키명",required=false) 또는
@CookieValue(value="쿠키명",defaultValue="0") 라고 명시해놓는게 좋음

 

@CookieValue 는 필수 명시해야하기 때문에!


kr.spring.ch05.controller
GameSearchController

검색했을 때
url = search/game.do?type=2&query=짱구

이건
1 개별적으로 처리
2 자바빈에 담아서 처리
방법이 두가지 있는데

학원에선 2번으로 처리했음


kr.spring.ch06.controller
CreateAccountController 유효성 체크

-커스텀 태그를 사용했기 때문에 보다 쉽게 자바빈과 연동할 수 있음 (필수 항목, 값 미리보기 기능)


-학습했던 빈칸 폼 대신 처음부터 데이터가 표시되어야 하는 경우라면 초기화+데이터 넣고 전송할 수 있음
근데 처음 호출했을 때에는 폼에 아무것도 안보이게 해야하기 때문에 기본적으로 비어있는 MemberInfo를 보내줘야 해서
CreateAccountController에서 초기화 작업을 먼저 진행한거임
@ModelAttribute("vo")
public MemberInfo initCommand() {
return new MemberInfo();
}

후에
if(result.hasErrors()) 를 실행해서 에러가 있으면 return "account/creationForm"; 처럼 폼을 다시 호출

+ creationForm.jsp에서 <form:errors path="id"/> 를 실행시켜서 errors 메시지가 나오게 출력
에러 메시지는 properties의 값이 나오게 됨 (servlet에서 리소스 번들 지정해야함!)

-servlet-context에서 리소스 번들 지정하는 것들은 다 properties 형식임
<beans:value>messages.validation</beans:value> 그래서 이렇게만 지정해도 properties로 인식함


 

 

 

로그인 처리 예제

로그인 처리 실패 -> 에러 -> 사용자 정의 예외 클래스 생성

예전에 순수 자바할 때 만들었던 경험이 있다고 하심 (난 왜 없지)

 

 

 

 

1. 로그인 폼 생성하기

컨트롤러

kr.spring.ch07.controller
LoginController (class)

package kr.spring.ch07.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class LoginController {
	@GetMapping("/login/login.do")
	public String form() {
		return "login/form";
	}
}

 

 

 

 

 

빈 설정

servlet-context.xml

<!-- 로그인 처리 -->
<beans:bean class="kr.spring.ch07.controller.LoginController"/>

 

 

 

 

 

 

뷰 파일

views
login (folder)
form.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인</title>
</head>
<body>
	<h1>로그인</h1>
	<form:form action="login.do" modelAttribute="loginVO">
		아이디 : <form:input path="userId"/>
		<form:errors path="userId"/>
		<br>
		비밀번호 : <form:password path="password"/>
		<form:errors path="password"/>
		<br>
		<form:button>전송</form:button>
	</form:form>
</body>
</html>

커스텀 태그 사용 + modelAttribute 를 이용하여 자바빈 연결

LoginController와 같은 url을 사용하지만(login.do) 얘는 post방식이라 충돌X (커스텀 태그는 기본값이 post방식)

 

 

 

 

 

자바빈 생성

kr.spring.ch07.vo
LoginVO

package kr.spring.ch07.vo;

public class LoginVO {
	private String userId;
	private String password;
	
	public String getUserId() {
		return userId;
	}
	public void setUserId(String userId) {
		this.userId = userId;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	
	@Override
	public String toString() {
		return "LoginVO [userId=" + userId + ", password=" + password + "]";
	}
}

 

 

 

 

 

다시 컨트롤러 수정

LoginController

package kr.spring.ch07.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;

import kr.spring.ch07.vo.LoginVO;

@Controller
public class LoginController {
	//자바빈 초기화
	@ModelAttribute
	public LoginVO initCommand() {
		return new LoginVO();
	}
	
	@GetMapping("/login/login.do")
	public String form() {
		return "login/form";
	}
}

자바빈 초기화 + 폼 불러오는 메서드 생성

 

 

 

 

 

실행파일

index.jsp

<a href="${pageContext.request.contextPath}/login/login.do">LoginController</a><br>

 

 

정상출력

 

 

 


 

 

 

 

 

2. 유효성 검사하기

Validator 만들기

kr.spring.ch07.validator
LoginVOValidator

package kr.spring.ch07.validator;

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

import kr.spring.ch07.vo.LoginVO;

public class LoginVOValidator implements Validator{

	@Override
	public boolean supports(Class<?> clazz) {
		return LoginVO.class.isAssignableFrom(clazz);
	}

	@Override
	public void validate(Object target, Errors errors) {
		LoginVO loginVO = (LoginVO)target; //object를 다운캐스팅
		if(loginVO.getUserId() == null || loginVO.getUserId().isEmpty()) {
								//필드		에러코드
			errors.rejectValue("userId", "required");
		}
		if(loginVO.getPassword() == null || loginVO.getPassword().isEmpty()) {
			errors.rejectValue("password", "required");
		}
	}
}

 

 

 

 

 

 

컨트롤러

LoginController

유효성체크 후 정상적으로 로그인이 되면 index로 redirect

package kr.spring.ch07.controller;

import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

import kr.spring.ch07.validator.LoginVOValidator;
import kr.spring.ch07.vo.LoginVO;

@Controller
public class LoginController {
	//자바빈 초기화
	@ModelAttribute
	public LoginVO initCommand() {
		return new LoginVO();
	}
	
	@GetMapping("/login/login.do")
	public String form() {
		return "login/form";
	}
	
	@PostMapping("/login/login.do")
	public String submit(LoginVO loginVO, BindingResult result) {
		System.out.println("데이터 전송 후 : " + loginVO);
		
		//데이터 유효성 체크
		new LoginVOValidator().validate(loginVO, result);
		
		//유효성 체크 결과 오류가 있으면 폼 호출
		if(result.hasErrors()) {
			return form();
		}
		return "redirect:/index.jsp";
	}
}

정상 로그인 성공 시 index.jsp 리다이렉트
유효성검사에서 걸리면 form이 다시 호출 + 오류메시지 출력

 

jsp에서 아무것도 안누르고 전송 -> 필수 항목 이 칸 옆에 뜨고 (validation.properties 에 이미 넣은 문구)

 

 

 

 

 

프로퍼티즈 파일 생성

messages(package)

validation.properties

에러코드(required)를 안 바꾼 상태에서 필드에 맞춰서 다른 문구가 나오게 할 수 있음
근데 그렇게 만들려면 에러코드를 만드는 규칙을 따로 설정해야함 (우선순위별로 상황에 따라 다르게 출력할 수 있도록)

만약 properties파일을 열었을 때 한글 대신 유니코드가 보인다면 우클릭 > open with > properties editor 로 열어야함

 

 

 

#rejectValue() 경우 다음의 순서로 메시지 코드 생성
# 적용 우선순위
# 1) 에러코드 . 커맨드 객체(VO) 이름 . 필드명 required.loginVO.userId
# 2) 에러코드 . 필드명 required.userId
# 3) 에러코드 . 필드타입 required.java.lang.String
# 4) 에러코드 required

#에러코드=에러문구
required=필수 항목
required.loginVO.userId=사용자 아이디는 필수 항목입니다.
required.password=비밀번호는 필수 항목입니다.

아이디 = 우선순위 가장 높은 1번 형태
비밀번호 = 2번 형태로 작성
기존 required=필수 항목 는 우선순위가 가장 낮은 4번 형태임
그래서 기존 에러코드를 변경하지 않더라도 필드에 맞춰서 에러 문구가 각각 다르게 나타남


 

정상출력

 

 


 

 

 

 

사용자 정의 예외클래스 생성

kr.spring.ch07.service
LoginCheckException (class)

로그인에 성공하면 등장하지 않지만 실패할 경우 나타나는 폼

package kr.spring.ch07.service;

//사용자 정의 예외 클래스
public class LoginCheckException extends Exception{
	
}

 

 

 

 

 

Service파일 생성

kr.spring.ch07.service
LoginService (class)

package kr.spring.ch07.service;

import kr.spring.ch07.vo.LoginVO;

public class LoginService {
	public void checkLogin(LoginVO loginVO)throws LoginCheckException{
		//테스트용으로 userId와 password의 값이 일치하면 로그인으로 처리
		if(!loginVO.getUserId().equals(loginVO.getPassword())) {
			System.out.println("인증 에러 : " + loginVO.getUserId());
			throw new LoginCheckException();
		}
	}
}

예외처리한 게 아니라 if문 수행 후 LoginCheckException 으로 던지기 위해서 예외를 저장함

아직 db연동을 하지 않았기 때문에 id!=password일 때에 인증 에러가 나도록 함

 

정상적으로 로그인처리할 때는 session에 저장하는 작업도 해야하는데

지금은 그냥 임의로 하는거라 체크하는 것만 넣었음

 

 

 

 

빈 설정

servlet-context.xml

<!-- 로그인 처리 -->
<beans:bean class="kr.spring.ch07.controller.LoginController"/>
<beans:bean class="kr.spring.ch07.service.LoginService"/>

 

 

 

 

 

컨트롤러

LoginController

package kr.spring.ch07.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

import kr.spring.ch07.service.LoginCheckException;
import kr.spring.ch07.service.LoginService;
import kr.spring.ch07.validator.LoginVOValidator;
import kr.spring.ch07.vo.LoginVO;

@Controller
public class LoginController {
	
	@Autowired
	private LoginService loginService;
	
	//자바빈 초기화
	@ModelAttribute
	public LoginVO initCommand() {
		return new LoginVO();
	}
	
	@GetMapping("/login/login.do")
	public String form() {
		return "login/form";
	}
	
	@PostMapping("/login/login.do")
	public String submit(LoginVO loginVO, BindingResult result) {
		System.out.println("데이터 전송 후 : " + loginVO);
		
		//데이터 유효성 체크
		new LoginVOValidator().validate(loginVO, result);
		
		//유효성 체크 결과 오류가 있으면 폼 호출
		if(result.hasErrors()) {
			return form();
		}
		
		//로그인체크
		try {
			loginService.checkLogin(loginVO);
			//로그인 성공 시
			return "redirect:/index.jsp";
		}catch(LoginCheckException e) {
			//로그인 실패 시
						//에러코드, 메시지에 전달될 값, default message(안쓸경우에 null처리 가능)
			result.reject("invalidIdOrPassword", new Object[] {loginVO.getUserId()}, null);
			return form();
		}
	}
}

1)

@Autowired를 통해 Service 클래스와 연결 (LoginService)

setter도 넣어야 하는데 @Autowired가 자동으로 생성해주기 때문에 set메서드 생략

 

2)

submit메서드에서 데이터 유효성 체크 후 로그인도 try~catch문으로 체크함

1에서 받은 loginService의 checkLogin(자바빈)을 통해 성공 시 index로 redirect

실패 시 catch블럭으로 들어가서 폼 재호출 + invalidIdOrPassword (아직 명시X) 에러코드 출력함

다른 예외가 아닌 LoginCheckException 여야 catch블럭으로 들어감 

사용자 정의 클래스는 저 catch블럭으로 들어가기 위해서 만든거임!!!

 

3)

result.reject("invalidIdOrPassword", new Object[] {loginVO.getUserId()}, null);
우리가 입력한 아이디를 invalidIdOrPassword (validation.properties)다가 전달

 

4)

지금까지 보여준 예외 메시지는 모두 field와 검증해서 출력했음 (id, password 필드 등의 값이 없을 경우,..)
근데 그것 말고도 아이디랑 비밀번호가 안맞는것도 예외 항목인데 얘네는 특정 한 필드를 지정할 수가

없기 때문에 따로 컨트롤러에 넣어줘야함

본래 필드와 연결했을 시에는 validator안에서 rejectValue로 썼지만

연결할 필드가 없을 시에는 controller안에서 reject를 사용함

rejectValue ==> 꼭 필드가 있어야함
reject ==> 자바빈과는 별개로 체크







프로퍼티즈 파일에 에러코드 명시

validation.properties

invalidIdOrPassword=아이디({0}) 또는 비밀번호가 불일치합니다.

 

컨트롤러에서 에러 발생 시 보낸 vo의 UserId 데이터를 받아서 출력하려고 

result.reject("invalidIdOrPassword", new Object[] {loginVO.getUserId()}, null);

라고 기재했기 때문에 properties에도 아이디 ({0}) 를 명시해서 데이터를 받을 수 있게끔 함

 

 

 

 

 

에러문구를 표시하기 위해 폼에 input 추가

form.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인</title>
</head>
<body>
	<h1>로그인</h1>
	<form:form action="login.do" modelAttribute="loginVO">
		<form:errors/>
		아이디 : <form:input path="userId"/>
		<form:errors path="userId"/>
		<br>
		비밀번호 : <form:password path="password"/>
		<form:errors path="password"/>
		<br>
		<form:button>전송</form:button>
	</form:form>
</body>
</html>

<form:errors/> 추가

 

 

 

 

 

 

index.jsp 실행

아이디=비밀번호 --> index로 돌아가고
아이디!=비밀번호 --> 불일치한다고 출력

 

그런데 에러 메시지가 span 태그로 바뀌어서 폼이 바로 옆으로 붙어버림

그렇기 때문에 span 대신 div 태그로 바꾸고 자동 줄바꿈이 실행된 형태로 변경하면 됨

 

 

앞서 수정했던 form 의 errors 부분을 한번 더 수정

element = "div"

 

 

form.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인</title>
</head>
<body>
	<h1>로그인</h1>
	<form:form action="login.do" modelAttribute="loginVO">
		<%-- 기본적으로 span태그에 에러 메시지를 명시하는데 element 속성에
		 	 div를 명시하면 div태그에 에러 메시지를 표시함 --%>
		<form:errors element="div"/>
		아이디 : <form:input path="userId"/>
		<form:errors path="userId"/>
		<br>
		비밀번호 : <form:password path="password"/>
		<form:errors path="password"/>
		<br>
		<form:button>전송</form:button>
	</form:form>
</body>
</html>

 

 

 

정상출력

 

 

 

 

++

reject도 우선순위가 존재함 (!=rejectValue)

#reject() 경우 다음의 순서로 메시지 코드 생성
# 적용 우선순위
# 1) 에러코드 . 커맨드 객체 이름 invalidIdOrPassword.loginVO
# 2) 에러코드 invalidIdOrPassword

 

 


 

 

 

 

<파일 업로드 기능>

 tomcat의 라이브러리를 사용해서 연동할거기 때문에 방식이 조금 다름

upload폴더와 fileupload.jar파일이 이미 들어가있기 때문에 따로 설정할 것X

 

 

 

 

기본설정하기 (upload경로를 절대경로 지정 방식으로 지정)

servlet-context.xml

<!-- 파일 업로드를 위한 multipartResolver 설정 -->
<beans:bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <beans:property name="maxUploadSize" value="52428800"/> <!-- 52428800 = 50메가를 뜻함(바이트 단위) -->
    <beans:property name="defaultEncoding" value="UTF-8"/>
</beans:bean>

 

 

 

 

 

 

파일 경로를 지정하고 저장하는 설정파일 생성

config(package)

file.properties(file)

파일 경로를 key와 value의 쌍으로 명시, 경로를 알아야 저장할 수 있으므로 꼭 만들어야함

 

<upload 폴더의 절대경로 찾기>

1) upload 폴더 우클릭

2) properties 클릭

3) location의 절대경로 복사

 

\가 특문이기 때문에 에러를 방지하기 위해 \를 하나 더 넣거나 /로 변경해야함

<일괄적으로 변경하는 방법>

1) \ 하나 드래그

2) ctrl + f

3) replace with = /

4) scope = all 말고 select lines (한줄변경)

5) replace all

 

file_path=C:/Users/user/git/ch10-Spring_MVC/ch10-Spring_MVC/src/main/webapp/upload

 

 구한 절대경로를 file_path 라는 key에 저장

 

 

 

 

 

파일 경로 적기

servlet-context.xml

<!-- 파일 경로 파일 설정 -->
<context:property-placeholder location="classpath:config/file.properties"/>

classpath=자바파일이 있는 공간을 알려줌 (위치를 알려주는 역할)

 

 

 

 

 

 

컨트롤러 생성

kr.spring.ch08.controller
SubmitReportController

package kr.spring.ch08.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;

@Controller
public class SubmitReportController {
	//파일 업로드 경로 읽기
	@Value("${file_path}")
	private String path;
}

${file_path} >> properties에서 명시했던 key


@Value("${file_path}") 키를 읽어와서 private String path; 여기 path에 넣음
그래서 path에 파일 경로가 들어있음

 

 

 

 

 

 

폼 호출 후 유효성 체크를 하기 위해 자바빈 생성

kr.spring.ch08.vo
SubmitReportVO

package kr.spring.ch08.vo;

import org.springframework.web.multipart.MultipartFile;

public class SubmitReportVO {
	private String subject; //제목
	private MultipartFile reportFile; //파일 (multipartFile 타입으로 처리해야함)
	
	public String getSubject() {
		return subject;
	}
	public void setSubject(String subject) {
		this.subject = subject;
	}
	public MultipartFile getReportFile() {
		return reportFile;
	}
	public void setReportFile(MultipartFile reportFile) {
		this.reportFile = reportFile;
	}
	
	@Override
	public String toString() {
		return "SubmitReportVO [subject=" + subject + ", reportFile=" + reportFile + "]";
	}
}

file은 MultipartFile로 처리해야함

 

 

 

 

 

 

 

컨트롤러 수정 (자바빈 초기화+폼 호출)

SubmitReportController

package kr.spring.ch08.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;

import kr.spring.ch08.vo.SubmitReportVO;

@Controller
public class SubmitReportController {
	//파일 업로드 경로 읽기
	@Value("${file_path}")
	private String path;
	
	//자바빈(VO) 초기화
	@ModelAttribute("report") //report=속성명
	public SubmitReportVO initCommand() {
		return new SubmitReportVO();
	}
	
	@GetMapping("/report/submitReport.do")
	public String form() {
		return "report/submitReportForm";
	}
}

 

 

 

 

 

폼 호출 전 빈 설정

servlet-context.xml

<!-- 파일 업로드 처리 -->
<beans:bean class="kr.spring.ch08.controller.SubmitReportController"/>

 

 

 

 

 

 

뷰 파일 만들기

views

report (folder)

submitReportForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>리포트 등록 폼</title>
</head>
<body>
<form:form action="submitReport.do" enctype="multipart/form-data" modelAttribute="report">
	<ul>
		<li>	
			<label for="subject">제목</label>
			<form:input path="subject"/>
			<form:errors path="subject"/>
		</li>
		<li>
			<label for="reportFile">리포트 파일</label>
			<input type="file" id="reportFile" name="reportFile">
			<form:errors path="reportFile"/>
		</li>
		<li>
			<form:button>리포트 전송</form:button>
		</li>
	</ul>
</form:form>
</body>
</html>

1) enctype="multipart/form-data" 기재 필수

2) controller에서 @ModelAttribute("report") 라고 속성명을 명시했기 때문에 modelAttribute="report" 라고 따라서 명시

3) file input 태그는 커스텀 태그가 없기 때문에 기존과 동일하게 input type="file" 태그 사용

 

 

 

 

 

실행파일

index.jsp

<a href="${pageContext.request.contextPath}/report/submitReport.do">SubmitReportController</a><br>

 

 

실행 후 폼 정상출력

 

 

 


 

 

 

 

<유효성체크>

Validator 만들기

kr.spring.ch08.validator
SubmitReportValidator

package kr.spring.ch08.validator;

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

import kr.spring.ch08.vo.SubmitReportVO;

public class SubmitReportValidator implements Validator{

	@Override
	public boolean supports(Class<?> clazz) {
		return SubmitReportVO.class.isAssignableFrom(clazz); //사용하는 객체가 자바빈 구조인지 확인
	}

	@Override
	public void validate(Object target, Errors errors) {
		SubmitReportVO report = (SubmitReportVO)target;
		if(report.getSubject()==null || report.getSubject().isEmpty()) {
			errors.rejectValue("subject", "required");
		}
		if(report.getReportFile()==null || report.getReportFile().isEmpty()) {
			errors.rejectValue("reportFile", "required");
		}
	}
}

 

 

 

 

 

 

 

컨트롤러

SubmitReportController

package kr.spring.ch08.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

import kr.spring.ch08.validator.SubmitReportValidator;
import kr.spring.ch08.vo.SubmitReportVO;

@Controller
public class SubmitReportController {
	//파일 업로드 경로 읽기
	@Value("${file_path}")
	private String path;
	
	//자바빈(VO) 초기화
	@ModelAttribute("report") //report=속성명
	public SubmitReportVO initCommand() {
		return new SubmitReportVO();
	}
	
	@GetMapping("/report/submitReport.do")
	public String form() {
		return "report/submitReportForm";
	}
	
	@PostMapping("/report/submitReport.do")
	public String submit(@ModelAttribute("report")SubmitReportVO vo, BindingResult result) {
		
		System.out.println("데이터 전송 후 : " + vo);
		
		//전송된 데이터 유효성 체크
		new SubmitReportValidator().validate(vo, result);
		//유효성 체크 결과 오류가 있으면 폼 호출
		if(result.hasErrors()) {
			return form();
		}
		return "report/submittedReport";
	}
}

1) submit 메서드 생성
1) 인자에 자바빈 넣음
2) 유효성 체크

==> 파일 업로드는 아직 안됐지만 유효성 체크까지 됐는지 안됐는지는 확인 가능

 

 

 

 

 

 

유효성 체크 후 등록 완료 폼 만들기

report (folder)

submittedReport

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>리포트 등록 완료</title>
</head>
<body>
리포트 등록이 완료되었습니다.
</body>
</html>

 

 

 

index.jsp 실행 후 공란으로 전송 누를 시

[console]
데이터 전송 후 : SubmitReportVO [subject=, reportFile=org.springframework.web.multipart.commons.CommonsMultipartFile@5babf8c1]
==> 객체 주소가 보이기 때문에 객체는 만들어진걸 알 수 있음

 

 

 


 

 

파일 업로드 처리 시 SubmitReportVO 의 private MultipartFile reportFile 에 담김 (자바빈에 담겨있음)
그래서 이걸 원하는 경로에 복사해주면 됨

 

 

 

 

컨트롤러

SubmitReportController

package kr.spring.ch08.controller;

import java.io.File;
import java.io.IOException;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

import kr.spring.ch08.validator.SubmitReportValidator;
import kr.spring.ch08.vo.SubmitReportVO;

@Controller
public class SubmitReportController {
	//파일 업로드 경로 읽기
	@Value("${file_path}")
	private String path;
	
	//자바빈(VO) 초기화
	@ModelAttribute("report") //report=속성명
	public SubmitReportVO initCommand() {
		return new SubmitReportVO();
	}
	
	@GetMapping("/report/submitReport.do")
	public String form() {
		return "report/submitReportForm";
	}
	
	@PostMapping("/report/submitReport.do")
	public String submit(@ModelAttribute("report")SubmitReportVO vo, BindingResult result) throws IllegalStateException, IOException {
		
		System.out.println("데이터 전송 후 : " + vo);
		
		//전송된 데이터 유효성 체크
		new SubmitReportValidator().validate(vo, result);
		//유효성 체크 결과 오류가 있으면 폼 호출
		if(result.hasErrors()) {
			return form();
		}
		
		//파일 업로드 처리
		File file = new File(path + "/" + vo.getReportFile().getOriginalFilename()); //절대경로+/+파일명
		//지정한 경로에 파일을 저장
		vo.getReportFile().transferTo(file);
		
		System.out.println("주제 : " + vo.getSubject());
		System.out.println("업로드한 파일 : " + vo.getReportFile().getOriginalFilename());
		System.out.println("파일 크기 : " + vo.getReportFile().getSize());
		
		return "report/submittedReport";
	}
}

유효성 체크가 끝난 후 파일 업로드 처리 (파일명 명시, 경로 명시)
경로 정보 만들 시 파일 객체를 이용해서 만드는거임

파일은 import java.io.File; java.io를 import하면 됨

 

 

==============================
1.
File file = new File(path + "/" + vo.getReportFile().getOriginalFilename());
파일을 읽어와서 저기 file에 복사함

2.
vo.getReportFile().transferTo(file);
지정한 경로에 1번에서 복사한 파일을 저장
근데 쟤만 명시하면 에러가 남 왜냐하면 에러가 날 수 있기 때문에 의무적으로 try~catch나 throws를 해야함


try~catch 시 에러가 나도 화면엔 정상적으로 출력할 수 있기 때문에 throws를 하는게 좋음

저 구문을 클릭하면 add throws declaration을 누르면 위에 throws IllegalStateException가 추가됨
>>>> public String submit(@ModelAttribute("report")SubmitReportVO vo, BindingResult result) throws IllegalStateException, IOException {


여기까지 명시하면
console에 출력되는 것

 

 

 

 

 

컨트롤러에서 접근할 때에는 자바빈 (vo)

jsp에서 접근할 때에는 request (report) 를 통해서 읽어오면 됨

 

 

 

 

 

 

jsp에 파일 정보 출력하기

submittedReport.jsp 수정

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>리포트 등록 완료</title>
</head>
<body>
리포트 <b>${report.subject}</b>를 등록했습니다.<br>
업로드한 파일 : ${report.reportFile.originalFilename}<br>
용량 : ${report.reportFile.size}
</body>
</html>

파일 불러올 때
report = 자바빈 접근
reportFile = MultipartFile
originalFilename 순으로 호출해야함

사이즈도 reportFile -> size

 

 

 

 

index.jsp에서 실행 ->
제목이랑 파일 등록하고 버튼 누르면

리포트 제목입니다를 등록했습니다.
업로드한 파일 : 블랙맘바.jpg
용량 : 59138

라고 화면에 표시됨
(사진말고 이름만)

그러고 upload 경로 (folder)에 가보면 (refresh 필수) 그 사진이 들어가있음

 

==============================

이미지 파일 저장할 때 상황에 따라서 저장할 수 있음  (방법2)
1. db에 저장하고 싶다 ==> 오라클-blob 타입으로 저장할 수 있음 (부트로 배울 때 해볼거임)
2. 특정 경로에 저장하겠다 ==> 오늘 배운 방법

 

 

 

 

 


 

 

 

 


지금까지 한 유효성 체크
1. script로 체크
2. validator 만들어서 implements
1) 회원가입
2) 로그인
3) 파일 업로드

근데 이번에는 3. 어노테이션을 이용해서 유효성 체크를 해볼거임 

 

 

 

<어노테이션을 이용한 유효성 체크>

 

컨트롤러 생성

kr.spring.ch09.controller
MemberWriteController

package kr.spring.ch09.controller;

import org.springframework.stereotype.Controller;

@Controller
public class MemberWriteController {

}

 

 

 

 

 

 

커스텀 태그 사용 전 자바빈 생성하기

kr.spring.ch09.vo
MemberVO

package kr.spring.ch09.vo;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.Range;

public class MemberVO {
	//정규표현식으로 패턴 검사
	@Pattern(regexp="^[0-9a-zA-Z]+$") //^=시작 +=한번이상반복 $=끝
	private String id;
	//문자열의 길이 지정
	@Size(min=4,max=10)
	private String password;
	@NotEmpty
	private String name;
	//숫자 데이터의 길이 지정
	@Range(min=1,max=200)
	private int age;
	@Email
	@NotEmpty
	private String email;
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	
	@Override
	public String toString() {
		return "MemberVO [id=" + id + ", password=" + password + ", name=" + name + ", age=" + age + ", email=" + email
				+ "]";
	}
	
}

패턴 검사 = @Pattern

문자열 길이 지정 = @Size(min, max)

숫자 길이 지정 = @Range(min, max)

공백 비허용 = @NotEmpty

이메일 형식 = @Email

 

@Pattern(regexp="^[0-9a-zA-Z]+$") 0-9,a-z,A-Z 숫자 영문자(대소문자) 1자 이상 입력

 

 

 

 

 

컨트롤러 수정 (자바빈 초기화 + 폼 호출)

MemberWriteController

package kr.spring.ch09.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;

import kr.spring.ch09.vo.MemberVO;

@Controller
public class MemberWriteController {
	//자바빈(VO) 초기화
	@ModelAttribute("vo")
	public MemberVO initCommand() {
		return new MemberVO();
	}
	
	//폼 호출
	@GetMapping("/member/write.do")
	public String form() {
		return "member/write";
	}
}

 

 

 

 

 

 

회원가입 폼 생성

views
member (folder)
write.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원가입</title>
</head>
<body>
<form:form action="write.do" modelAttribute="vo">
	아이디 : <form:input path="id"/>
	<form:errors path="id"/>
	<br>
	비밀번호 : <form:password path="password"/>
	<form:errors path="password"/>
	<br>
	이름 : <form:input path="name"/>
	<form:errors path="name"/>
	<br>
	나이 : <form:input path="age"/>
	<form:errors path="age"/>
	<br>
	이메일 : <form:input path="email"/>
	<form:errors path="email"/>
	<br>
	<form:button>전송</form:button>
</form:form>
</body>
</html>

 

 

 

 

 

 

빈 설정

servlet-context.xml

<!-- 어노테이션을 이용한 유효성 체크 -->
<beans:bean class="kr.spring.ch09.controller.MemberWriteController"/>

 

 

 

 

 

 

실행파일

index.jsp

<a href="${pageContext.request.contextPath}/member/write.do">MemberWriteController</a><br>

넣고 실행하면
나이는 int기 때문에 기본값인 0이 표시된 입력폼이 나옴

만약 form의 나이 type을 number로 바꾸고 싶다면 write.jsp에서
나이 : <form:input path="age"  type="number"/>
라고 type="number"를 추가해주면 됨 --> 그럼 type="text"가 아니라 number로 바뀐걸 볼 수 잇음

------------------------
이름 : <input id="name" name="name" type="text" value=""/>
<br>
나이 : <input id="age" name="age" type="number" value="0"/>

------------------------

 

 

 

 

 

프로퍼티즈 파일

messages

validation.properties

자바빈에서 원하는 어노테이션대로 명시하지 않았을 때 에러코드를 표시하려고 함

근데 그것도 형식이 있음

#어노테이션을 이용한 유효성 체크시 에러코드 지정 명시 방법
#어노테이션명.커맨드객체.필드 or 어노테이션명.필드
Pattern.id=영문자와 숫자만 가능
Size.password=비밀번호는 4자이상 10자이하로 입력
NotEmpty.name=이름은 필수 항목
Range.age=나이는 1살이상 200살이하로 명시
Email.email=이메일 형식에 맞게 입력
NotEmpty.email=이메일은 필수 항목

 

 

 

 

 

어노테이션을 이용해서 유효성 검사

MemberWriteController

package kr.spring.ch09.controller;

import javax.validation.Valid;

import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

import kr.spring.ch09.vo.MemberVO;

@Controller
public class MemberWriteController {
	//자바빈(VO) 초기화
	@ModelAttribute("vo")
	public MemberVO initCommand() {
		return new MemberVO();
	}
	
	//폼 호출
	@GetMapping("/member/write.do")
	public String form() {
		return "member/write";
	}
	
	//데이터
	@PostMapping("/member/write.do")
	public String submit(@ModelAttribute("vo") @Valid MemberVO memberVO, BindingResult result) {
		
		System.out.println("데이터 전송 후 " + memberVO);
		
		//@Valid를 이용해서 이미 유효성 체크가 됨
		//유효성 체크 결과 오류가 있으면 폼 호출
		if(result.hasErrors()) {
			return form();
		}
		return "redirect:/index.jsp";
	}
}

public String submit(@ModelAttribute("vo") @Valid MemberVO memberVO, BindingResult result)

 

어노테이션으로 유효성을 체크할 때에 

오류가 났으면  result 로 들어가게 하기 위해선 컨트롤러 메서드에 어노테이션을 한 번 더 써줘야함 (@Valid)

 

@Valid = 어노테이션으로 유효성 검사 할 때 에러가 난 (자바빈에 없는) 객체를 result에 넣어주는 역할을 함

오류체크+체크 결과를 BindingResult에 저장까지 해주기 때문에 객체를 생성해서 유효성 검사를 했을 때 명시했던
new SubmitReportValidator().validate(vo, result); << 이 부분을 적지 않아도 됨

 

 

 

 

 

 

index.jsp 실행하고 공백인 상태에서 전송 누르면

이미 처리를 다 해서 나이 default 0이 사라진 화면 (감안)

 

 

다 맞게 입력하면 index.jsp로 리다이렉트 됨

 

 

 

 

***

만약 write.jsp에서  type="number"를 지우고 text인 칸에 숫자 대신 문자를 넣는다면??

 

 

write.jsp에서 type="number" 를 기재했기 때문에 이 상태에서는 문자가 아예 쳐지질 않음

하지만 저 type을 삭제하고 text로 input을 바꾼 후 문자를 넣는다면 나이 옆 칸에
Failed to convert property value of type java.lang.String to required type int for property age; nested exception is java.lang.NumberFormatException: For input string: "sdfsfd"
라고 NumberFormatException 에러가 화면 출력됨

 

 

그래서 text를 넣을 때에 일어나는 에러를 알아보기 위해 에러코드를 하나 더 넣을 것임 (타입이 다를 때 사용)

 

 

 

 

프로퍼티즈 파일

validation.properties

typeMismatch.age=나이는 1살이상 200살이하로 명시

 

 

 

명시 후 index.jsp 에서 실행하면 (나이 란에 text 기입)


라고 뜸
==> 숫자 체크하는 경우는 typeMismatch.객체명 을 넣는 게 좋음 (혹시 모르니까)

 

 

 


 

 

 

기본적으로 나이에 0이 보이게 되어있는데 (int 타입이라서) 이를 안나오게 하려면 

자바빈에서 int -> Integer 로 바꾸면 0이 보이지 않음

 

그런데 Integer로 바꾼 뒤 나이를 아무것도 입력하지 않았을 때에 오류메시지가 뜨지 않음

그래서 자바빈에서 @NotEmpty 어노테이션을 걸고

프로퍼티즈에 NotEmpty.age=나이는 1살이상 200살이하로 명시 하면 에러가 남 라고 명시하면

창 자체가 에러가 나서 안보임! (타입이 Integer 기 때문)

 

 

그래서 이런 경우는 그냥 js로 처리하는 게 좋음

 

 

자바빈의 @NotEmpty 지우고,  int로 변경, 프로퍼티즈 NotEmpty.age삭제 후

js로 처리

 

 

write.jsp

<title>회원가입</title>
<script type="text/javascript">
window.onload=function(){
	let age = document.getElementById('age');
	if(age.value==0){
		age.value=''; //age를 0이 아닌 빈칸으로 보이게 하려고
	}
};
</script>

 

그럼 처음에 창을 열었을 때에도 0이 보이지 않고,

비어있는 경우에 전송을 눌렀을 때에도 정상적으로 에러메시지가 뜸

 

 

 


 

 

 

 

<파일 다운로드>

단순히 경로만 링크했을 경우에는 브라우저가 기본적으로 뷰어 역할을 하기 때문에 파일이 보여지기만 함

그래서 별개로 다운로드 기능을 따로 구현해야함 (Stream을 만들어서 구현해야 한다는 뜻)

 

 

WEB-INF 클릭 > new > file
file.txt 이름으로 파일 생성

 

 

다운로드 받을 txt파일 생성

WEB-INF
file.txt

하하 호호 Welcome to Seoul

그냥 아무 말이나 적음

 

 

 

 

 

file.txt를 다운로드 할 수 있게끔 stream처리를 할건데 먼저 컨트롤러를 만들고

downlaodview 라고 해서 class형태로 뷰를 만들거임 (여기서 다운로드 할 예정)

 

컨트롤러 생성

kr.spring.ch10.controller
DownloadController

package kr.spring.ch10.controller;

import java.io.File;

import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class DownloadController {
	@RequestMapping("/file.do")
	public ModelAndView download(HttpSession session) {
		String path = session.getServletContext().getRealPath("/WEB-INF/file.txt");
		//파일객체 생성 후 정보 넘기기
		File downloadFile = new File(path);
		
		//위에서 받은 정보를 ModelAndView를 통해 전달하기
					//뷰이름	뷰에서 호출할 속성명		속성값
		return new ModelAndView("download", "downloadFile", downloadFile);
	}
}

1.

public ModelAndView download(HttpSession session)

session을 통해 ServletContext를 구할 수 있음 (파일 경로)

 

2.

String path = session.getServletContext().getRealPath("/WEB-INF/file.txt");
("/WEB-INF/file.txt")처럼 상대경로를 넣으면 절대경로를 구해줌

 

3.

File downloadFile = new File(path);
파일객체 생성 후 정보 넘기기

 

4.

return new ModelAndView("download", "downloadFile", downloadFile);
위에서 받은 정보를 ModelAndView를 통해 전달하기

 

 

 

 

 

 

 

지금까진 jsp만 뷰 역할을 했지만 spring에서는 class도 뷰 역할을 할 수 있음

다운로드는 뷰를 동작시킬 때 자바코드가 들어가야하는데 jsp에 넣기엔 조금 부담스러워서 많이들 하는 방법

그래서 4에서 명시한 download(view)를 생성할 때 jsp가 아닌 class를 만들어서 한다고 함

 

 

 

 

view역할을 하는 class만들기

kr.spring.ch10.view
DownloadView (class)

package kr.spring.ch10.view;

import java.io.File;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.view.AbstractView;

public class DownloadView extends AbstractView{

	public DownloadView() {
		setContentType("application/download;charset=utf-8");
	}
	
	@Override
	protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		
		File file = (File)model.get("downloadFile");
		//컨텐트 타입 지정
		response.setContentType(getContentType());
		//전송하는 데이터의 용량
		response.setContentLength((int)file.length());
		
		//전송할 파일명 구하기
		String fileName = new String(file.getName().getBytes("utf-8"),"iso-8859-1");
		
		response.setHeader("Content-Disposition", "attachment; filename=\"file.txt\";");
	}
}

1.

다운로드 뷰를 만들기 위해선 상속을 받아야 뷰 역할을 할 수 있기 때문에 extends AbstractView 추가

 

2.

protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response)

데이터를 전달하게 되면(DownloadController에서 명시한 뷰에서 호출한 속성명과 속성값) map구조(model)을 통해

전달받을 수 있음

 

3.

File file = (File)model.get("downloadFile");

파일객체 생성 후 받기

 

4.

public DownloadView() {
     setContentType("application/download;charset=utf-8");
}

헤더에 파일 정보 전송하고 다운로드 하라고 알려주기

헤더에 전송하기 위해선 response를 통해서 세팅해야하는데 response에서 contexttype을 받기 위해 미리 위에 세팅
이미지건, 일반 텍스트건 무조건 컨텐트 타입 지정하고 다운로드할 수 있게 함

 

5.

response.setContentLength((int)file.length());

파일의 용량 알아내기

 

6.
String fileName = new String(file.getName().getBytes("utf-8"),"iso-8859-1");
파일명 구하기 (파일명을 알아야 지정한 파일 다운로드 가능)

근데 그럴 때 인코딩 문제가 좀 생기기 때문에 이중으로 인코딩 처리 해줘야함
getBytes("utf-8"), "iso-8859-1" >> 이렇게 두개

7.
response.setHeader("Content-Disposition", "attachment; filename=\"file.txt\";");
준비가 다 됐기 때문에 헤더에 명시
http응답 메시지 헤더에 넣어서 보내고 클라이언트가 헤더에서 정보 뽑아내면 다운로드 해야되는구나 싶어서 다운로드를 한다고 하네용

"Content-Disposition"

정해진 형식임 파일명을 지정하기 위해 넣었음
"attachment; filename=\"file.txt\";"
파일 이름은 ""로 묶어야 하는데 밖에도 "가 있기 때문에 특문이라고 인식돼서 오류남
그래서 \를 이용해서 일반문자로 바꿈 file이름을 변수로 바꿔야하는데 일단 수업끝

728x90
반응형