📝 MVC와 REST 분리
에러 페이지 처리를 위해서 Advice(부가 기능을 담은 객체)를 사용하려고 한다. 이때 예외에 응답하는 컨트롤러를 지정해 주어야 하는데, MVC 방식과 REST 방식은 응답 방식이 다르다. 따라서 MVC와 REST를 분리하여 작업하려고 한다.
위와 같이 분리해 놓았다.
📝 에러 페이지 - e403과 error
e403
Spring Security에서 발생한 403 상태 코드에 대한 처리 페이지이다.
error
ControllerAdvice를 거쳐, 다양한 상황에서 RedirectAttibutes나 HttpSession에 담긴 에러들을 처리하는 페이지이다.
e403.html
, error.html
생성<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" xmlns:th="http://www.thymeleaf.org">
<title>잘못된 접근</title>
</head>
<body>
<p>잘못된 접근입니다.</p>
<a href="/">첫 페이지로 이동</a>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" xmlns:th="http://www.thymeleaf.org">
<title>Insert title here</title>
</head>
<body>
<p th:text="${msg}"></p>
<a href="/">첫 페이지로 이동</a>
</body>
</html>
지난 시간에 SecurityConfig - accessDeniedPage("/system/e403");
해 준 부분의 화면을 만든다.
기존의 MemberController
에 있던 REST 방식의 메소드들을 src/main/java - com.example.demo.controller.rest - MemberRestController
로 이동 후 수정
package com.example.demo.controller.rest;
import java.security.Principal;
import javax.validation.constraints.NotEmpty;
import org.springframework.http.*;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import com.example.demo.service.MemberService;
import lombok.AllArgsConstructor;
// @RestController로 작동하는 메소드의 집합, @Controller는 REST + MVC
@Validated
@AllArgsConstructor
@RestController
public class MemberRestController {
private MemberService service;
// javax.validation을 이용한 사용자 입력 값 검증
@GetMapping("/member/id_check")
public ResponseEntity<String> idCheck(@NotEmpty(message="아이디는 필수 입력입니다.") String username, Principal principal) {
if (principal != null)
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("잘못된 접근입니다.");
if (service.idCheck(username)==false)
return ResponseEntity.status(HttpStatus.CONFLICT).body("사용 중인 아이디입니다.");
return ResponseEntity.status(HttpStatus.OK).body("사용 가능한 아이디입니다.");
}
@ResponseBody
@GetMapping("/member/find/id")
public ResponseEntity<String> findId(@NotEmpty(message="아이디는 필수 입력입니다.") String email, Principal principal) {
if (principal != null)
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("잘못된 접근입니다.");
String username = service.findId(email);
if (username == null)
return ResponseEntity.status(HttpStatus.CONFLICT).body("아이디를 찾지 못했습니다.");
return ResponseEntity.ok(username);
}
}
src/main/java - com.example.demo.controller.mvc - SystemController
생성
package com.example.demo.controller.mvc;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class SystemController {
@GetMapping("/system/e403")
public void e403() {
}
@GetMapping("/system/error")
public void error() {
}
}
src/main/java - com.example.demo.controller.advice - ProjectAdvice
, ProjectRestAdvice
생성
package com.example.demo.controller.advice;
import javax.validation.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.*;
@ControllerAdvice(basePackages = "com.example.demo.controller.mvc")
public class ProjectAdvice {
@ExceptionHandler(ConstraintViolationException.class)
public String constraintViolationException(ConstraintViolationException e, RedirectAttributes ra) {
ra.addFlashAttribute("msg", e.getMessage());
return "redirect:/system/error";
}
}
package com.example.demo.controller.advice;
import javax.validation.*;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.*;
@ControllerAdvice(basePackages = "com.example.demo.controller.rest")
public class ProjectRestAdvice {
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<String> constraintVoilationException(ConstraintViolationException e, RedirectAttributes ra) {
return ResponseEntity.status(HttpStatus.CONFLICT).body(e.getMessage());
}
}
💡 ConstraintViolationException
메소드의 파라미터나 리턴 값에 문제가 있을 때 발생하는 오류이다. 500 상태 코드를 반환한다. 본문은 @ControllerAdvice를 이용해 그에 대한 변경 처리를 해 준 것이다.
예외에 응답하는 컨트롤러 역할을 한다.