스프링부트 사용 버전
소스 : 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)
댓글 남기기