AOP 개념 정리와 유효성 검사의 중요성
AOP
package shop.mtcoding.bank.handler.aop;
import java.util.HashMap;
import java.util.Map;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import shop.mtcoding.bank.handler.ex.CustomValidationException;
@Aspect // 관점
@Component // 메모리에 띄우기
public class CustomValidationAdvice {
@Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
public void postMapping() {
}
@Pointcut("@annotation(org.springframework.web.bind.annotation.PutMapping)")
public void putMapping() {
}
// Before, After
@Around("putMapping() || postMapping()") // jointpoint 전, 후 제어
public Object validationAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object[] args = proceedingJoinPoint.getArgs(); // joinpoint 매개변수
for (Object arg : args) {
if (arg instanceof BindingResult) { // 매소드에 BindingResult가 존재하면
BindingResult bindingResult = (BindingResult) arg;
if (bindingResult.hasErrors()) { // 에러가 존재하면
Map<String, String> errorMap = new HashMap<>();
for (FieldError error : bindingResult.getFieldErrors()) {
errorMap.put(error.getField(), error.getDefaultMessage());
}
throw new CustomValidationException("유효성검사 실패", errorMap);
}
}
}
return proceedingJoinPoint.proceed(); // 진행시켜~
}
}
@Around : 이거일 경우만 ProceedingJoinPoint를 받을 수 있다.
@Before
@After
putMapping() || postMapping() 가 있는 모든 메소드가 실행 전에 동작
코드 추가
package shop.mtcoding.bank.handler.ex;
import java.util.Map;
import lombok.Getter;
@Getter
public class CustomValidationException extends RuntimeException {
private Map<String, String> errorMap;
public CustomValidationException(String message, Map<String, String> errorMap) {
super(message);
this.errorMap = errorMap;
}
}
코드 추가
package shop.mtcoding.bank.handler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import shop.mtcoding.bank.dto.ResponseDto;
import shop.mtcoding.bank.handler.ex.CustomApiException;
import shop.mtcoding.bank.handler.ex.CustomValidationException;
@RestControllerAdvice
public class CumstomExceptionHandler {
private final Logger log = LoggerFactory.getLogger(getClass());
@ExceptionHandler(CustomApiException.class)
public ResponseEntity<?> apiException(CustomApiException e) {
log.error(e.getMessage());
return new ResponseEntity<>(new ResponseDto<>(-1, e.getMessage(), null), HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(CustomValidationException.class)
public ResponseEntity<?> validationApiException(CustomValidationException e) {
log.error(e.getMessage());
return new ResponseEntity<>(new ResponseDto<>(-1, e.getMessage(), e.getErrorMap()), HttpStatus.BAD_REQUEST);
}
}
만약 postMapping을 @Around에서 제거하면
아직 길이 제한에 대한 유효성 검사가 필요, 반드시 각 레이어는 본인의 책임을 가져야한다.
정리