안녕하세요, 코린이의 코딩 학습기 채니 입니다.
개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.
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("<", "<").replaceAll(">", ">");
}
}
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의 부담을 줄여줄 수가 있습니다.
'Java > Spring' 카테고리의 다른 글
Spring) 게시글 작성 - 파일 업로드 처리를 위한 설정 (0) | 2022.08.30 |
---|---|
Spring) 페이징 처리 (content, pagebar 영역) - RowBounds (0) | 2022.08.30 |
Spring) AOP 흐름 이해하기, 원리 및 구조 파악 (0) | 2022.08.25 |
Spring) AOP 개념 정리 (0) | 2022.08.25 |
Spring) 할일 체크하기 (0) | 2022.08.24 |