일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
Tags
- Spring Security
- WebConfigurerAdapter
- 스프링시큐리티
- HttpSecurity
- WebSecurity
- ㅂ
- ㅇㅇㅇㄴㅇ
- 스프링 #스프링 시큐리티 #spring security
- securityconfig
- Session1이 그 모든 클라이언트의 저올
Archives
- Today
- Total
다오의 개발일지
TIL-27 AOP란 무엇인가 본문
AOP란
Aspect Oriented Programming의 약자로 관점 지향 프로그래밍이라고 불린다. 관점 지향은 쉽게 말해 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화하겠다는 것이다. 여기서 모듈화란 어떤 공통된 로직이나 기능을 하나의 단위로 묶는 것을 말한다.

@Aspect
Spring 빈(Bean) 클래스에만 적용 가능
- 어드바이스 종류
- @Around: '핵심기능' 수행 전과 후 (@Before + @After)
- @Before: '핵심기능' 호출 전 (ex. Client 의 입력값 Validation 수행)
- @After: '핵심기능' 수행 성공/실패 여부와 상관없이 언제나 동작 (try, catch 의 finally() 처럼 동작)
- @AfterReturning: '핵심기능' 호출 성공 시 (함수의 Return 값 사용 가능)
- @AfterThrowing: '핵심기능' 호출 실패 시. 즉, 예외 (Exception) 가 발생한 경우만 동작 (ex. 예외가 발생했을 때 개발자에게 email 이나 SMS 보냄)
- 포인트컷
- 포인트컷 Expression Language
- 포인트컷 Expression 형태
- 포인트컷 Expression Language
execution(modifiers-pattern? return-type-pattern declaring-type-pattern?
method-name-pattern(param-pattern) throws-pattern?)
- ? 는 생략 가능
- 포인트컷 Expression 예제
@Around("execution(public * com.sparta.myselectshop.controller..*(..))")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable { ... }
- modifiers-pattern
- public, private, *
- return-type-pattern
- void, String, List<String>, *****
- declaring-type-pattern
- 클래스명 (패키지명 필요)
- com.sparta.myselectshop.controller.* - controller 패키지의 모든 클래스에 적용
- com.sparta.myselectshop.controller.. - controller 패키지 및 하위 패키지의 모든 클래스에 적용
- method-name-pattern(param-pattern)
- 함수명
- addFolders : addFolders() 함수에만 적용
- add* : add 로 시작하는 모든 함수에 적용
- 파라미터 패턴 (param-pattern)
- (com.sparta.myselectshop.dto.FolderRequestDto) - FolderRequestDto 인수 (arguments) 만 적용
- () - 인수 없음
- (*) - 인수 1개 (타입 상관없음)
- (..) - 인수 0~N개 (타입 상관없음)
- 함수명
- @Pointcut
- 포인트컷 재사용 가능
- 포인트컷 결합 (combine) 가능
@Component
@Aspect
public class Aspect {
@Pointcut("execution(* com.sparta.myselectshop.controller.*.*(..))")
private void forAllController() {}
@Pointcut("execution(String com.sparta.myselectshop.controller.*.*())")
private void forAllViewController() {}
@Around("forAllContorller() && !forAllViewController()")
public void saveRestApiLog() {
...
}
@Around("forAllContorller()")
public void saveAllApiLog() {
...
}
}
강의 내용 중 myselectshop
ProductController 에 추가했던 부가기능 제거 (Rollback)
@PostMapping("/products")
public ProductResponseDto createProduct(@RequestBody ProductRequestDto requestDto, @AuthenticationPrincipal UserDetailsImpl userDetails) {
return productService.createProduct(requestDto, userDetails.getUser());
}
AOP 사용해 FolderController, ProductController, NaverApiController 에 부가기능 추가
package com.sparta.myselectshop.aop;
import com.sparta.myselectshop.entity.ApiUseTime;
import com.sparta.myselectshop.entity.User;
import com.sparta.myselectshop.repository.ApiUseTimeRepository;
import com.sparta.myselectshop.security.UserDetailsImpl;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
@Slf4j(topic = "UseTimeAop")
@Aspect
@Component
public class UseTimeAop {
private final ApiUseTimeRepository apiUseTimeRepository;
public UseTimeAop(ApiUseTimeRepository apiUseTimeRepository) {
this.apiUseTimeRepository = apiUseTimeRepository;
}
@Pointcut("execution(* com.sparta.myselectshop.controller.ProductController.*(..))")
private void product() {}
@Pointcut("execution(* com.sparta.myselectshop.controller.FolderController.*(..))")
private void folder() {}
@Pointcut("execution(* com.sparta.myselectshop.naver.controller.NaverApiController.*(..))")
private void naver() {}
@Around("product() || folder() || naver()")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
// 측정 시작 시간
long startTime = System.currentTimeMillis();
try {
// 핵심기능 수행
Object output = joinPoint.proceed();
return output;
} finally {
// 측정 종료 시간
long endTime = System.currentTimeMillis();
// 수행시간 = 종료 시간 - 시작 시간
long runTime = endTime - startTime;
// 로그인 회원이 없는 경우, 수행시간 기록하지 않음
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.getPrincipal().getClass() == UserDetailsImpl.class) {
// 로그인 회원 정보
UserDetailsImpl userDetails = (UserDetailsImpl) auth.getPrincipal();
User loginUser = userDetails.getUser();
// API 사용시간 및 DB 에 기록
ApiUseTime apiUseTime = apiUseTimeRepository.findByUser(loginUser).orElse(null);
if (apiUseTime == null) {
// 로그인 회원의 기록이 없으면
apiUseTime = new ApiUseTime(loginUser, runTime);
} else {
// 로그인 회원의 기록이 이미 있으면
apiUseTime.addUseTime(runTime);
}
log.info("[API Use Time] Username: " + loginUser.getUsername() + ", Total Time: " + apiUseTime.getTotalTime() + " ms");
apiUseTimeRepository.save(apiUseTime);
}
}
}
}
Spring AOP 동작 이해
- 개념적 이해

스프링 실제 동작

시퀀스 다이어그램 (Sequence Diagram)
AOP 적용 전

AOP 적용 후

- Spring이 프록시(가짜 혹은 대리) 객체를 중간에 삽입
- DispatcherServlet 과 ProductController 입장에서는 변화가 없다.
- 호출되는 함수의 input, output 이 완전 동일
- "joinPoint.proceed()" 에 의해서 원래 호출하려고 했던 함수, 인수(argument) 가 전달
- → createProduct(requestDto);
'WTIL' 카테고리의 다른 글
TIL-29 Redis 란 무엇인가 (0) | 2023.07.28 |
---|---|
TIL-28 Refresh Token, Access Token 의 차이점과 사용 이유 (0) | 2023.07.26 |
TIL-26 TenThread 프로젝트 도중 발생한 문제 (0) | 2023.07.19 |
TIL-25 Mockito (0) | 2023.07.17 |
TIL-24 테스트에 필요한 어노테이션 (0) | 2023.07.17 |