7.1 AOP 가 필요한 상황
예를 들어, 모든 메소드의 호출 시간을 측정해야 한다고 가정하자.
시간 측정을 위해서는 memberConrtoller, memberService, memberRepository 에 시간 측정 로직을 전부 작성하여야 할 것이다.
실제 MemberService 에 회원 조회 시간을 측정하는 코드를 작성해보자.
try { } finally { } 문을 통해, ( 종료시간 - 시작 시간 ) 으로 시간을 측정하도록 한다.
@Transactional
public class MemberService {
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
/**
* 회원가입
*/
public Long join(Member member) {
long start = System.currentTimeMillis();
try {
validateDuplicateMember(member); //중복 회원 검증
memberRepository.save(member);
return member.getId();
} finally {
long finish = System.currentTimeMillis();
long timeMs = finish = start;
System.out.println("join " + timeMs + "ms");
}
}
private void validateDuplicateMember(Member member) {
memberRepository.findByName(member.getName())
.ifPresent(m -> {
throw new IllegalStateException("이미 존재하는 회원입니다.");
});
}
public List<Member> findMembers() {
long start = System.currentTimeMillis();
try {
return memberRepository.findAll();
} finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
System.out.println("findMembers " + timeMs + "ms");
}
}
public Optional<Member> findOne(Long memberId) {
return memberRepository.findById(memberId);
}
}
회원가입 test code 를 실행해보면, 다음과 같이 메소드 호출시간이 출력되는 것을 확인할 수 있다.

하지만 위의 코드처럼 모든 메소드에 로직을 작성하면, 다음과 같은 문제가 발생한다.
1) 회원가입, 회원 조회에서 시간 측정은 핵심 로직이 아니다.
2) 시간 측정 로직은 '공통 관심 사항' 이다.
3) 핵심 로직과 공통 로직 코드가 섞여, 유지보수가 어려워진다.
4) 하지만, 시간 측정 로직을 공통 로직으로 작성하기 어렵다.
5) 모든 메소드에 로직을 작성해야 하므로, 비효율적이다.
따라서, 이러한 문제점을 해결하기 위해 AOP 기술을 사용된다.
7.2 AOP 란?
AOP 는 Aspect Oriented Programming 의 약자로,
관점 지향 프로그래밍이라고도 한다.
이는 '공통 관심 사항' 과 '핵심 관심 사항' 을 분리시키는 것이다.
예를 들어 앞선 예제에서,
공통 관심 사항인, 시간을 측정하는 로직을 별도의 'TimeTraceAop' 으로 작성한다.
memberConrtoller, memberService, memberRepository 등 원하는 곳에 적용하는 것이다.

따라서 AOP 를 사용하면, 다음과 같은 장점을 얻을 수 있다.
1) 변경이 필요할 경우 공통 로직만 변경하면 된다.
2) 공통 로직을 적용할 대상을 선택할 수 있다.
7.3 시간 측정 AOP 등록
공통 관심 사항인, 시간 측정 로직을 갖고 있는 TimeTraceAop 코드를 작성해보자.
먼저, src > main > java > hello.hellospring 에 aop 패키지를 생성한다.
aop 패키지에 TimeTraceAop 파일을 생성하고, 아래와 같이 코드를 작성한다.
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.printf("START: " + joinPoint.toString());
try {
return joinPoint.proceed();
} finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
System.out.println("END: " + joinPoint.toString() + " " + timeMs + "ms");
}
}
}
@Component
: 스프링 빈에 등록하기 위해 컴포넌트 스캔을 사용한다.
@Aspect
: AOP로 사용하기 위해 작성하는 어노테이션이다.
@Around("execution(* hello.hellospring..*(..))")
: @ Around 를 통해 공통 관심 사항을 타겟팅할 수 있다.
: hello.hellospring 하위 파일에 시간 측정 로직을 적용하도록 작성하였다.
(매뉴얼을 통해 다양한 문법을 확인할 수 있다.)
try { } finally { }
: 앞서 작성한 시간 측정 로직을 동일하게 작성한다.
회원가입 test 코드를 실행시켜보면,
다음과 같이 시간 측정 로직이 모두 적용된 것을 확인할 수 있다.

7.4 스프링의 AOP 동작 방식
AOP 를 적용하기 전에는, 단순히 Controller 가 Service 를 의존하고 호출하는 관계이다.
하지만 AOP 를 적용하면, 다음과 같은 순서로 동작한다.

1) 스프링 빈을 등록할 때, 프록시 (일종의 가짜 memberService) 를 연결한다.
2) 이후, joinPoint.proceed() 를 통해 실제 memberService 를 호출한다.
+ 강의를 마치며
Spring 에 대한 선행 지식 없이 강의를 수강하다보니, 조금은 두렵고 어려운 마음으로 공부를 시작했다.
하지만 점점 코드가 쌓여가면서, 내가 상상했던 로직이 실제로 구현되는 것을 보는 것은 너무나 재미있고 값진 경험이었다.
특히 H2 Database에 데이터를 저장하는 수업에서, 몇 시간 동안 오류를 해결하지 못하고 헤매다가, 마침내 성공적으로 실행됐을때의 쾌감은 아직도 잊을 수 없다ㅎㅎ
웹 MVC나 DB 접근 기술에서 아직까지는 제대로 이해하지 못한 모호한 개념들이 많은데, 지금까지 정리해온 블로그를 찬찬히 훑어보면서 조금 더 깊이있는 공부를 이어나가고자 한다.

김영한 '스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술' 강의를 기반으로 작성하였습니다.
'SPRING 입문 [ 코드로 배우는 스프링 부트 ]' 카테고리의 다른 글
[스프링 입문] 섹션 6.6 스프링 DB 접근 기술 (스프링 데이터 JPA) (0) | 2023.01.18 |
---|---|
[스프링 입문] 섹션 6.5 스프링 DB 접근 기술 (JPA) (1) | 2023.01.18 |
[스프링 입문] 섹션 6.4 스프링 DB 접근 기술 (스프링 통합 Jdbc Template) (0) | 2023.01.17 |
[스프링 입문] 섹션 6.3 스프링 DB 접근 기술 (스프링 통합 테스트) (0) | 2023.01.17 |
[스프링 입문] 섹션 6.2 스프링 DB 접근 기술 (순수 JDBC) (0) | 2023.01.17 |