Taeyoung Kim

Engineering

7. AOP (Aspect Oriented Programming)

7. AOP (Aspect Oriented Programming) 학습 내용을 정리한 백필 노트입니다.

이 글은 2025년 학습 기록을 블로그 형식으로 정리한 백필 노트입니다.

1️⃣ AOP가 필요한 상황

모든 메소드의 호출 시간을 측정하고 싶을 때, 이를 각 서비스 로직마다 직접 넣는 것은 비효율적이다.

이는 공통 관심 사항(cross-cutting concern)핵심 관심 사항(core concern) 이 섞여 유지보수를 어렵게 만든다.

예시: 회원 가입(join), 회원 조회(findMembers) 시간 측정 기능 추가.


🔹 MemberService 코드 예시

package hello.hellospring.service;

@Transactional
public class 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");
        }
    }
}


2️⃣ 문제점 요약

  • 회원가입/조회 기능의 시간 측정은 핵심 로직이 아닌 공통 관심 사항이다.
  • 공통 로직과 핵심 비즈니스 로직이 섞여 코드가 복잡하고 유지보수가 어렵다.
  • 시간 측정 로직을 바꾸려면 모든 메소드에서 수정해야 한다.
  • 즉, 중복 코드와 결합도가 높음.

3️⃣ AOP 적용

AOP는 공통 관심 사항핵심 관심 사항을 분리하기 위한 프로그래밍 기법이다.

✅ 개념

  • AOP (Aspect Oriented Programming)

    : 관점 지향 프로그래밍.

    공통 로직(시간 측정, 로깅, 트랜잭션 등)을 분리해 모듈화함.

  • cross-cutting concern → 여러 곳에서 공통으로 필요한 기능

  • core concern → 실제 비즈니스 로직


🔹 시간 측정 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");
        }
    }
}


4️⃣ AOP 적용 후 결과 및 장점

✅ 해결된 점

  • 회원가입, 회원조회 등 핵심 로직과 시간 측정 공통 로직 분리
  • 시간 측정 코드 한 곳에서 관리 가능
  • 핵심 비즈니스 코드가 깔끔하고 집중도 높아짐
  • 변경 시 AOP 클래스만 수정하면 됨
  • 적용 대상(@Around의 execution 표현식)을 유연하게 지정 가능

5️⃣ 스프링 AOP 동작 방식

  • AOP 적용 전

    : Controller → Service → Repository 구조로 직접 호출

    (모든 객체가 실제 인스턴스 직접 참조)

  • AOP 적용 후

    : 스프링이 Proxy 객체를 만들어 주입함

    (실제 객체 호출 전후에 공통 로직이 자동 실행됨)

즉, 스프링 컨테이너는 프록시 객체를 Bean으로 등록하고,

이 프록시가 내부에서 실제 객체를 호출하며 AOP 로직을 삽입한다.

👉 확인 방법

콘솔에 START, END 로그를 출력하여

프록시 객체가 주입되어 실행되는지 확인 가능.


🧩 정리 요약

| 구분 | 내용 | | --- | --- | | 문제 | 모든 메소드에 중복되는 시간 측정 코드 삽입 필요 | | 원인 | 공통 관심 사항과 핵심 로직이 섞임 | | 해결책 | AOP 적용으로 공통 로직 분리 | | 핵심 기술 | @Aspect, @Around, ProceedingJoinPoint, Proxy 객체 | | 효과 | 코드 간결, 유지보수성 향상, 확장 용이 |