[TIL 23] AOP

nini·2025년 6월 19일

KB IT's Your Life

목록 보기
32/40

1. AOP란?

AOP(Aspect-Oriented Programming, 관점 지향 프로그래밍)는 공통적으로 사용되는 관심사(Concern)를 핵심 비즈니스 로직과 분리하여 모듈화하는 프로그래밍 기법입니다.

목적

  • 핵심 로직을 깨끗하게 유지 (예: 서비스 메서드에 로깅, 보안, 트랜잭션 코드가 섞이지 않음)
  • 모든 메서드마다 반복되는 로직(예: 로그 출력, 권한 검사)을 한 곳에서 관리할 수 있음

예시 관심사

  • 파라미터 유효성 검사
  • 사용자 권한 체크
  • 로깅, 예외 처리, 트랜잭션 처리


2. AOP 주요 용어 정리

용어설명
Target핵심 로직을 포함한 객체 (예: SampleServiceImpl)
JoinPointAOP가 적용 가능한 지점 (예: 메서드 실행 전/후)
Advice삽입되는 공통 관심사 로직 (예: 로그 출력)
Pointcut어떤 JoinPoint에 Advice를 적용할지 지정
AspectPointcut + Advice 묶음 (공통 기능 단위)
Proxy클라이언트 대신 호출되는 래퍼 객체
WeavingAdvice를 실제 코드에 적용하는 과정


3. Advice의 종류 및 어노테이션

어노테이션설명
@BeforeJoinPoint 실행 전에 Advice 실행
@AfterReturning정상 종료된 경우 Advice 실행
@AfterThrowing예외 발생 시 Advice 실행
@After정상/예외 관계 없이 항상 실행
@Around메서드 실행 전/후 모두 제어 가능 (가장 강력함)

💡 예제

@Before("execution(* com.example.service.*.*(..))")
public void logBefore() {
    log.info("메서드 실행 전 로깅");
}


4. Pointcut 표현식 예시

표현식의미
execution(...) 메서드 시그니처 기반
within(...) 클래스 기준
args(...)전달되는 파라미터 기준
@annotation 특정 어노테이션 기준
this주어진 인터페이스를 구현한 객체를 대상으로 Pointcut 지정

💡 예제(Advice + Pointcut)

@Before("execution(* org.scoula.sample.service.SampleService*.*(..))")
public void logBefore() {
    ...
}


5. 실습 코드 구성 흐름

① 핵심 로직 (Target)

public class SampleServiceImpl implements SampleService {
    public Integer doAdd(String str1, String str2) throws Exception {
        return Integer.parseInt(str1) + Integer.parseInt(str2);
    }
}

② 공통 로직 (Advice)

@Aspect
@Component
@Log4j2
public class LogAdvice {

    @Before("execution(* org.scoula.sample.service.SampleService*.*(..))")
    public void logBefore() {
        log.info("====== 메서드 실행 전 ======");
    }

    @AfterThrowing(pointcut = "execution(* org.scoula.sample.service.SampleService*.*(..))", throwing = "exception")
    public void logException(Exception exception) {
        log.info("예외 발생!! " + exception);
    }

    @Around("execution(* org.scoula.sample.service.SampleService*.*(..))")
    public Object logTime(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = pjp.proceed();
        long end = System.currentTimeMillis();
        log.info("소요 시간: " + (end - start));
        return result;
    }
}

③ 설정 클래스

@Configuration
@ComponentScan(basePackages = {
    "org.scoula.advice",
    "org.scoula.sample.service"
})
@EnableAspectJAutoProxy
public class RootConfig { }

④ 테스트 클래스

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = { RootConfig.class })
@Log4j2
class SampleServiceTest {
    @Autowired
    private SampleService service;

    @Test
    public void doAdd() throws Exception {
        log.info(service.doAdd("123", "456"));
    }

    @Test
    public void addError() throws Exception {
        log.info(service.doAdd("123", "ABC"));
    }
}

💡 AOP 작동 흐름 간단 요약

Client → Proxy 객체 → Advice 실행 → Target 실행 → 결과 반환



6. args()를 이용한 파라미터 추적

@Before("execution(* org.scoula.sample.service.SampleService*.doAdd(String, String)) && args(str1, str2)")
public void logBeforeWithParam(String str1, String str2) {
    log.info("str1: " + str1);
    log.info("str2: " + str2);
}

📌 args()를 활용하면 메서드 인자를 Advice 메서드의 파라미터로 받을 수 있음



7. 실제 적용 예시

상황Advice 종류Pointcut 예시
모든 서비스 메서드 호출 전 로깅@Beforeexecution(* ..service..*.*(..))
예외 발생 시 예외 내용 기록@AfterThrowingexecution(* ..*.*(..))
메서드 실행 시간 측정@Aroundexecution(* ..*.*(..))
특정 메서드에만 적용@Before@annotation(MyLog)


✅ 마무리

  • 공통 기능(관심사)를 한 곳에 모아 코드 중복 없이 관리할 수 있음

  • 핵심 로직은 깔끔하게 유지, 공통 기능은 자동 삽입

  • Proxy 객체가 실제 객체 대신 동작하며 Advice를 실행

  • @Aspect, @Before, @Around, @AfterThrowing 등으로 유연하게 제어 가능

  • execution, args, @annotation 등으로 Pointcut을 정밀하게 지정 가능

profile
사용자를 고려한 디자인과 UX에 관심있는 개발자

0개의 댓글