본 글은 AOP에 대해 어느정도 이해를 했다는 전제 하에 적은 글입니다.
회사 프로젝트 진행 중 로그 저장 및 조회 기능을 만들어야 했다.
처음에는 logService 하나를 만든 후 로그를 저장해야 하는 곳에 일일이 DI를 하자고 지시가 내려왔으나
이걸 언제 다 붙이고 다니기도 귀찮고 코드 섞이는 것도 보기 싫어서
AOP랑 인터셉터 둘 중 하나를 쓰자고 제안했다.
참고 : 회사 코드를 유출할 수는 없어서 거의 새로운 코드로 다시 재 작성했습니다.
Interceptor를 간략하게 설명하자면
Interceptor란 컨트롤러에 들어오는 요청 HttpRequest와 컨트롤러가 응답하는 HttpResponse를 가로채는 역할을 합니다.
...라고 설명되어 있다.
처음에는 Interceptor를 사용하려 했으나
클라이언트가 생각보다 로그를 자세하게 남기기를 원해서
컨트롤러에만 적용할 수 있는 Interceptor 대신 더 세밀하게 작업할 수 있는 AOP를 활용하기로 하였다.
implementation 'org.springframework.boot:spring-boot-starter-aop'
build.gradle에 이거 하나 추가해주면 된다.
@EnableAspectJAutoProxy //aop
@SpringBootApplication
public class XXApplication {
public static void main(String[] args) {
SpringApplication.run(XXApplication.class, args);
}
}
AOP사용을 위해 @EnableAspectJAutoProxy를 Application.java에 부착해준다.
Pointcut를 간략히 설명하자면 AOP에서 Advice를 타깃의 코드 중 어디에 적용할지를 뜻하는 AOP 용어이다.
기본적으로 아래와 같이 사용이 된다.
@Around("execution(* com.example.domain.AService(..))")
public void exampleAdvice(ProceedingJoinPoint joinPoint) {
...
}
그러나 Pointcut가 생각보다 많은 곳에 적용을 해야 되어 도메인별로 포인트컷을 모아놓고 사용하기로 결정하였다.
public class APointcuts {
@Pointcut("execution(* com.example.domain.AService.createA(..))")
public void createA() {}
@Pointcut("execution(* com.example.domain.AService.deleteAById(..))")
public void deleteA() {}
@Pointcut("execution(* com.example.domain.AService.updateA(..))")
public void updateA() {}
}
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class AServiceLogAspectj {
private final LogService logService;
@AfterReturning(value = "com.example.global.log.aop.pointcut.APointcuts.createA()", returning = "returnObj")
public void createA(JoinPoint joinPoint, Object returnObj) {
Long a_Id = (Long) returnObj;
String message = "A 저장됨, id : " + a_id;
LogModel logModel = LogModel.builder()
.message(message)
.build();
logService.save(logModel);
}
@AfterReturning(value = "com.example.global.log.aop.pointcut.APointcuts.deleteA()", returning = "returnObj")
public void deleteA(JoinPoint joinPoint, Object returnObj) {
// AService.updateA()의 파라미터 추출
Object[] args = joinPoint.getArgs();
Long a_id = (Long) args[0];
String message = "A 삭제됨, id : " + a_id;
LogModel logModel = LogModel.builder()
.message(message)
.build();
logService.save(logModel);
}
@AfterReturning(value = "com.example.global.log.aop.pointcut.APointcuts.updateA()", returning = "returnObj")
public void updateA(JoinPoint joinPoint, Object returnObj) {
Object[] args = joinPoint.getArgs();
Long a_id = (Long) args[0];
ARequestDto a = (ARequestDto) args[1];
String message = "A 수정됨, " + "name : " + a.getName + ", title : " + a.getTitle() + ", id : " + a_id;
LogModel logModel = LogModel.builder()
.message(message)
.build();
logService.save(logModel);
}
}