πŸ“š[Spring] AOP

텁텁·2025λ…„ 6μ›” 12일

AOP λ₯Ό ν™œμš©ν•œ λ‘œκΉ…κ΅¬ν˜„

μš”κ΅¬μ‚¬ν•­

  • μ–΄λ“œλ―Ό API λ©”μ„œλ“œ μ‹€ν–‰ 전후에 μš”μ²­/응닡 데이터λ₯Ό λ‘œκΉ…ν•©λ‹ˆλ‹€.
  • λ‘œκΉ… λ‚΄μš©μ—λŠ” λ‹€μŒμ΄ ν¬ν•¨λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€:
    • μš”μ²­ν•œ μ‚¬μš©μžμ˜ ID
    • API μš”μ²­ μ‹œκ°
    • API μš”μ²­ URL
    • μš”μ²­ λ³Έλ¬Έ(RequestBody)
    • 응닡 λ³Έλ¬Έ(ResponseBody)

ν•΅μ‹¬κ°œλ…

  1. @Aspect : AOP 클래슀λ₯Ό μ •μ˜ν•˜λŠ” μ–΄λ…Έν…Œμ΄μ…˜μœΌλ‘œ 곡톡 관심사λ₯Ό λ‹΄λŠ” 역할을 ν•œλ‹€.
  2. @Pointcut : 관심 λŒ€μƒ λ©”μ„œλ“œλ₯Ό μ§€μ •ν•˜λŠ” μ–΄λ…Έν…Œμ΄μ…˜
@Pointcut("execution(* μœ„μΉ˜ ν‘œν˜„μ‹)")
	public void startPoint() {}

λ³΅μž‘ν•œ μœ„μΉ˜ ν‘œν˜„μ‹μ„ @Pointcut 을 μ‚¬μš©ν•˜μ—¬ λ©”μ„œλ“œλ‘œ 좔상화해 이름을 뢙일 수 μžˆλ‹€.
μ΄λ ‡κ²Œ μ •μ˜λœ λ©”μ„œλ“œλ“€μ€ Advice μ—μ„œ "startPoint()" 처럼 μž¬μ‚¬μš©ν•˜μ—¬ μ‰½κ²Œ μ μš©ν•  수 μžˆλ‹€.

  1. Advice : ꡬ체적인 μ‹€ν–‰λ‘œμ§μ— λŒ€ν•΄ μ •μ˜ν•˜λ©° μ•„λž˜ μ–΄λ…Έν…Œμ΄μ…˜μ— 따라 μ‹€ν–‰ μ‹œμ μ„ μ •ν•œλ‹€.
    • @Before : λ©”μ„œλ“œ μ‹€ν–‰ μ „ λ™μž‘
    • @After : λ©”μ„œλ“œ μ‹€ν–‰ ν›„ λ™μž‘
    • @Around : λ©”μ„œλ“œ μ‹€ν–‰ 전후에 λ™μž‘ν•˜λ©° 싀행을 직접 μ œμ–΄ν•  수 μžˆλ‹€
    • @AfterReturning : λ©”μ„œλ“œκ°€ μ„±κ³΅μ μœΌλ‘œ λ°˜ν™˜λœ ν›„ λ™μž‘
    • @AfterThrowing : λ©”μ„œλ“œμ—μ„œ μ˜ˆμ™Έκ°€ λ°œμƒν–ˆμ„ λ•Œ λ™μž‘

κ΅¬ν˜„μ˜ˆμ œ - LoggingAspect

@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class LoggingAspect {
	private final ObjectMapper objectMapper;

	@Pointcut("execution(* org.example.expert.domain.user.controller.UserAdminController.*(..)) ||"
		+ " execution(* org.example.expert.domain.comment.controller.CommentAdminController.*(..))")
	public void adminMethod() {}

	@Around("adminMethod()")
	public Object beforeLogging(ProceedingJoinPoint joinPoint) throws Throwable {
		//μŠ€λ ˆλ“œ μ•ˆμ—μ„œ request 객체 κ°€μ Έμ˜€κΈ°
		HttpServletRequest request = ((ServletRequestAttributes)Objects.requireNonNull(
			RequestContextHolder.getRequestAttributes())).getRequest();

		//싀행쀑인 λ©”μ„œλ“œ 정보
		Method joinMethod = ((MethodSignature)joinPoint.getSignature()).getMethod();

		//λ©”μ„œλ“œ νŒŒλΌλ―Έν„° 정보 및 μ‹€μ œ κ°’
		Parameter[] params = joinMethod.getParameters();
		Object[] args = joinPoint.getArgs();

		//μš”μ²­, 응닡 바디값을 담을 λ¬Έμžμ—΄ null -> 값이 μ—†κ±°λ‚˜ 직렬화 μ‹€νŒ¨
		String requestBody = null;
		String responseBody = null;

		// μš”μ²­ λ³Έλ¬Έ 직렬화 μ‹œλ„
		try {
			for (int i = 0; i < args.length; i++) {
				if (params[i].isAnnotationPresent(RequestBody.class)) {
					requestBody = objectMapper.writeValueAsString(args[i]);
				}
			}
		} catch (JsonProcessingException e) {
			log.warn("requestBody 직렬화 μ‹€νŒ¨ : {}", e.getMessage());
		}

		String url = request.getRequestURI();

		log.info("[Method Before] path={}, timestamp={}, requestBody={} ", url, LocalDateTime.now(), requestBody);

		//μ‹€μ œ λ©”μ„œλ“œ μ‹€ν–‰ 및 κ²°κ³Ό μ €μž₯
		Object result = joinPoint.proceed();

		// 응닡 λ³Έλ¬Έ 직렬화 μ‹œλ„
		try {
			responseBody = objectMapper.writeValueAsString(result);
		} catch (JsonProcessingException e) {
			log.warn("responseBody 직렬화 μ‹€νŒ¨ : {}", e.getMessage());
		}

		log.info("[Method After] path={}, timestamp={}, responseBody={} ", url, LocalDateTime.now(), responseBody);

		return result;
	}

}

μ–΄λ“œλ―Ό μš”μ²­μ΄ ν•„μš”ν•œ λ©”μ„œλ“œλ“€μ— λŒ€ν•΄ 곡톡 λ‘œκΉ… λ‘œμ§μ„ μˆ˜ν–‰ν•˜λŠ” Aspect 이닀.
@Around μ–΄λ“œλ°”μ΄μŠ€λ₯Ό ν™œμš©ν•΄ λ©”μ„œλ“œ μ‹€ν–‰ μ „ν›„μ˜ μš”μ²­κ³Ό 응닡 데이터λ₯Ό ν•œλ²ˆμ— μ²˜λ¦¬ν•  수 μžˆλ„λ‘ κ΅¬μ„±ν–ˆλ‹€.
λ©”μ„œλ“œ μ‹€ν–‰μ „ RequestBody μ–΄λ…Έν…Œμ΄μ…˜μ΄ 뢙은 νŒŒλΌλ―Έν„°λ₯Ό μ°Ύμ•„ μš”μ²­ 본문을 μΆ”μΆœν•˜κ³  μ‹€ν–‰ ν›„ λ°˜ν™˜κ°’λ„ 직렬화해 응닡 본문으둜 λ‘œκ·Έμ— 남긴닀.

profile
μ°¨κ·Όμ°¨κ·Ό

0개의 λŒ“κΈ€