본문 바로가기
Java/Spring

Spring) AOP - 특정 메소드 소요시간 계산, Escaping 처리, ErrorLog처리

by 박채니 2022. 8. 28.

안녕하세요, 코린이의 코딩 학습기 채니 입니다.

 

개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.


Aspect로 특정 메소드 소요 시간 계산하기

 

StopWatchAspect

@Component
@Aspect
@Slf4j
public class StopWatchAspect {
	
	@Pointcut("execution(* com.ce.spring2.todo.controller.TodoController.insertTodo(..))")
	public void pointcut() {}
	
	@Around("pointcut()")
	public Object stopWatch(ProceedingJoinPoint joinPoint) throws Throwable {
		StopWatch stopWatch = new StopWatch();

		// before
		stopWatch.start();	
		
		// 주업무 실행 (insertTodo)
		Object obj = joinPoint.proceed();
		
		// after
		stopWatch.stop();
		
		long millis = stopWatch.getTotalTimeMillis();
		log.debug("소요시간 - {}", millis);
		
		return obj;
	}
}

@콘솔출력값

DEBUG: com.ce.spring2.common.aop.LogAspect - com.ce.spring2.todo.controller.TodoController.insertTodo 실행 전!
DEBUG: com.ce.spring2.common.aop.LogAspect - com.ce.spring2.todo.model.service.TodoService.insertTodo 실행 전!
DEBUG: com.ce.spring2.todo.model.dao.TodoDao.insertTodo - ==>  Preparing: insert into todo values (seq_todo_no.nextval, ?, default, null) 
DEBUG: com.ce.spring2.todo.model.dao.TodoDao.insertTodo - ==> Parameters: 약 먹기(String)
DEBUG: com.ce.spring2.todo.model.dao.TodoDao.insertTodo - <==    Updates: 1
DEBUG: com.ce.spring2.common.aop.LogAspect - com.ce.spring2.todo.model.service.TodoService.insertTodo 실행 후!
DEBUG: com.ce.spring2.common.aop.StopWatchAspect - 소요시간 - 2550
DEBUG: com.ce.spring2.common.aop.LogAspect - com.ce.spring2.todo.controller.TodoController.insertTodo 실행 후!

 


Escaping 처리하기

 

아래와 같이 escaping 처리가 되지 않아있기 때문에, script태그 자체로 받아드려서 alert가 띄워진 것을 확인할 수 있습니다.

이를 보호하기 위해 Aspect를 통해 Escaping 처리를 해보겠습니다.

 

Controller

TodoController

@GetMapping("/todoList.do")
public ModelAndView todoList(ModelAndView mav) {
    try {
        // new TodoServiceImpl()
//			log.debug("todoService = {}", new TodoServiceImpl().getClass());
//			log.debug("todoService = {}", todoService.getClass());

        List<Todo> list = todoService.selectAllTodo();
        mav.addObject("list", list);

        return mav;
    } catch(Exception e) {
        log.error(e.getMessage(), e);
        throw e;
    }
}

ModelAndView를 리턴하고 있는 todoList에 대해서 Escaping 처리

 

EscapeXmlAspect

@Component
@Aspect
@Slf4j
public class EscapeXmlAspect {
	
	@Pointcut("execution(* com.ce.spring2.todo.controller.TodoController.todoList(..))")
	public void pc() {}
	
	// returning => AfterReturning은 리턴값에 접근할 수 있는데, 해당 리턴값을 어느 변수명에 담아줄 지 결정!
	@AfterReturning(pointcut = "pc()", returning = "returnObj")
	public void escapeXml(JoinPoint joinPoint, Object returnObj) {
		ModelAndView mav = (ModelAndView) returnObj;
		log.debug("mav = {}", mav);
	}
}	

@콘솔출력값
DEBUG: com.ce.spring2.common.interceptor.LogInterceptor - mav = ModelAndView [view="todo/todoList"; model={msg=할일을 추가하였습니다., modelAndView=ModelAndView [view=[null]; model={list=[Todo(no=1, todo=우산 청소하기, createdAt=2022-08-24T12:57:38, completedAt=null), Todo(no=2, todo=형광등 교체, createdAt=2022-08-24T12:57:48, completedAt=null), Todo(no=21, todo=딸기 요거트 사기, createdAt=2022-08-24T14:34:18, completedAt=null), Todo(no=41, todo=약 먹기, createdAt=2022-08-25T14:59:47, completedAt=null), Todo(no=61, todo=<script> alert('메롱') </script>, createdAt=2022-08-28T14:37:37, completedAt=null), Todo(no=3, todo=장 보기, createdAt=2022-08-24T12:57:56, completedAt=2022-08-24T14:58:36), Todo(no=4, todo=차에 물 퍼내기, createdAt=2022-08-24T12:58:02, completedAt=2022-08-24T14:54:47)]}], org.springframework.validation.BindingResult.modelAndView=org.springframework.validation.BeanPropertyBindingResult: 0 errors, list=[Todo(no=1, todo=우산 청소하기, createdAt=2022-08-24T12:57:38, completedAt=null), Todo(no=2, todo=형광등 교체, createdAt=2022-08-24T12:57:48, completedAt=null), Todo(no=21, todo=딸기 요거트 사기, createdAt=2022-08-24T14:34:18, completedAt=null), Todo(no=41, todo=약 먹기, createdAt=2022-08-25T14:59:47, completedAt=null), Todo(no=61, todo=<script> alert('메롱') </script>, createdAt=2022-08-28T14:37:37, completedAt=null), Todo(no=3, todo=장 보기, createdAt=2022-08-24T12:57:56, completedAt=2022-08-24T14:58:36), Todo(no=4, todo=차에 물 퍼내기, createdAt=2022-08-24T12:58:02, completedAt=2022-08-24T14:54:47)]}]

 

todoList의 리턴값인 ModelAndView가 잘 출력되는 것을 확인할 수 있습니다.

 

Escaping 처리

@Component
@Aspect
@Slf4j
public class EscapeXmlAspect {
	
	@Pointcut("execution(* com.ce.spring2.todo.controller.TodoController.todoList(..))")
	public void pc() {}
	
	// returning => AfterReturning은 리턴값에 접근할 수 있는데, 해당 리턴값을 어느 변수명에 담아줄 지 결정!
	@AfterReturning(pointcut = "pc()", returning = "returnObj")
	public void escapeXml(JoinPoint joinPoint, Object returnObj) {
		ModelAndView mav = (ModelAndView) returnObj;
		log.debug("mav = {}", mav);
		
		Map<String, Object> model = mav.getModel();
		List<Todo> list = (List<Todo>)model.get("list");
		for(Todo todo : list) {
			String maybeXss = todo.getTodo();
			String safeXss = escapeXmlProcess(maybeXss);
			todo.setTodo(safeXss);
		}
	}

	private String escapeXmlProcess(String maybeXss) {
		return maybeXss.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
	}
}

getModel로 Model 객체를 가져왔으며, 그 중 list속성을 가져와 for문을 통해 각 todoList에 대해 escaping처리를 하였습니다.

더이상 alert()가 뜨지 않으며, 잘 출력되는 것을 확인할 수 있습니다.

 


ErrorLog 처리하기

 

Controller

TodoController

@GetMapping("/todoList.do")
public ModelAndView todoList(ModelAndView mav) {
        // new TodoServiceImpl()
//			log.debug("todoService = {}", new TodoServiceImpl().getClass());
//			log.debug("todoService = {}", todoService.getClass());

    List<Todo> list = todoService.selectAllTodo();
    mav.addObject("list", list);

    if(true) {
        throw new RuntimeException("메롱"	);
    }

    return mav;
}

 

ErrorLogAspect

@Component
@Aspect
@Slf4j
public class ErrorLogAspect {
	// @Pointcut('execution(* com.ce.spring2..*Controller.*(..))") 도 가능!
	@Pointcut("within(com.ce.spring2.*.controller.*)") // com.ce.spring2.어느패키지.controller 하위의 모든 클래스
	public void pc() {}
	
	@AfterThrowing(pointcut = "pc()", throwing = "e")
	public void errorLogAdvice(JoinPoint joinPoint, Exception e) {
		log.debug("ErrorLogAspect 실행!");
		log.error(e.getMessage(), e);
	}
}

@콘솔출력값
DEBUG: com.ce.spring2.common.aop.ErrorLogAspect - ErrorLogAspect 실행!
ERROR: com.ce.spring2.common.aop.ErrorLogAspect - 메롱
java.lang.RuntimeException: 메롱
	at com.ce.spring2.todo.controller.TodoController.todoList(TodoController.java:41)

com.ce.spring2.어느패키지.controller 하위의 모든 클래스(메소드)들에 대해서 예외가 발생하면 실행되도록 하였습니다.

throwing = "e" 를 통해 예외객체를 받아 처리해줄 수 있습니다.

 

TodoList 조회 시 발생한 에러로그를 확인해보면 ErrorLogAspect를 통해 로그가 출력된 것을 확인할 수 있습니다.

이로써 매번 try~catch절을 사용하던 controller의 부담을 줄여줄 수가 있습니다.