https://bpeach.tistory.com/31

 

AOP 관점지향 프로그래밍 - 전처리, 후처리

전처리와 후처리를 나누기 전 클라이언트에서 userName, Password 등 데이터를 입력해서 요청하면 서버와 DB를 걸친 뒤에 응답한다. 이런 경우, userName이나 Password에 설정한 길이 또는 userName의 동일 여

bpeach.tistory.com

전에 작성한 AOP 관점지향 프로그래밍을 이제 객체 지향 프로그래밍과 합해 코드를 간단하게 만들어 보려고 한다.

 

먼저 전처리와 후처리, 그리고 공통 기능을 파악해야 한다. 

  로그인 회원가입
전처리 유효성 검사, 보안처리 유효성 검사, 보안처리
핵심 username, password username, password,
address, name ...
DB - SELECT DB - INSERT
로그인 완료(세션 만드는 것)  
후처리 로그(언제 로그인 했는 지 등) 로그(언제 회원가입 했는 지 등)

 

예시로 로그인과 회원가입 기능을 보며 전처리와 후처리가 동일한 기능을 한다. 공통기능으로 묶어 필터링 처리를 하면 된다. 

 

@RequiredArgsConstructor
@Controller
public class AuthController{
    private static final Logger log = LoggerFactory.getLogger(AuthController.class);
    
    private final AuthService authService;
    
    ....
    
    @PostMapping("auth/signup")
    public String signup(@Valid SignupDto signupDto, BindingResult bindingResult){
    	if(bindingResult.hasErrors()){ //Valid에서 선정한 max, notbink의 에러가 발생한다면
        	Map<String, String> errorMap = new HashMap<>();
            
            for(FieldError error : bindingResult.getFieldErrors()){
            	errorMap.put(error.getField(), error.getDefaultMessage());
           	}
            //사용자에게 보이면 좋은 UX가 아니기 때문에 Exception을 가로채는 Handler패키지 만듦
            throw new CustomValidationException("유효성검사 실패함", errorMap);
        }else{
            User user = signupDto.toEntity(); //User에 대한 데이터 넣음
            User userEntity = authService.회원가입(user);
            return "auth/singin"; //회원가입 성공 시 로그인 페이지로 이동
       	}
   }
}

 

기존에는 Controller에서 유효성 검사가 필요한 메서드에 하나하나 작성했다. 

 

 

이제는 위에서 말한 것처럼 공통기능을 묶어서 핸들러 할 수 있게 하려고 한다.

먼저 ValidationAdvice라는 클래스 하나를 생성하고 여기서 Advice은 공통기능이라는 뜻

@Component
@Aspect
public class ValidatonAdvice{
    @Around("execution(* com.cos.photogramstart.web.api.*Controller.*(..))")
    public Object apiAdive(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
    	Object[] args = proceedingJoinPoint.getArgs();
        
        for(Object arg : args){
            if(arg instanceof BindingResult){
            	System.out.println("유효성 검사하는 함수입니다");
                BindingResult bindingResult = (BindingResult) arg;
                
                if(bindingResult.hasErrors()){
                	Map<String, String> errorMap = new HashMp<>();
                    
                    for(FieldError error : bindingResult.getFieldErrors()){
                    	errorMap.put(error.getField(), error.getDefaultMessage());
                    }
                    throw new CustomValidationApiException("유효성 검사 실패", errorMap);
                }
            }
       }
       return proceedingJoinPoin.proceed(); //그 함수로 다시 돌아가라
    }
   
    @Around("execution(* com.cos.phtogramstart.web.*Controller.*(..))")
    public Object advice(ProceedingJoinPoint proceedingJoinPoint) throw Throwable{
        Object[] args = proceedingJoinPoint.getArgs();
        
        for(Object arg : args){
            if(arg instanceof BindingResult){
            	System.out.println("유효성 검사하는 함수입니다.");
                BindingResult bindingResult = (Binding)arg;
                
                if(bindingResult.hasError()){
                    Map<String, String> errorMap = new HashMap<>();
                    
                    for(FieldError error : bindingResult.getFieldErrors()){
                    	errorMap.put(error.getField(), error.getDefaultMessage());
                    }
                    throw new CustomValidationException("유효성 검사 실패함", errorMap);
                }
            }
        }
        return proceedingJoinPoint.proceed();
    }
}

 

  • @Component :  RestController, Service 모든 것들이 Component를 상속해서 만들어져 있다. 
  • @Aspect : AOP 할 수 있는 Handler 애너테이션
  • @Around :  @Before와 @After 모두 사용해서 앞과 뒤에서 체크 가능한 애너테이션, (처음에 나온 * 은 public, proctected, private 등 함수 정하는 건데 모두 사용할 거니까 * 로 표시, *(..)은 모든 컨트롤러가 작동할 때 모든 함수가 작동한다는 것을 의미함)
  • ProceedingJoinPoint : 함수의 파라미터뿐만 아니라 내부 정보에 접근할 수 있는 파라미터(여기서는 Controller 안에 있는 함수의 모든 곳에 접근할 수 있는 변수로, Controller 안에 있는 함수들보다 먼저 실행함)

ProceedingJoinPoint를 좀 더 자세히 설명하면, 공통기능인 Advice를 실행할 때 사용되는 지점에서 나타나고, Adivce가 적용될 수 있는 지점(메서드 호출, 예외 발생 등)을 가리키며, Advice가 실행되는 동안 해당 지점에서의 실행 흐름을 제어할 수 있음. 이를 통해 AOP를 사용하여 로깅, 트랜잭션 관리, 보안 등과 같은 횡단 관심사를 분리된 모듈로 처리할 수 있다.

 

@RequiredArgsConstructor
@Controller
public class AuthController{
    private static final Logger log = LoggerFactory.getLogger(AuthController.class);
    
    private final AuthService authService;
    
    ....
    
    @PostMapping("auth/signup")
    public String signup(@Valid SignupDto signupDto, BindingResult bindingResult){
    	log.info(signupDto.toString());
        User user = signupDto.toEntity();
        authService.회원가입(user);
        return "auth/singin"; //회원가입 성공 시 로그인 페이지로 이동
   }
}

 

위의 Adivce 클래스를 따로 구현하면 원래 작성했던 authControllerr의 signup 함수를 간단하게 작성할 수 있다.

+ Recent posts