이전 @Transcational 을 학습할 때, AOP 라는 개념이 나왔었다.
AOP 가 이전부터 많이 들어왔지만...
아직까지도 "정확히 이게 무엇이다!" 하고 말할 수 있을 정도로는 이해되지 않았다.
단순히 @Transactional 같은 애노테이션이 내부적으로 작동할 때 쓰인다는 정도만 알고 있을 뿐이다.그래서 이번 기회에 AOP가 무엇인지,
왜 필요한지,
그리고 어떤 문제를 해결하기 위한 기술인지
개념적으로 정리해보려 한다.단순한 암기를 넘어, AOP의 필요성과 배경을 스스로 납득하고,
나중에 누군가 " AOP가 뭐야? "라고 물어보면
“ 아, 그거? 이런 상황에서 이렇게 쓰는 개념이야 ”라고
설명할 수 있을 만큼 내 것으로 만들기 위해서다.
AOP는 프로그램의 논리를 여러 관심사로 나누고, 이 중에서도 공통적으로 사용되는 수평적 관심사를 따로 모듈화하려는 접근 방식이다. 프로그래밍 언어나 패러다임 대부분은 일정 수준의 모듈화 수단을 제공한다. (예: 함수, 클래스, 메서드 등은 어떤 기능을 하나로 묶는 구조다.) 하지만 문제는, "로깅", "에러 처리", "보안 검사"처럼 여러 곳에 걸쳐 반복되는 기능은 여전히 코드 여기저기에 흩어지게 된다는 것이다. 이런 기능들을 횡단 관심사(cross-cutting concerns)라고 부른다. 예를 들어, 로깅은 시스템 곳곳에서 수행되므로 로그 코드가 수많은 클래스와 메서드에 흩어지게 된다. 이것이야말로 관심사 분리가 제대로 안 되어 생기는 전형적인 문제다.
즉 정리하면, AOP는
흩어진 공통 로직을 한 곳에 정의하고, 필요한 곳에만 자동으로 끼워넣는 구조다.
" 핵심 기능은 아닌데, 모든 코드에 반복적으로 등장하는 부가기능 "이 바로 횡단 관심사다.
쉽게 말하면....
햄버거 = 내 핵심 기능 (비즈니스 로직)
포장지 = 부가기능 (횡단 관심사) <- 이게 햄버거의 맛을 바꾸지 않으니까/로직을 바꾸지 않으니까
public void makeBurger() {
wrap(); // 포장지
grillPatty();
addCheese();
wrap(); // 또 포장지
}
이것이 횡단 관심사다.
이걸 AOP 로 바꾼다면? 포장지를 따로 만들어두고,
버거 다 만들고 나면 자동으로 포장해주는 것이다.
우리는 OOP 를 깊게 파고들지 않기 때문에
이 [ AI ] 가 하는 말을 줄이면 결국 OOP는
" 객체 간의 상호작용을 중심으로 프로그램을 구성하는 방식이다. "
이전에 내가 작성했던 객체지향 프로그래밍 생각정리 Link
"OOP로는 부족해서 AOP가 나왔는가?"
OOP(객체 지향 프로그래밍)의 가장 큰 강점 중 하나는 바로 관심사의 분리다.
그런데 아이러니하게도, OOP의 가장 큰 한계 역시 바로 이 관심사 분리의 부족에서 비롯된다.
예를 들어서 당신이 택배를 포장한다고 한다면
전제 조건 - 모든 택배는 똑같은 상자에 담긴다. - 포장 방식(테이핑, 라벨 부착 등)도 전부 동일하다.OOP 스타일이라면?
당신은 이렇게 행동하게 된다:
- 상자 배급소로 가서 포장 상자를 받는다.
- 다시 돌아와 물건을 상자에 넣는다.
(이 부분이 핵심 로직이다.)- 그 다음, 포장 전용 코너로 가서 포장 작업을 수행한다.
즉, 공통 작업인
상자 받기, 포장하기가
여러 군데에 흩어져 있고,
작업 흐름마다 반복적으로 이동하며 수행해야 한다.
AOP 스타일이라면?
당신은 그냥 물건만 창구에 건네주면 된다.
- 상자는 자동으로 배급되고
- 포장지도 자동으로 씌워지며
- 라벨도 자동으로 부착된다
즉, 당신은 핵심 작업(물건 담기)에만 집중하면 되고,
반복적인 공통 작업은 자동 시스템이 알아서 처리해준다.그리고 포장 방식이 바뀌더라도,
당신의 흐름은 단 1도 바뀌지 않는다.
변경은 포장 시스템 한 곳만 수정하면 끝이다.
AOP는 이런 흩어진 공통 로직을
한 곳에 정의하고, 자동으로 필요한 곳에 끼워넣는 구조다.
AOP 의 단점도 있다.
1. 디버깅이 어렵다.
2. 흐름 추적이 어렵다
3. Stack Overflow 에서 말하는 단점
이 말에 대해서 하나씩 알아보도록 하자.
Why ?
AOP는 메서드에@Log,@Transactional등만 달아놓고
실제로는 실행 전후에 로직이 자동으로 끼어든다.즉, 내 코드에는 분명 log()가 없는데 실행하면 로그가 남는다. 디버깅할 때 호출 흐름이 중간에 끊긴 듯한 느낌을 주고, 개발자는 "이 코드 왜 실행되지?" 하고 당황하게 될 수 있다.
Why ?
예를 들어 아래와 같은 코드가 있다면public void save() { // 핵심 로직 }실제 실행 흐름은?
1. AOP 프록시가 메서드를 가로챔
2. 트랜잭션 시작 (@Transactional)
3. 로그 기록 (@Log)
4. save() 실행
5. 트랜잭션 커밋
6. 후처리 Advice 실행 (예: 알림 발송)이걸 한눈에 알기 어렵고, 흐름이 코드 밖에 숨어 있어서 추적이 힘들다.
즉, " 어떤 부가기능이, 언제, 어떤 방식으로 실행되는지 "는 코드 본문에 드러나지 않는다.
( 내용이 길어서 정리한 것만 ) 1. Aspect의 의미가 불명확하다 Aspect가 어떤 동작을 하는지, 어떤 맥락에서 안전한지 독립적으로 정의하기 어렵다 로깅처럼 단순한 건 괜찮지만, 복잡한 동작은 예측 불가능하다 2. 정보 은닉 원칙 위반 핵심 로직 외부에서 실행되는 코드가 많아져서 어디서 무슨 일이 일어나는지 코드만 봐선 알 수 없다 3. 실행 흐름이 깨지기 쉽다 메서드 이름 변경, 구조 변경 등으로 Pointcut이 깨지면 Aspect 적용이 사라짐 이로 인해 애플리케이션이 오작동할 수 있음 4. 설계가 취약해지기 쉽다 Join Point를 의미 기반이 아니라 "이름 패턴"으로 지정하면 코드 의도와 무관하게 엮여서 깨지기 쉬움 5. 기술적으로 아직 미성숙 ( 10년 전 글이라 그럴 수 있다 생각함 ) 이론은 매력적이지만, 실무에서는 불안정하고 예측이 어렵다
1. 도구 체인 지원 부족 디버거나 프로파일러 같은 개발 도구들이 AOP를 제대로 인식하지 못할 수 있다. 그래서 실제로는 Aspect가 적용되어 동작하는 코드임에도 불구하고, 도구들은 그것을 단순한 절차형(일반적인) 코드로 인식하여 오작동하거나 정확한 분석을 하지 못할 수 있다. 2. 코드 부피 증가(Code Bloat) 소스 코드는 짧고 간단해 보여도, AOP에 의해 Aspect가 코드 전반에 "위빙(weaving)"되면서 실제로 생성되는 바이트코드(객체 코드)는 훨씬 커질 수 있다.
글 자체가 오래 되었지만, 지금도 어느정도 공통되는 단점은 있는 듯하다.
위에서도 여러 번 언급했지만, 스프링에서는 AOP가 이미 다양한 곳에서 사용되고 있다.
우리가 직접 @Aspect를 만들지 않아도, 다음과 같은 애노테이션들이 내부적으로 AOP 기반으로 동작한다.
@Transactional
트랜잭션 시작/커밋/롤백을 메서드 앞뒤로 자동 처리
@Async
메서드를 별도 쓰레드에서 비동기로 실행
@Scheduled
일정 시간 간격으로 메서드를 자동 실행
@PreAuthorize, @Secured
메서드 실행 전 권한 검사
@Cacheable, @CacheEvict
캐싱 처리 (메서드 결과를 저장하거나 캐시에서 제거)
이런 애노테이션들은 전부 "핵심 로직 외의 반복적인 공통 기능"을 자동으로 처리하기 위해 AOP 기반으로 구현되어 있다.
비즈니스 로직과 무관한 반복 작업이 여러 메서드/클래스에 걸쳐 흩어져 있을 때
→ 예: 로그 기록, 성능 측정, 트랜잭션, 인증 검사
핵심 코드의 가독성을 해치지 않으면서 공통 로직을 삽입하고 싶을 때
→ 핵심 로직은 그대로 두고, 앞뒤 처리만 붙이고 싶을 때
변경 가능성이 높은 로직을 분리하고 싶을 때
→ 공통 관심사를 모듈화해 한 곳만 수정해도 전체 반영되도록
가장 중요한 건, AOP를 완전히 마스터한 건 아니지만,
최소한 왜 나왔고, 어떤 문제를 해결하려는 기술인지에 대한 개념은 잡히게 되었다.
특히 OOP의 한계(로직의 흩어짐, 중복, 관심사 분리 문제)를 택배/포장 비유로 생각하면서
AOP가 왜 등장했는지를 직관적으로 이해할 수 있었다.
AOP는 강력하지만, 단점도 명확하다.
그렇기 때문에, "무조건 좋다"는 태도보단,
어디서 쓰면 좋은지, 어디서 피해야 하는지를 구분할 줄 아는 게 더 중요하다는 걸 느꼈다.
결론적으로 AOP는
( 글이 길어서 Daily 글은 패스 )
대신 제목이 API 명세서도 AOP가 해줬으면... 이 부분은
당연히 API 명세서는 "설계" 단계에서 해야 하니까 이론적으로는 맞지 않다.
그렇지만 너무 반복적이고 귀찮은 일이라,
AOP가 로깅처럼 알아서 "명세도 찍어줬으면..." 하는 마음은
아마 많은 개발자들의 솔직한 바람일지도 모른다.
그리고 그런 생각이 들 만큼,
오늘 AOP라는 개념이 실제로 어떤 문제를 해결하기 위해 등장했는지,
왜 필요한지, 언제 쓰면 좋은지를 이제는 좀 더 명확히 이해할 수 있게 되었다.