스프링

 

스프링부트 사용 버전

소스 : 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

 

 

 

about author

PHRASE

Level 60  라이트

못된 나무에 열매만 많다 / 못된 소나무에 솔방울만 많다 , 못된 것이 도리어 성하게 되는 경우를 이르는 말.

댓글 ( 8)

댓글 남기기

작성