수업 복습

XML 기반 AOP 설정 실습 흐름

_김영인 2026. 1. 23. 16:51

 

수업 실습의 핵심 흐름은 한 문장으로 끝난다.

 

“코드로 횡단 관심 (공통 로직)을 먼저 눈으로 확인 → AOP 설정 (XML)으로 자동 결합 (위빙)시키기”

 

그리고 계속 강조한 핵심은 이것이다.

 

AOP 설정은 2파트로 나뉜다.

    • 포인트컷 (Pointcut) 설정 파트
    • 애스펙트 (Aspect) 결합 파트

 

1) 왜 “기능 완성 후 AOP 분리”가 추천되는가

 

수업 Q&A의 실무형 결론은 이 흐름이었다.

  • 책은 “완벽한 설계 후 분리”를 말하지만 현실은 어렵다.
  • 실무에서는 먼저 비즈니스 로직을 다 만든 뒤, 반복 공통 로직을 AOP로 리팩터링 (분리 / 디벨롭) 하는 경우가 많다.

이 방식이 좋은 이유는 명확하다.

    • 문제가 터졌을 때 원인이 “로직”인지 “AOP 설정”인지 분리하기 쉬움
    • 처음부터 AOP를 얹으면 “코드에 없는데 동작하는 로직” 때문에 디버깅 난도가 급격히 올라감 (수업에서도 이 부분을 체감 포인트로 말함)

 

2) “핵심 로직이 어디서 실행되는지” 먼저 확인

 

이 실습은 웹이 아니라 콘솔 클라이언트 기반이다.


 BoardClient가 스프링 컨테이너를 올리고 (applicationContext.xml) 서비스 빈을 꺼내서 실행한다.

 
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
BoardService boardService = (BoardService)factory.getBean("bs");

 

이 흐름이 BoardClient에 그대로 들어 있다.

그리고 메뉴 입력으로 CRUD 호출이 발생한다. 예를 들어:

    • 전체출력 → getBoardList() 호출
    • 글 작성 → insertBoard() 호출

이 구조를 먼저 이해해야 “왜 AOP가 여기서 끼어들 수 있는지 (메서드 실행 시점)” 감이 잡힌다.

 

3) 횡단 관심사 (Advice) 클래스 만들기: “공통 로그”를 예제로

 

수업에서는 로깅을 공통 로직 (횡단 관심)의 대표 예시로 잡았다.

 

기본 로그 (LogAdvice)

public class LogAdvice {
	public void printLog() {
		System.out.println("작은티모님의 로그");
	}
}

 

수업 중에도 “공통 로그가 서비스에 잘 들어왔는지 확인하려고 찍는다”는 식으로 설명했다.

 

향상 로그 (PlusLogAdvice): “공통 로직 교체는 설정만 바꾸면 끝”

public class PlusLogAdvice {
	public void printLog() {
		System.out.println("향상된 작은티모님의 로그");
	}
}

 

이 포인트가 실무자들이 좋아하는 지점이다.

  • 서비스 코드 (핵심 로직)는 그대로 둔다
  • AOP 설정에서 ref만 바꿔 공통 정책을 교체한다

“공통 로직 변경이 전체 코드 수정으로 번지는 문제”를 막는다.

 

예외 전용 로그 (AfterThrowingAdvice)

public class AfterThrowingAdvice {
	public void printLog() {
		System.out.println("예외발생시 출력되는 로그");
	}
}

 

수업에서도 “예외가 나지 않으면 아무 일도 안 하고 예외가 터지면 그때만 실행된다”는 방식으로 실습을 진행했다.

 

조회 전 / 후 감싸기 (AroundAdvice)

public class AroundAdvice {
	public Object around(ProceedingJoinPoint pjp) throws Throwable {
		System.out.println("비즈니스 메서드 수행 전");
		Object returnObj = pjp.proceed();
		System.out.println("비즈니스 메서드 수행 후");
		return returnObj;
	}
}

 

pjp.proceed()가 핵심 메서드 “실행 지점” 이라서 Around는 “감싸서 제어하는” 타입이 된다.

 

4) 핵심 로직 (서비스)은 “AOP 모르게” 그대로 둔다

 

BoardServiceImpl은 AOP를 전혀 모른다.
그냥 CRUD만 한다.

 

그리고 실습에서 AfterThrowing을 확인하기 위해 일부러 예외를 만든다.

public boolean insertBoard(BoardDTO dto) {
	if(dto.getBid() == 0) { // == 항상
		throw new NullPointerException("런타임 에러");
	}
	return boardDAO.insertBoard(dto);
}
 

여기서 중요한 실무 포인트:

  • AOP는 “예외를 해결”하려고 쓰기보다는
  • 예외 상황을 공통 정책으로 관측 / 로그 / 알림하기 위한 목적으로 쓰는 경우가 많다.

 

5) 포인트컷 표현식: “메서드 시그니처 기반으로 선택한다”

 

수업에서 포인트컷 표현식은 이런 식으로 설명됐다.

    • 정규식처럼 보이지만 “정규식”이라기보다 메서드 시그니처 (함수명 / 인풋 / 아웃풋) 기반으로 선택하는 표현식이다.
    • 괄호 앞이 함수명, 괄호 안이 인풋, 앞쪽은 리턴 타입 (아웃풋)이라는 관점으로 해석한다.

 

수업 실습에서는 포인트컷을 2개로 나눴다.

  • aPointcut: *Impl.*(..)
    • Impl로 끝나는 클래스의 모든 메서드
  • bPointcut: *Impl.get*(..)
    • Impl로 끝나는 클래스 중 get으로 시작하는 메서드만

 

왜 이렇게 나누는가?

  • CRUD 전체에 공통 로그를 찍고 (aPointcut)
  • 조회 계열 (get*)만 전 / 후 감싸고 싶다 (bPointcut)

수업에서도 “위 표현식은 CRRUD 전체 / 아래 표현식은 get 계열 (조회)만”이라는 식으로 설명했다.

 

그리고 표현식 기호 해석도 강조했다.

    • * : 반환 타입 상관없음
    • (..) : 파라미터 개수 / 타입 상관없음
    • .. : 하위 패키지 포함
    • “점점 (..)은 개수 무관, 별 (*)은 타입 무관” 같은 설명

 

6) AOP 설정은 2파트: Pointcut 설정 + Aspect (결합) 설정

 

여기가 수업에서 “XML 작성 규칙”으로 찍어준 부분이다.

 

(A) 포인트컷 설정 파트: “어디에 적용할 건지 선택”

  • 표현식으로 실제 선택을 하고
  • id로 이름을 붙여 재사용한다
<aop:config>
  <aop:pointcut id="aPointcut"
    expression="execution(* com.example.biz..*Impl.*(..))"/>

  <aop:pointcut id="bPointcut"
    expression="execution(* com.example.biz..*Impl.get*(..))"/>
</aop:config>

패키지명 (com.example.biz..)은 네 코드가 실제로 com.example.biz 하위로 구성돼 있다는 전제를 따른다. (Board / Member 모두 그 구조다.)

 

(B) 애스펙트 (결합) 설정 파트: “무엇을 / 언제”를 붙인다

 

이 문장이 사실상 공식이다.

    • ref = 어드바이스 객체 (빈)
    • method = 그 객체가 가진 메서드
    • pointcut-ref = 결합할 포인트컷 이름
    • 그리고 가장 중요한 것 = 동작 시점 (when)
<aop:config>
  <!-- (1) Before: 모든 Impl 메서드 실행 전 기본 로그 -->
  <aop:aspect ref="la">
    <aop:before method="printLog" pointcut-ref="aPointcut"/>
  </aop:aspect>

  <!-- (2) After-throwing: 모든 Impl 메서드에서 예외 발생 시 로그 -->
  <aop:aspect ref="ata">
    <aop:after-throwing method="printLog" pointcut-ref="aPointcut"/>
  </aop:aspect>

  <!-- (3) Around: get* 조회 메서드만 전/후 감싸기 -->
  <aop:aspect ref="aa">
    <aop:around method="around" pointcut-ref="bPointcut"/>
  </aop:aspect>
</aop:config>
 

그리고 위 설정이 동작하려면 Advice 객체가 스프링 컨테이너에 bean으로 등록돼 있어야 한다.
수업에서도 “객체가 있어야 끼워 넣는다 (먼저 new / 객체 준비)”는 식으로 설명했다.

 

<bean id="la"  class="com.example.biz.common.LogAdvice"/>
<bean id="pla" class="com.example.biz.common.PlusLogAdvice"/>
<bean id="ata" class="com.example.biz.common.AfterThrowingAdvice"/>
<bean id="aa"  class="com.example.biz.common.AroundAdvice"/>

 

 

 

7) 실행 결과로 확인: “코드에 없는데 로그가 나온다”

 

이 실습의 핵심 장면은 수업에서 이렇게 표현됐다.

    • “없잖아. 안 불렀잖아. 코드에 없는데 돌아간다. 스프링이 해준다.”

또한 포인트컷을 2개로 나눴기 때문에 상황에 따라 로그가 다르게 찍힌다.

 

수업에서 실제로 “글 작성 (insert)”을 했을 때:

    • get*가 아니므로 bPointcut 대상이 아니다
    • 그래서 around는 안 걸리고 aPointcut에 걸린 기본 로그만 나온다고 설명했다.

이게 포인트컷 분리의 맛이다.

  • 전체에 적용할 정책 (aPointcut)
  • 조회에만 적용할 정책 (bPointcut)

 

“AOP 설정이 안 먹을 때” 점검 리스트

 

XML AOP는 “오타 한 글자”로도 바로 안 먹는다. 수업에서도 오타 주의를 강조했다.


그래서 아래를 순서대로 본다.

 

1. 빈 등록 여부

  • ref="la"인데 실제 <bean id="la" ...>가 없으면 무조건 실패

2. method 이름 일치

  • method="around"인데 실제 메서드명이 around()인지 확인
    (printLog ↔ around 혼동이 제일 흔한 실수)

3. pointcut-ref 이름 일치

  • pointcut-ref="aPointcut"인데 <aop:pointcut id="aPointcut"...>가 있는지 확인

4. 표현식이 코드 구조와 맞는지

    • 구현체가 BoardServiceImpl처럼 *Impl로 끝나므로, *Impl.*(..)가 자연스럽게 먹는다.
    • 조회만 잡으려면 get*가 실제 메서드명과 일치해야 한다. (getBoard, getBoardList)

5.  공통 패키지 / 스캔 범위

  • 포인트컷의 패키지 범위 (com.example.biz..)가 실제 클래스 패키지와 맞는지 확인

 

 

    • 1단계: 공통 로직 (Advice)을 클래스로 만든다
      • LogAdvice/PlusLogAdvice/AfterThrowingAdvice/AroundAdvice
    • 2단계: 핵심 로직은 AOP를 모르게 둔다 (서비스 / DAO는 본업만)
    • 3단계: 포인트컷 표현식으로 적용 대상을 고른다 (메서드 시그니처 기반)
    • 4단계: 애스펙트에서 “동작 시점 + advice 메서드 + pointcut”을 결합한다
    • 5단계: 실행해보면 “코드에 호출이 없는데도” 로그가 찍힌다 (스프링이 위빙)

 

 

“서비스는 일을 하고 공통 정책은 설정이 붙인다.”

'수업 복습' 카테고리의 다른 글

AOP 질의응답  (0) 2026.01.26
Spring XML 기반 AOP 실습 정리  (0) 2026.01.23
AOP 동작 시점 (Advice 타입) 5가지  (0) 2026.01.23
Spring AOP 개념 / 용어 정리  (0) 2026.01.23
Spring MVC / JSP Q&A 정리  (0) 2026.01.22