[25강]
              
          2023. 2. 6. 14:56ㆍSpring 강의/section7
AOP 적용
- AOP : Aspect Oriented Programming - 관점 지향 프로그래밍
- 공통 관심 사항(cross-cutting concern) vs 핵심 관심 사항(core concern) 분리

이전처럼 모든 메소드마다 시간 측정 로직을 붙이는 게 아니라

원하는 메소드에 시간 측정 로직을 적용시킬 수 있다.
먼저 hello - hellospring2 밑에 aop란 패키지를 만들고 그 안에 TimeTraceAop 클래스를 만든다.

TimeTraceAop.class
| 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 | package hello.hellospring2.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Aspect //붙여줘야 AOP로 쓸 수 있다. @Component public class TimeTraceAop {     @Around("execution(* hello.hellospring2..*(..))")     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");         }     } } | cs | 
@Aspect : 붙어줘야 AOP로 쓸 수 있다.
@Component : 빈으로 등록시켜준다.
@Around : 해당 로직을 어떤 클래스에 사용할 것인지 정할 수 있다.
@Around("execution(* hello.hellospring2..*(..))") : hello.hellospring2 밑에 있는 모든 클래스에 적용시키겠다.
이제 MemberService 클래스는 핵심 로직만으로 깔끔하게 유지할 수 있다.
| 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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | package hello.hellospring2.service; import hello.hellospring2.domain.Member; import hello.hellospring2.repository.MemberRepository; import hello.hellospring2.repository.MemoryMemberRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Optional; @Transactional //jpa를 쓰려면 항상 있어야 한다//data를 저장하고 변경할 때 필요 public class MemberService {     private final MemberRepository memberRepository;     public MemberService(MemberRepository memberRepository) {         this.memberRepository = memberRepository;     }     /* 회원 가입 */     public Long join (Member member){             validateDuplicateMember(member); //중복회원 검증             memberRepository.save(member);             return member.getId();     }     private void validateDuplicateMember(Member member) {         memberRepository.findByName(member.getName())                 .ifPresent(m -> {                     throw new IllegalStateException("이미 존재하는 회원입니다.");                 });     }     /*전체 회원 조회 */     public List<Member> findMembers(){             return memberRepository.findAll();     };     public Optional<Member> findOne(Long memberId){         return memberRepository.findById(memberId);     } } | cs | 
실행 결과

처음 서버를 실행했을 때
회원을 등록할 때
회원을 조회할 때
모든 시간을 알 수 있다.
해결
- 회원가입, 회원 조회 등 핵심 관심사항과 시간을 측정하는 공통 관심 사항을 분리한다.
- 시간을 측정하는 로직을 별도의 공통 로직으로 만들었다.
- 핵심 관심 사항을 깔끔하게 유지할 수 있다.
- 변경이 필요하다면 이 로직만 변경하면 된다.
- 원하는 적용 대상을 선택할 수 있다.
스프링의 AOP 동작 방식 설명
AOP 적용 전 의존관계

바로 memberController에서 memberService를 호출한다.
AOP 적용후 의존관계

aop가 있으면 가짜 memberSerivce를 만들어낸다.
스프링 컨테이너에 스프링 빈을 등록할 때 가짜 스프링 빈을 앞에 세워둔다.
그리고 가짜 스프링 빈이 joinPoint.proceed()하면 그때 진짜 memberSerivce를 호출한다.
memberController가 호출하는 것은 프록시라는 기술로 발생하는 가짜 memberService이다.
AOP 적용 전 전체 그림

AOP 적용 후 전체 그림

컨테이너에서 스프링 빈을 관리하면 가짜를 만들어서 가짜를 진짜에 DI해주면 된다.
이게 바로 DI의 장점!