모든 @Controller 즉, 전역에서 발생할 수 있는 예외를 잡아 처리해주는 어노테이션이다.
규모가 작은 경우는 위, 규모가 최대인 경우는 아래 방법
모든 컨트롤러의 이름은 패키지의 이름과 상관없이 다 달라야 한다.
ZBoard8Advice.java
ZBoard8RestAdvice.java
MemberRestController.java → 아이디 중복 체크, 아이디 찾기
package com.example.demo.controller.rest;
import java.security.*;
import javax.validation.constraints.*;
import org.springframework.http.*;
import org.springframework.validation.annotation.*;
import org.springframework.web.bind.annotation.*;
import com.example.demo.service.*;
import lombok.*;
// @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("잘못된 접근입니다");
// $.ajax(.....success:function() {}, error:function() { } );
// $.ajax().done(성공했을 때).fail(실패했을 때);
// 아이디 사용여부를 확인했을 때 어떤 경우 success(done)인가?
// 웹에서 성공(200)은 서버에서 오류가 발생하지 않았다 -> 어떤 결과가 나왔다
// 아이디가 사용가능하든 불가능하든(true/false) 모두 서버측에서 보면 200 -> ajax의 success가 동작 -> true/false로 if문 작성
// 내가 원하면 결과면 200, 원하지 않는 결과면 다른 상태 코드(409)를 사용하자
if(service.idCheck(username)==false)
return ResponseEntity.status(HttpStatus.CONFLICT).body("사용중인 아이디입니다");
return ResponseEntity.status(HttpStatus.OK).body("사용가능한 아이디입니다");
}
// REST 방식에서 값을 읽어내는 동작은 GET
@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);
}
}
MemberController.java
package com.example.demo.controller.mvc;
import java.security.*;
import java.time.*;
import javax.servlet.http.*;
import javax.validation.*;
import javax.validation.constraints.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.http.*;
import org.springframework.security.access.prepost.*;
import org.springframework.security.core.*;
import org.springframework.stereotype.*;
import org.springframework.validation.*;
import org.springframework.validation.annotation.*;
import org.springframework.web.bind.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.*;
import com.example.demo.controller.editor.*;
import com.example.demo.entity.*;
import com.example.demo.service.*;
// 스프링에서 파라미터와 커맨드 객체를 모두 검증하려면 @Validate를 사용
// @Validated를 사용하지 않으면 파라미터 검증이 안되고 커맨드 검증의 경우 예외가 BindException으로 달라진다
@Validated
@Controller
public class MemberController {
@InitBinder
public void init(WebDataBinder wdb) {
wdb.registerCustomEditor(LocalDate.class, new DatePropertyEditor());
}
@Autowired
private MemberService service;
@PreAuthorize("isAnonymous()")
@GetMapping("/member/join")
public void join() {
}
@PreAuthorize("isAnonymous()")
@GetMapping("/member/login")
public void login(HttpSession session, HttpServletRequest request) {
// 로그인에 실패한 경우 오류 메시지를 세션에 저장한 상태로 이곳으로 이동한다
// 세션에 메시지가 들어있으면 반복 출력된다 -> 세션에서 삭제해야 한다
// 세션에서 꺼내서 request에 옮긴 다음 삭제
if(session.getAttribute("msg")!=null) {
request.setAttribute("msg", session.getAttribute("msg"));
session.removeAttribute("msg");
}
}
// @Valid를 사용하면 커맨드 객체(사용자가 입력한 값을 담은 클래스)에 대한 검증을 스프링이 수행한다
// 객체가 아니라 파라미터에 대해 검증하려면 @Validated 어노테이션을 컨트롤러에 지정해야 한다
@PreAuthorize("isAnonymous()")
@PostMapping("/member/join")
public String join(@Valid Member member, BindingResult bindingResult, RedirectAttributes ra) {
// @Valid로 검증하면 검증 오류가 발생하서 BindingResult에 자동으로 담긴다
// 그래서 컨트롤러에 파라미터를 기술할 때 유일하게 순서가 있는 것이 BindingResult는 @Valid 바로 뒤에 와야 한다
service.join(member);
ra.addFlashAttribute("msg", "가입확인 코드를 이메일로 보냈습니다. 이메일을 확인하세요");
return "redirect:/member/check_join";
}
@PreAuthorize("isAnonymous()")
@GetMapping("/member/check_join")
public void checkJoin() {
// RedirectAttribute 담은 값을 화면에 출력하는 것은 추가 작업이 불필요
// 컨트롤러에서 꺼내려면 코딩이 필요
}
@PreAuthorize("isAnonymous()")
@PostMapping("/member/check_join")
public String checkJoin(String checkcode, RedirectAttributes ra) {
Boolean result = service.checkcode(checkcode);
if(result==true)
return "redirect:/member/login";
else {
// "체크코드를 확인하세요"라는 오류 메시지를 출력하려고 한다
// 에러메시지를 담은 다음 redirect해서 출력한다
ra.addFlashAttribute("msg", "체크코드를 확인하세요");
return "redirect:/member/check_join";
}
}
@PreAuthorize("isAnonymous()")
@GetMapping("/member/find_id")
public void findId() {
}
@PreAuthorize("isAnonymous()")
@GetMapping("/member/reset_password")
public void resetPassword() {
}
@PreAuthorize("isAnonymous()")
@PostMapping("/member/reset_password")
public String resetPassword(String username, String email, RedirectAttributes ra) {
// 비밀번호를 찾으면 로그인 창으로 이동해 "임시비밀번호를 이메일로 전송했습니다"라고 출력
// 비밀번호를 못찾으면 GET /member/reset_password으로 이동해서 "비밀번호를 찾지 못했습니다"라고 출력
Boolean result = service.resetPassword(username, email);
if(result==true) {
ra.addFlashAttribute("msg", "임시비밀번호를 이메일로 전송했습니다");
return "redirect:/member/login";
} else {
ra.addFlashAttribute("msg", "비밀번호를 찾지 못했습니다");
return "redirect:/member/reset_password";
}
}
@PreAuthorize("isAuthenticated()")
@GetMapping("/member/change_password")
public void changePassword(HttpSession session, HttpServletRequest request) {
if(session.getAttribute("msg")!=null) {
request.setAttribute("msg", session.getAttribute("msg"));
session.removeAttribute("msg");
}
}
@PreAuthorize("isAuthenticated()")
@PostMapping("/member/change_password")
public String changePassword(String password, Principal principal) {
service.changePassword(password, principal.getName());
return "redirect:/";
}
}
SystemController.java
package com.example.demo.controller.mvc;
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;
@Controller
public class SystemController {
// 스프링 시큐리티 설정에서 403이 발생했을 때 이동 - 설정에서는 ra, session을 사용할 수 없다 -> 403 고정 페이지를 따로 만듬
@GetMapping("/system/e403")
public void e403() {
}
// ControllerAdvice를 거쳐서 온다 - 상황에 따라 서로 다른 오류 메시지가 ra, session에 담겨있다
@GetMapping("/system/error")
public void error() {
}
}