스프링부트 사용 버전
소스 : https://github.com/braverokmc79/Springboot-JPA-Blog
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.2</version> <relativePath/> <!-- lookup parent from repository --> </parent>
1.설정
1~2번만해도 글로벌 에러페이지 설정완료
1) application.properties 설정
#################################################################################### #################################################################################### #########################에러페이지 설정########################################### #오류 응답에 exception의 내용을 포함할지 여부,기본적으로 발생하게 된 에러 내용을 모두 포함 server.error.include-exception= true #오류 응답에 stacktrace 내용을 포함할지 여부 (ALWAYS, NEVER, ON_TRACE_PARAM) #stacktrace는 오류가 발생하게 된 과정에 대한 로그를 의미 server.error.include-stacktrace=ALWAYS #브라우저 요청에 대해 서버 오류시 기본으로 노출할 페이지를 사용할지 여부, 스프링부트에서 제공하는 기본 에러 페이지를 사용할 것인지에 대한 설정 #server.error.whitelabel.enabled 속성을 false 로 설정하여 화이트 라벨 오류 페이지를 완전히 비활성화 server.error.whitelabel.enabled= false #이 항목을 application.properties 파일에 추가하면 오류 페이지가 비활성화되고 기본 애플리케이션 컨테이너(예: Tomcat)에서 시작된 간결한 페이지가 표시됩니다. #########################에러페이지 설정########################################### #################################################################################### ####################################################################################
2) GlobalExceptionHandler 추가
package com.cos.blog.handler; import java.util.Date; import javax.servlet.RequestDispatcher; import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.web.servlet.error.ErrorController; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; //@ControllerAdvice 설정을 하지 않는다. //설정을 하지 않아도,스프링부트에서 error 로 매핑된 url 주소를 통해 에러 처리 @Controller public class GlobalExceptionHandler implements ErrorController { private final Logger logger = LoggerFactory.getLogger(this.getClass()); // 에러 페이지 정의 private final String ERROR_404_PAGE_PATH = "error/404"; private final String ERROR_500_PAGE_PATH = "error/500"; private final String ERROR_ETC_PAGE_PATH = "error/error"; //Json type 형태의 예외처리 와 CustomException 을 설정한것 이곳에서 처리된다. @RequestMapping(value = "/jsonError") @ExceptionHandler({ CustomException.class //HttpRequestMethodNotSupportedException.class }) @ResponseBody public ResponseEntity JsonException(String statusCode, String msg) { logger.info("ajax ,json 형태의 예외처리 error "); if(msg!=null){ logger.info("statusCode {}", statusCode); logger.info("msg {}", msg); return ResponseEntity.badRequest().body(statusCode + " : " +msg); } return ResponseEntity.badRequest().body("error"); } @RequestMapping(value = "/error") @ExceptionHandler({ Exception.class }) public String handleError(HttpServletRequest request, Model model) { // 에러 코드를 획득한다. Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE); // HttpStatus와 비교해 페이지 분기를 나누기 위한 변수 int statusCode = 0; if(status != null){ statusCode = Integer.valueOf(status.toString()); } // 에러 코드에 대한 상태 정보 HttpStatus httpStatus = HttpStatus.valueOf(statusCode); logger.info("에러 코드에 대한 상태 정보 :" +httpStatus); String contentType=request.getContentType(); logger.info("ContentType에 대한 정보 :" +contentType); //1.JSON 타입으로 요청한 에러일경우 다음과 같이 처리 if(request.getContentType()!=null && contentType.contains("application/json")){ logger.info("JSON 방식을 통한 에러 요청 : " +request.getContentType()); return "redirect:/jsonError?statusCode="+statusCode+"&msg="+httpStatus.getReasonPhrase(); } if (status != null) { // 로그로 상태값을 기록 및 출력 logger.info("httpStatus : " + statusCode); // 404 error if (statusCode == HttpStatus.NOT_FOUND.value()) { // 에러 페이지에 표시할 정보 model.addAttribute("code", status.toString()); model.addAttribute("msg", httpStatus.getReasonPhrase()); model.addAttribute("timestamp", new Date()); return ERROR_404_PAGE_PATH; } // 500 error if (statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) { // 서버에 대한 에러이기 때문에 사용자에게 정보를 제공하지 않는다. return ERROR_500_PAGE_PATH; } } // 정의한 에러 외 모든 에러는 error/error 페이지로 보낸다. return ERROR_ETC_PAGE_PATH; } }
3) 별도
CustomException
public class CustomException extends Exception { // 1. 매개 변수가 없는 기본 생성자 public CustomException() { } // 2. 예외 발생 원인(예외 메시지)을 전달하기 위해 String 타입의 매개변수를 갖는 생성자 public CustomException(String message) { super(message); // RuntimeException 클래스의 생성자를 호출합니다. } }
4 ) 다음을 참조해서 에러페에지 만들기
HTML 404 Page Templates
https://freefrontend.com/html-css-404-page-templates/
2.사용 예
constroller
public ResponseEntity<?> save(@RequestBody User user) throws Exception{
throws Exception 과 try catch 문을 사용하지 않아도 스프링부트 내에서 /error url 이 기본 매핑되어 이동 처리되어진다.
package com.cos.blog.controller.api; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import com.cos.blog.model.User; import com.cos.blog.service.UserService; import lombok.RequiredArgsConstructor; @RestController @RequiredArgsConstructor public class UserApiController { private final Logger log=LoggerFactory.getLogger(this.getClass()); private final UserService userService; @PostMapping("/auth/joinProc") public ResponseEntity<?> save(@RequestBody User user) { //실제로 DB에 insert 를 하고 아래에서 리턴 log.info("회원 가입"); try{ userService.userJoin(user); }catch (DataIntegrityViolationException e) { return new ResponseEntity<String>("중복된 데이터 입니다.", HttpStatus.BAD_REQUEST); }catch (Exception e) { return new ResponseEntity<String>("등록 오류 입니다.", HttpStatus.BAD_REQUEST); } return new ResponseEntity<Integer>(1, HttpStatus.OK); }
service
import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.cos.blog.model.User; import com.cos.blog.repository.UserRepository; import lombok.RequiredArgsConstructor; /** * 서비스 * 1. 트랜잭션 관리 * 2. 두개 이상 서비스 create, update, * * select 만 있을 경우 : @Transactional(readOnly = true) 스프링이 컴포넌트 스캔을 통해서 Bean 에 등록을 해줌. IoC 를 해준다. * */ @Service @RequiredArgsConstructor @Transactional public class UserService { //@Autowired @RequiredArgsConstructor 통해 생성 private final UserRepository userRepository; public User userJoin(User user) { User result=userRepository.save(user); return result; } }
js
let user ={ init:function(){ $("#btn-save").on("click",()=>{ //function()P{, ()=>{} this를 바인딩하기 위해서, // function을 사용시에는 this 아니라 변수 user 를 사용 예) user.save() this.save(); }); $("#btn-login").on("click", function(){ user.login(); }); }, save:function(){ const $home=$("#home").val(); let data={ username:$("#username").val(), password:$("#password").val(), email:$("#email").val() }; console.log(data); console.log("$home : " +$home); //ajax 호출시 default가 비동기 호출 //ajax 통신을 이용해서 3개의 데이터를 json 으로 변경하여 insert 요청! //application/json 통신시 반환되는 dataType 은 json $.ajax({ type:"POST", url:$home+"auth/joinProc", data:JSON.stringify(data), //JSON 문자열로 변환 - http body 데이터 contentType:"application/json; charset=urf-8", // body 데이터가 어떤 타입인지(MIME) dataType:"json" //요청을 서버로해서 응담이 왔을 때 기본적으로 모든 것이 문자열(생긴것이 json이라면 ) => javascript }).done(function(res, status){ console.log(res, status); alert("회원가입이 완료 되었습니다."); location.href=$home+"user/loginForm"; }).fail(function(res, status, error){ console.log(res, status, error); console.log("res.responseText :" +res.responseText); console.log(JSON.stringify(res)); alert(res.responseText); }); // $.ajax({ // type:"POST", // url:$home+"/api/user", // data:JSON.stringify(data), //JSON 문자열로 변환 - http body 데이터 // contentType:"application/json; charset=urf-8", // body 데이터가 어떤 타입인지(MIME) // dataType:"json" //요청을 서버로해서 응담이 왔을 때 기본적으로 모든 것이 문자열(생긴것이 json이라면 ) => javascript // ,success:function(result, status){ // //console.log(result, status); // }, // error:function(res, status, error){ // console.log(res, status, error); // console.log("data :" +res.responseText); // console.log(JSON.stringify(res)); // alert(res.responseText); // } // }); }, login:function(){ const $home=$("#home").val(); let data={ username:$("#username").val(), password:$("#password").val(), }; console.log(data); $.ajax({ type:"POST", url:$home+"api/user/login", data:JSON.stringify(data), //JSON 문자열로 변환 - http body 데이터 contentType:"application/json; charset=urf-8", // body 데이터가 어떤 타입인지(MIME) dataType:"json" //요청을 서버로해서 응담이 왔을 때 기본적으로 모든 것이 문자열(생긴것이 json이라면 ) => javascript }).done(function(res, status){ console.log("done"); console.log(res, status); if(res==1){ alert("로그인이 완료 되었습니다.."); location.href=$home; } }).fail(function(res, status, error){ console.log("fail"); console.log(res, status, error); console.log(JSON.stringify(res)); if(res.responseText=="2"){ alert("아이디 또는 비밀번호가 일치 하지 않습니다."); }else{ alert(res.responseText); } }); } } user.init();
joinForm.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:layout=http://www.ultraq.net.nz/thymeleaf/layout layout:decorate="~{layouts/layout1}"> <div layout:fragment="content"> <div class="container"> <form th:action="@{/}" method="post"> <div class="form-group"> <label for="username">Username</label> <input type="text" class="form-control" placeholder="Enter username" id="username" name="username" required="required"> </div> <div class="form-group"> <label for="password">Password</label> <input type="password" class="form-control" placeholder="Enter password" id="password" name="password" required="required"> </div> <div class="form-group"> <label for="email">Email</label> <input type="email" class="form-control" placeholder="Enter email" id="email" name="email" required="required"> </div> <button type="button" id="btn-save" class="btn btn-primary">회원가입완료</button> </form> </div> </div> <th:block layout:fragment="script"> <script th:src="@{/js/user.js}"></script> </th:block> </html>
3. 에러 페이지 템플릿 만들기
https://freefrontend.com/html-css-404-page-templates/
위 사이트를 참조해서 에러 페이지 템플릿 만들기
1) https://github.com/braverokmc79/Springboot-JPA-Blog/tree/master/src/main/webapp/WEB-INF/views/error
2) https://github.com/braverokmc79/Springboot-JPA-Blog/tree/master/src/main/resources/templates/error
댓글 ( 8)
댓글 남기기