반응형
01.[exception] 패키지
- 개발자가 만든 예외처리클래스
- 실제 만들 때는 더 많은 예외들이 필요함
- 데이타 처리에 필요한 다양한 문제점들 모두 예외클래스로 처리할 수 있도록 만들기
1) ExistUserinfoException.java
package xyz.itwill10.exception;
import lombok.Getter;
import lombok.Setter;
import xyz.itwill10.dto.Userinfo;
//회원정보에 대한 등록 명령이 실행될 때 사용자로부터 입력받아 전달된 회원정보의 아이디가 이미 회원정보의
//아이디로 존재하는 경우 발생되어 처리하기 위한 예외 클래스
@Getter
@Setter
public class ExistUserinfoException extends Exception {
//회사에서는 잘 안만듦, 경고 그냥 무시함
private static final long serialVersionUID = 1L;
//예외 처리에 필요한 값을 저장하기 위한 필드
// => 사용자로부터 입력되어 전달된 회원정보를 저장하기 위한 필드
private Userinfo userinfo;
public ExistUserinfoException() {
// TODO Auto-generated constructor stub
}
public ExistUserinfoException(String message, Userinfo userinfo) {
super(message);
this.userinfo=userinfo;
}
}
2) UserinfoNotFoundException.java
package xyz.itwill10.exception;
//회원정보에 대한 등록 변경, 삭제, 검색 명령이 실행될 때 사용자로부터 전달받은 아이디의 회원정보가
//없는 경우 발생되어 처리하기 위한 예외 클래스
public class UserinfoNotFoundException extends Exception {
private static final long serialVersionUID = 1L;
//우리가 만들 프로그램에서는 필드는 따로 필요하자 않아 안만듦
public UserinfoNotFoundException() {
// TODO Auto-generated constructor stub
}
public UserinfoNotFoundException(String message) {
super(message);
}
}
3) LoginAuthFailException.java
package xyz.itwill10.exception;
import lombok.Getter;
import lombok.Setter;
//로그인에 대한 인증 명령이 실행될 때 사용자로부터 전달받은 아이디와 비밀번호에 대한 인증
//실패가 된 경우 발생되어 처리하기 위한 예외 클래스
@Getter
@Setter
public class LoginAuthFailException extends Exception {
private static final long serialVersionUID = 1L;
//예외 처리에 필요한 값을 저장하기 위한 필드
// => 사용자로부터 입력되어 전달된 아이디를 저장하기 위한 필드
private String userid;
public LoginAuthFailException() {
// TODO Auto-generated constructor stub
}
public LoginAuthFailException(String message, String userid) {
super(message);
this.userid=userid;
}
}
4) @ControllerAdvice | 모든 Controller의 요청처리메소드에서 발생
[🖤Controller] ExceptionController.java
- 모든 Controller의 요청처리메소드에서 발생
- @ControllerAdvice를 작성하면 "모든" Controller 클래스의 요청처리메소드에서 발생한다는 것이 핵심
package xyz.itwill10.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
//@ControllerAdvice : 예외 처리 메소드만 작성된 Controller 클래스를 Spring Bean으로 등록하기 위한 어노테이션
// => "모든" Controller 클래스의 요청 처리 메소드에서 발생되어 전달된 예외를 제공받아 처리
// => SpringAOP 기능이 이용됨
// => 프로그램 다 개발 후에 맨 마지막에 작성될 메소드!
// => 왜? 예외가 떨어져야하는데 이 클래스를 만들면 예외페이지로 이동하기 때문에 예외의 이유가 떨어지지 않음ㅜ
@ControllerAdvice
public class ExceptionController {
private static final Logger logger=LoggerFactory.getLogger(ExceptionController.class);
@ExceptionHandler(value = Exception.class) //Exception 클래스에서 처리할게! (모든 예외를 다 잡을 예정)
public String userinfoExceptionHandler(Exception exception) {
// 예외를 제공받아 처리하는 메소드를 미리 만들고 싶다면 printStackTrace() & getMessage()를 이용하자
exception.printStackTrace(); //예외발생위치와 호출된 메소드의 호출 스택 정보 출력
logger.error(exception.getMessage()); //에러로그파일에 계속 기록함
return "userinfo/user_error";
}
}
- 에러로그 파일은 log4j.xml의 환경설정 파일에 의해 error 레벨 이상의 기록들이 아래처럼 기록될 것임
03. [util] 패키지
Interceptor클래스 (특정 Controller의 요청처리메소드에서발생)
- 일반적으로 권한에 관련된 것들을 처리하고, 그 외에 것들은 Filter 클래스에서 함
- 서블릿에 의해 관리되므로 root-context.xml(모든서블릿)이나 해당 서블릿의 Spring Bean Configuration File에 작성, 그 중 appServlet서블릿의 요청처리메소드 호출 전후에만 발생할 것이므로 servlet-context.xml에 작성
- 팁) 장바구니 페이지 : 비로그인 사용자가 장바구니 클릭 시 로그인 페이지로 갔다가 다시 돌아오는 인터셉터 만들어보기!!
- 권한 관련은 반드시 테스트 해보기!! 필수!!!!
- (appServlet서블릿)의 요청처리메소드 호출 전후에 발생할 인터셉터!!
VS Filter 클래스 : WAS에 의해 관리되므로 web.xml에 작성
1) AdminAuthInterceptor.java (인터셉터)
package xyz.itwill10.util;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import xyz.itwill10.dto.Userinfo;
//Interceptor 클래스
//=> Front Controller에 의해 요청 처리 메소드가 호출되어 실행되기 전 또는 후에
//삽입되어 실행될 기능을 제공하는 클래스
//=> 🌈Interceptor 클래스는 반드시 HandlerInterceptor 인터페이스를 상속받아 작성
//=> 필요한 메소드만 오버라이드 선언하여 작성 (인터페이스 내 default 메소드이기 때문 - JDK8 이상)
//=> Spring Bean Configuration File(servlet-context.xml)에 Spring Bean으로 등록하고 요청 처리
//메소드 호출 전후에 인터셉터가 동작될 수 있도록 환경 설정
//DAO, Service, Controller 도 아니기 때문에 @Component 어노테이션 사용 가능하지만, 그러려면 <component-scan> 을 패키지로 등록해야하기 때문에
//그냥 스프링 빈 태그로 빈 등록할 것임
//@Component
//관리자 관련 권한 처리를 위해 작성된 Interceptor 클래스
//=> 요청 처리 메소드의 명령 실행 전에 비로그인 사용자이거나 관리자가 아닌 사용자가 페이지를
//요청한 경우 에러 메세지를 출력하는 페이지의 URL 주소를 전달하는 기능 제공
public class AdminAuthInterceptor implements HandlerInterceptor{
- preHandle
//preHandle : 요청 처리 메소드의 명령이 실행되기 전에 실행될 명령을 작성하는 메소드 - 인터셉터가 요청 처리 메소드의 흐름을 훔쳐와서 아래의 메소드 부터 실행하게 해줄 것임
// => false 반환 : 요청 처리 메소드의 명령 미실행
// => true 반환 : 요청 처리 메소드의 명령 실행
// => 권한 관련 명령을 작성하기 위한 메소드 - 실제로 권한관련 명령은 이 메소드 하나만 만들어서 사용하는 경우가 많음
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
HttpSession session=request.getSession();
Userinfo loginUserinfo=(Userinfo)session.getAttribute("loginUserinfo");
//비로그인 사용자이거나 관리자가 아닌 사용자인 경우
if(loginUserinfo==null || loginUserinfo.getStatus()!=9) {
//방법1. 직접 이동
//request.getRequestDispatcher("userinfo/user_error.jsp").forward(request, response); //포워드 이동하고,
//return false; //권한이 없는 경우 요청처리메소드 미호출
//방법2. 예외처리 메소드로 예외전달
throw new Exception("비정상적인 요청입니다."); //인위적 예외 발생 - 예외 처리 메소드로 이동
}
return true; //권한이 있는 경우 요청처리메소드 호출
}
- postHandle
//postHandle : 요청 처리 메소드의 명령이 실행되고 뷰가 생성되기 전에 실행될 명령을 작성하는 메소드
// => ModelAndView 객체를 제공받아 ViewName 또는 Model 객체의 속성값 변경할 경우 사용
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
- afterCompletion
//afterCompletion : 요청 처리 메소드의 명령이 실행되고 뷰에서 의해 최종 결과물이 생성된 후
//실행될 명령을 작성하는 메소드
// => 응답 결과를 변경할 경우 사용
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
2) LoginAuthInterceptor.java (인터셉터)
package xyz.itwill10.util;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.servlet.HandlerInterceptor;
import xyz.itwill10.dto.Userinfo;
//로그인 사용자 관련 권한 처리를 위해 작성된 Interceptor 클래스
//=> 요청 처리 메소드의 명령 실행 전에 비로그인 사용자가 페이지를 요청한 경우 에러 메세지를
//출력하는 페이지의 URL 주소를 전달하는 기능 제공
public class LoginAuthInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
HttpSession session=request.getSession();
Userinfo loginUserinfo=(Userinfo)session.getAttribute("loginUserinfo");
//비로그인 사용자이거나 관리자가 아닌 사용자인 경우
if(loginUserinfo==null) {
//response.sendRedirect("login.jsp");
throw new Exception("비정상적인 요청입니다.");
}
return HandlerInterceptor.super.preHandle(request, response, handler);
}
}
3) Pager.java (페이징처리 클래스)
package xyz.itwill10.util;
import lombok.Data;
//페이징 처리 관련 값을 필드에 저장하기 위한 클래스
@Data
public class Pager {
//생성자를 이용하여 초기값을 전달받아 필드에 저장
private int pageNum; //요청 페이지의 번호 - "전달값" 이용
private int totalBoard; //전체 게시글의 갯수
private int pageSize; //하나의 페이지에 출력될 개시글의 갯수
private int blockSize; //하나의 블럭에 출력될 페이지 번호의 갯수
//생성자로 초기화된 필드값을 계산하여 결과값을 필드에 저장
private int totalPage; //전체 페이지의 갯수
private int startRow; //요청 페이지에 출력될 게시글의 시작 행번호
private int endRow; //요청 페이지에 출력될 게시글의 종료 행번호
private int startPage; //현재 블럭에 출력될 시작 페이지 번호
private int endPage; //현재 블럭에 출력될 종료 페이지 번호
private int prevPage; //이전 블럭에 출력될 시작 페이지 번호
private int nextPage; //다음 블럭에 출력될 시작 페이지 번호
public Pager(int pageNum, int totalBoard, int pageSize, int blockSize) {
//매개변수를 통해 초기화
// => pageNum , totalBoard , pageSize , blockSize
super();
this.pageNum = pageNum;
this.totalBoard = totalBoard;
this.pageSize = pageSize;
this.blockSize = blockSize;
//초기화된 필드를 이용해 계산결과를 저장
// => totalPage,startRow,endRow,startPage,endPage,previousPage,nextPage
calcPage();
}
//계산된 결과값을 필드에 저장하는 메소드 - 생성자에서 호출하여 사용
private void calcPage() {
totalPage=(int)Math.ceil((double)totalBoard/pageSize);
if(pageNum<=0 || pageNum>totalPage) {
pageNum=1;
}
startRow=(pageNum-1)*pageSize+1;
endRow=pageNum*pageSize;
if(endRow>totalBoard) {
endRow=totalBoard;
}
startPage=(pageNum-1)/blockSize*blockSize+1;
endPage=startPage+blockSize-1;
if(endPage>totalPage) {
endPage=totalPage;
}
prevPage=startPage-blockSize;
nextPage=startPage+blockSize;
}
}
4) FileDownload.java (파일 다운로드 기능을 제공하기 위한 클래스)
- BeanNameViewResolver에 의해 실행
package xyz.itwill10.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.servlet.view.AbstractView;
//파일 다운로드 기능을 제공하기 위한 클래스 - BeanNameViewResolver 객체에 의해 실행되는 클래스
// => Spring Bean Configuration File(servlet-context.xml)에 Spring Bean으로 등록
//BeanNameViewResolver 객체에 의해 실행되는 클래스는 반드시 AbstractView 클래스를 상속받아 작성
// => renderMergedOutputModel() 메소드를 오버라이드 선언하여 응답에 필요한 명령 작성
public class FileDownload extends AbstractView {
//클라이언트에게 응답될 파일형식(MimeType)에 대한 변경 - 파일 다운로드 기능
public FileDownload() {
//AbstractView.setContentType(String mimeType) : 응답 파일 형식을 변경하는 메소드
setContentType("application/download; utf-8");
}
//BeanNameViewResolver 객체에 의해 자동 호출되는 메소드
// => Map 자료형의 매개변수에는 요청 처리 메소드에서 제공된 속성값이 엔트리로 저장된 Map 객체 제공
@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
//요청 처리 메소드에서 제공된 속성값(파일 관련 정보)을 객체로 반환받아 저장
String uploadDir=(String)model.get("uploadDir");
String uploadFilename=(String)model.get("uploadFilename");
String originalFilename=(String)model.get("originalFilename");
//서버 디렉토리에 저장된 다운로드 파일에 대한 File 객체 생성
File downloadFile=new File(uploadDir, uploadFilename);
//클라이언트에게 파일을 전달하기 위해 리스폰즈 메세지의 머릿부 변경
response.setContentType(getContentType());
response.setContentLengthLong((int)downloadFile.length());
//다운로드 처리되어 클라이언트에 저장되 파일명을 리스폰즈 메세지의 머릿부에 저장하여 전달
// => 파일명에 대한 한글 문제로 인해 부호화 처리하여 전달
originalFilename=URLEncoder.encode(originalFilename, "utf-8");
response.setHeader("Content-Disposition","attachement;filename=\\\\""+originalFilename+"\\\\";");
//리스폰즈 메세지 몸체부에 파일을 전달하여 저장하기 위한 출력스트림을 반환받아 저장
OutputStream out=response.getOutputStream();
//서버 디렉토리에 저장된 다운로드 파일을 읽기 위한 입력스트림을 생성하여 저장
InputStream in=new FileInputStream(downloadFile);
//FileCopyUtils.copy(InputStream in, OutputStream out) : 입력스트림으로 원시데이타를
//반복적으로 읽어 출력스트림으로 전달하는 메소드 - 파일 복사
FileCopyUtils.copy(in, out);
in.close();
}
}
반응형
'framework > spring mvc 웹사이트제작법' 카테고리의 다른 글
[springMVC웹] 15. 파일 업로드 프로그램 (요청 처리 메소드에서 파일 업로드 처리) (0) | 2024.08.05 |
---|---|
[springMVC웹] 14. REST API 게시판 프로그램 (0) | 2024.08.05 |
[springMVC웹] 13. 요청 처리 메소드에서 JSON으로 응답하는 법 (0) | 2024.08.05 |
[springMVC웹] 12. 암호화처리, 보안처리(권한처리), 예외처리하는 법 (0) | 2024.08.05 |
[springMVC웹]11. 트랜잭션 매니저 이용하는 법 @Transactional (0) | 2024.08.05 |