71일: Spring 예외처리(ControllerAdvice)

Jiwontwopunch·2022년 2월 26일
0

국비기록

목록 보기
71/121
post-thumbnail

ControllerAdvice

모든 @Controller 즉, 전역에서 발생할 수 있는 예외를 잡아 처리해주는 어노테이션이다.
규모가 작은 경우는 위, 규모가 최대인 경우는 아래 방법
모든 컨트롤러의 이름은 패키지의 이름과 상관없이 다 달라야 한다.

패키지 분리

com.example.demo.controller.advice

ZBoard8Advice.java

ZBoard8RestAdvice.java

com.example.demo.controller.rest

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);
	}
}

com.example.demo.controller.mvc

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() {
	}
}

정리..

0개의 댓글