본문 바로가기
  • GDG on campus Ewha Tech Blog
3-2기 스터디/Spring 입문

[4주차] AOP

by 하동녹초오레오 2022. 5. 4.

1. AOP가 필요한 상황

 

🌱 모든 메소드의 호출 시간을 측정하고 싶다면?

  • 메소드 시작과 끝에 메소드 호출시간 코드 집어넣기 (시간 측정 로직을 모든 메소드에 다 집어넣기)
public Long join(Member member) 
{
	long start = System.currentTimeMillis(); 

	try 
	{
		//핵심 비지니스 로직
	} 
	finally 
	{
			long finish = System.currentTimeMillis(); long timeMs = finish - start;
      System.out.println("join " + timeMs + "ms");
	} 
}
  • try - finally 구문 사용
    • try 내부 : 핵심 비지니스 로직
    • finally : 시간 측정 로직 (핵심 비지니스 로직이 끝날때 시간을 찍어야함, finally는 항상 들어오니까)
  • 모든 메소드에 해당 로직 추가하기

 

🌱 문제점

  • 시간을 측정하는 기능(finally 내부)은 핵심 로직이 아님
  • 시간을 측정하는 로직은 공통 기능==공통 관심사항임
  • 시간을 측정하는 로직과 핵심 비즈니스 로직이 섞여있어서 유지 보수가 어렵다.
  • 시간을 측정하는 로직을 뽑아내서 별도의 공통 로직으로 만들기 매우 어렵다. (메소드로 뽑아내기 어려움)
  • +) 특히 시작부분, 끝 부분에 로직이 들어가있기 때문에..!!
  • 시간을 측정하는 로직을 변경할 때 모든 로직을 찾아가면서 변경해야한다. ex) 시간측정 단위를 변경하고 싶을 때 +) 특히 시작부분, 끝 부분에 로직이 들어가있기 때문에..!!

 

2. AOP 적용

 

🌱 AOP란?

  • Aspect Oriented Programming
  • 공통 관심사항과 핵심 관심 사항을 분리하는 것
  • 시간 측정 로직을 한군데 모으고 내가 원하는 곳에 공통 관심 사항 적용함
package hello.hellospring.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component 
@Aspect
public class TimeTraceAop {

	@Around("execution(* hello.hellospring..*(..))")
	public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
		
		long start = System.currentTimeMillis();
    
		System.out.println("START: " + joinPoint.toString());
		
		try 
		{
			return joinPoint.proceed(); 
		} 
		finally 
		{
				long finish = System.currentTimeMillis(); 
				long timeMs = finish - start;
		            
				System.out.println("END: " + joinPoint.toString()+ " " + timeMs + "ms");
		} 
	}
}
  • 패키지를 AOP라고 하나 만들고 그 안에 TimeTraceAop 클래스를 만듦
  • @Component 사용 대신 스프링빈에 직접 등록해주는게 좋음
  • @Aspect => AOP로 적용하기 위해 적어줘야함
  • @Around 활용해서 ⇒ 공통관심사항을 어디에다 적용할건지 타겟팅해줌
  • 시간을 측정해서 ⇒ 어디서 병목현상(트래픽 밀림)이 있는지 찾을 수 있음

 

🌱 해결

  • 핵심 관심사항과 공통 관심사항을 분리했음
  • 시간을 측정하는 로직을 별도의 공통로직으로 만들었음
  • 핵심 관심 사항을 깔끔하게 유지할 수 있다.
  • 변경이 필요하면 이 로직만 변경하면 됨
  • 원하는 적용대상을 선택할 수 있음 (@Around)

 

🌱 AOP 적용 전 의존 관계

  • 컨트롤러에서 서비스 호출할 때 그냥 호출
  • Controller가 Service에 의존하고 있음 => Controller에서 메소드 호출 -> Service 메소드 호출

 

🌱 AOP 적용 후

AOP 적용 후

  • 가짜 멤버 서비스를 만들어냄 == 프록시라고 함
  • 스프링 컨테이너 동작 : 컨테이너에 스프링 빈 등록할 때 가짜 스프링빈을 앞에 세워둠
  • 가짜 스프링빈이 끝나면 그때 진짜 memberService 호출
  • helloController가 호출하는 건 진짜 멤버 서비스가 아니라 가짜 멤버 서비스
  • 이때 사용되는 기술 프록시 기술 ⇒ 스프링 핵심 강의때 ..

+) 프록시 확인해보기

  • 멤버 컨트롤러에서 멤버 서비스가 인젝션될 때 => memberService.getClass() 찍어보기
  • 멤버 서비스를 가지고 복제를 해서 코드를 조작하는 기술 (== 프록시)

댓글