WINK-(Web & App)/Spring Boot 스터디
[2025 1학기 스프링부트 스터디] 고윤정 #7주차
윤정 님의 블로그
2025. 5. 24. 23:46
반응형
안녕하세요
7주차 스터디 시작합니당
섹션 8 - AOP
AOP가 필요한 상황

MemberService 파일에 아래의 코드를 작성해줍니다
/**
* 회원가입
*/
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");
}
}
/**
* 전체 회원 조회
*/
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");
}
}
이 코드에서 선생님이 언급하신 핵심 관심 사항과 공통 관심 사항을 정리해볼게용
★ 핵심 관심 사항 (Core Concern)
이 클래스의 본래 목적에 해당하는 핵심 로직
- 회원 가입(join):
- validateDuplicateMember(member) -> 중복 회원 검증
- memberRepository.save(member) -> 회원 저장
- 회원 조회(findMembers):
- memberRepository.findAll() -> 저장된 회원 전체 조회
★ 공통 관심 사항 (Cross Cutting Concern)
비즈니스 로직과는 직접적인 관련은 없지만, 여러 로직에서 공통적으로 수행되는 작업
- 메서드 실행 시간 측정 (로그 출력 포함)
- long start = System.currentTimeMillis();
- long finish = System.currentTimeMillis();
- System.out.println("join/findMembers " + timeMs + "ms");
-> 이는 비즈니스 로직과 별개로, 성능 측정이나 로그 기록을 위한 코드입니다.
여기서 문제점!!!
1. 회원가입, 회원조회에서 시간을 측정하는 기능은 핵심 관심 사항이 아니다.
2. 시간을 측정하는 로직은 공통 관심 사항이다.
3. 시간을 측정하는 로직과 핵심 비즈니스의 로직이 섞여서 유지보수가 어렵다.
4. 시간을 측정하는 로직을 별도의 공통 로직으로 만들기 매우 어렵다.
5. 시간을 측정하는 로직을 변경할 때 모든 로직을 찾아가면서 변경해야한다.
그래서 이제
AOP를 적용하여
이 문제점들을 해결해보도록 하겠습니다!!
AOP 적용

★ AOP (Aspect Oriented Programming)
AOP는 관점 지향 프로그래밍이라고도 하는데, 공통 관심 사항과 핵심 관심 사항을 분리합니다!
시간 측정 로직을 한곳에 다 모으고 원하는 곳에 적용하는 것을 가능하게 한다고 합니다.
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");
}
}
}
선생님이 설명하신 부분을 중심으로 중요한 부분들을 짚고 넘어가겠습니당
- @Aspect : 이 클래스가 AOP 역할을 한다는 표시
- @Component : 이 클래스를 스프링이 자동으로 빈으로 등록하게 함
- @Around("execution( hello.hellospring..(..))")** : hello.hellospring 패키지와 그 하위 모든 메서드를 대상으로 전후 실행 로직을 적용하겠다는 의미
- ProceedingJoinPoint joinPoint : 실제 대상 메서드에 접근할 수 있게 해주는 객체로, joinPoint.proceed()를 호출해야 원래 메서드가 실행됨

적용 전에는
컨트롤러가 서비스를 직접 호출합니다

하지만 적용 후에는
프록시라는 기술로 발생하는 가짜 MemberService를 호출하게 된다고 합니다


정리하자면 AOP를 사용하여 코드 중복 없이 여러 계층에 필요한 공통 기능을 쉽게 삽입하고 관리할 수 있습니다!

반응형