AOP (Aspect-Oriented Programming)는 관심사를 분리하여 코드 모듈화를 할 수 있도록 스프링 부트에서 지원하는 라이브러리다.
어노테이션을 통해 원하는 위치의 코드가 실행할 때, 부수적으로 수행할 공통기능을 정의할 수 있다.
AOP는 아래 세가지 용어로 설명하고 이를 활용해 수행한다.
먼저 아래와 같이 join(회원가입)하는 메서드가 있다.
@PostMapping("/join")
public String join(@Valid UserRequest.JoinDTO requestDTO, Errors errors){
System.out.println(requestDTO);
if (errors.hasErrors()) {
for (FieldError error : errors.getFieldErrors()) {
throw new Exception400(error.getDefaultMessage()+" : "+error.getField());
}
}
return "redirect:/loginForm";
}
이 join메서드는 @Valid 어노테이션을 통해 DTO 필드에 붙어있는 조건을 검사하고(NotNull 등) 검사에 통과하지 못하면 Errors에 저장한다. 이 중 아래 부분은 login에도 유효성검사를 진행하기에 반복되는 코드가 생긴다.
//코드 반복!
if (errors.hasErrors()) {
for (FieldError error : errors.getFieldErrors()) {
throw new Exception400(error.getDefaultMessage()+" : "+error.getField());
}
}
이를 전담하여 관리할 클래스를 생성한다. 해당 클래스 위에 @Aspect 어노테이션을 작성하여 디스패쳐 서블릿이 예외를 인식할 때 이 클래스로 전달할 수 있도록 한다.
@Aspect
@Component
public class ValidAdvice {
@Before("@annotation(org.springframework.web.bind.annotation.PostMapping)")
public void validationAdvice(JoinPoint jp) throws Throwable { //advice
Object[] args = jp.getArgs(); //jp의 매개변수에 접근 가능 //UserController의 join(Dto,Errors)
for (Object arg : args) {
if (arg instanceof Errors) { //해당 매개변수가 Errors면,
Errors errors = (Errors) arg; //다운 캐스팅
if (errors.hasErrors()) {
for (FieldError error : errors.getFieldErrors()) {
throw new Exception400(error.getDefaultMessage()+" : "+error.getField());
}
}
}
}
}
}
또한 @Before어노테이션을 통해 Pointcut을 발동하여 수행되기전에 어노테이션이 적용된 메서드를 실행한다. 내부에서 JointPoint객체로 받아 매개변수에 접근하여 에러 종류에 대한 예외처리를 진행할 수 있다.
controller에서 공통적인 예외처리 코드를 뺄 수 있게 된다.
@RequiredArgsConstructor
@RestController
public class UserController {
// Valid AOP 발동
@PostMapping("/valid")
public String join(@Valid JoinDTO joinDTO, Errors errors){
System.out.println(joinDTO);
return "ok";
}
}