[Spring] AOP를 왜 사용할까?

Spring에서 AOP를 사용하는 이유와 사용 방법을 코드 예시를 통해 알아보자.
Hi's avatar
Apr 16, 2024
[Spring] AOP를 왜 사용할까?
 
 

✅ AOP란?

📋
Spring에서 AOPAspect-Oriented Programming(관점 지향 프로그래밍)의 약자이다. 이는 애플리케이션에서 핵심 비즈니스 로직과는 별개로 발생하는 부가적인 관심사(로깅, 트랜잭션 관리, 보안 등)를 모듈화하기 위한 프로그래밍 패러다임이다.
 
Spring AOP는 핵심 비즈니스 로직을 변경하지 않고도 이러한 부가적인 관심사를 적용할 수 있도록 도와준다. 이를 통해 코드의 재사용성과 유지보수성을 향상시키며, 각 모듈의 역할을 명확히 분리할 수 있다. Spring AOP는 프록시를 사용하여 부가적인 동작을 핵심 로직 주변에 삽입한다.
 
아직 AOP가 뭔지 감이 잘 잡히지 않았을 것이다..! 예시를 통해 더 알아보자.
 
예를 들어, 메서드 실행 전에 로그를 출력하거나, 트랜잭션을 시작하거나, 보안 검사를 수행하는 등의 작업을 Spring AOP를 사용하여 구현할 수 있다. 이는 각 관심사를 별도의 모듈로 분리하여 코드를 깔끔하게 유지하면서도 이러한 부가적인 기능을 쉽게 추가하고 변경할 수 있도록 한다.
 
만약 사용자의 ID를 통해 사용자 정보를 가져오는 UserService 클래스의 getUserById라는 간단한 핵심 비즈니스 로직이 있다고 하자. 이때, 이 함수가 실행되기 전에 로그를 남기고 싶다! 이때 로그를 남기는 것은 부가로직이다. (로그를 남기는 것은 핵심 로직을 실행하는 데 딱히 필요 없는 기능이다.)
 
원래라면 getUserById에 로그를 남기는 코드를 적어야 한다. 그래야 메소드가 실행될 때마다 로그를 남길 수 있으니까 !! 하지만 AOP를 사용하면 코드를 분리하여 따로 적을 수 있다. 따로 적은(모듈화한) 코드(로그를 남기는 코드)는 getUserById 메소드 외에 또 다른 setUser, deleteUserById, … 등 여러 메소드에 적용할 수 있다.
 

✅ 사용 방법 (코드 예시)

코드 예시도 살펴보자!
 
getUserById 메소드가 있는 UserService 서비스 인터페이스가 있다고 가정해 보자.
public interface UserService { User getUserById(long id); }
 
그리고 그 구현 UserServiceImpl은 다음과 같다.
@Service public class UserServiceImpl implements UserService { @Override public User getUserById(long id) { // Simulated database call System.out.println("Fetching user with id: " + id); return userRepository.findById(id); } }
 
이제 메서드 실행을 기록하는 Aspect를 만들어 보자!
@Aspect @Component public class LoggingAspect { private static final Logger LOGGER = LoggerFactory.getLogger(LoggingAspect.class); /* 대충, 메소드 실행 전에 로그를 남기는 코드 */ @Before("execution(* com.example.UserService.*(..))") public void logBefore(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); LOGGER.info("Executing method: " + methodName); } }
 
  • @Aspect 주석은 이 클래스를 Aspect로 만들어준다.
  • @Before 주석은 포인트컷 표현식과 일치하는 메소드를 실행하기 전에 logBefore 어드바이스를 실행해야 함을 나타낸다.
  • execution(* com.example.UserService.*(..))com.example.UserService 인터페이스의 모든 메소드를 나타내는 포인트컷 표현식이다(이때, com.example을 실제 패키지명으로 대체해야 함).
 
이제 UserServicegetUserById 메소드를 호출하면 해당 Aspect가 호출을 가로채고 메소드가 실행되기 전에 메시지를 기록한다.
public class Main { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); UserService userService = context.getBean(UserService.class); userService.getUserById(123); } }
 
출력 결과:
Executing method: getUserById Fetching user with id: 123
 
이렇게 하면 getUserById 메소드 구현을 수정하지 않고 애플리케이션에 로깅 기능이 추가되어 문제를 분리하고 코드를 깔끔하게 유지할 수 있다!
 
 
Share article

soultree