← 글 목록

Java AOP 프록시와 어노테이션 이해하기

/ 6분 분량

자바 AOP(Aspect-Oriented Programming)의 프록시와 어노테이션 개념이 처음에는 낯설었지만, 점차 명확하게 이해하게 되었습니다. 특히 멀티모듈 프로젝트 환경에서 AOP가 어떻게 동작하고 왜 필요한지에 대한 궁금증을 풀 수 있었습니다.

Java AOP 프록시와 어노테이션 이해하기

자바 AOP(Aspect-Oriented Programming)의 프록시와 어노테이션 개념이 처음에는 낯설었지만, 점차 명확하게 이해하게 되었습니다. 특히 멀티모듈 프로젝트 환경에서 AOP가 어떻게 동작하고 왜 필요한지에 대한 궁금증을 풀 수 있었습니다.

학습 주제

  • 공부 주제: Java AOP, 프록시, 어노테이션, 멀티모듈 구조
  • 대화 제목: java-AOP-proxy-annotations-classpath-jar-jvm-gradle-build
  • 학습 날짜: 2026년 4월 3일

질문과 탐구

처음에는 자바 프로그래밍 경험이 없어 AOP, 프록시, 트랜잭션 어노테이션 등에 대한 이해가 부족했습니다. NGINX 프록시 경험은 있지만, 객체 지향에서의 프록시는 어떻게 작동하는지, 특히 this.method() 호출 시 AOP가 왜 적용되지 않는지, @Transactional이 무엇인지, 그리고 멀티모듈 구조에서 common 모듈의 Aspect가 어떤 역할을 하는지에 대해 깊이 탐구했습니다.

핵심 학습 내용

AOP와 프록시의 원리

AOP는 코드를 수정하지 않고도 공통 기능(로깅, 트랜잭션 등)을 적용할 수 있게 해주는 기법입니다. 이는 스프링이 대상 객체 앞에 '프록시(Proxy)'라는 껍데기 객체를 세워 동작합니다.

  • 프록시의 역할: 외부에서 객체로의 호출을 가로채서, 실제 메서드 실행 전후에 로깅, 시간 측정, 트랜잭션 처리 등의 공통 기능을 수행합니다.
  • this.method() 호출의 문제: 같은 클래스 내에서 this.otherMethod()를 호출하면, 이는 프록시를 우회하고 실제 객체의 메서드를 직접 호출하게 됩니다. 이 때문에 AOP가 적용되지 않는 현상이 발생합니다. @Transactional 역시 프록시 기반으로 동작하므로, self-invocation 시 트랜잭션이 정상적으로 동작하지 않을 수 있습니다.
  • @Around 어노테이션의 유용성: @Around는 메서드 실행 전, 실행 후, 성공 시, 실패 시 등 모든 단계를 한 곳에서 처리할 수 있어 AOP 구현에 가장 유용합니다.

스프링 어노테이션과 멀티모듈 구조

  • @Service, @RestController: 스프링이 클래스를 스캔하여 Bean으로 등록하도록 하는 표식입니다. @RestController는 추가적으로 응답을 JSON으로 변환하는 기능도 수행합니다.
  • Port (Contract)와 구현체: 모듈 간 의존성을 줄이기 위해 인터페이스(Port)로만 정의하고, 실제 구현체는 다른 모듈에서 주입받는 방식입니다. profile 모듈이 BlogPort 인터페이스만 의존하면, blog 모듈의 내부 구현이 변경되어도 profile 모듈은 영향을 받지 않습니다.
  • 멀티모듈 모놀리식: 여러 모듈로 코드를 분리하더라도, 하나의 실행 가능한 JAR 파일로 빌드되어 단일 프로세스로 실행되는 구조입니다. app 모듈이 다른 모듈들을 의존하여 최종적으로 모든 Bean을 포함한 스프링 컨테이너를 구성합니다.
  • common 모듈의 Aspect: 공통으로 사용되는 AOP 로직을 common 모듈에 두면, 각 모듈에서 이를 참조하여 중복 없이 AOP 기능을 적용할 수 있습니다.

클래스패스와 Bean 등록

  • 클래스패스: JVM이 클래스를 찾기 위한 경로 목록으로, 컴파일타임과 런타임 모두에 존재합니다.
  • Bean 등록 과정: 클래스패스에 있어야 JVM/스프링이 클래스를 로딩할 수 있고, @Service 등의 어노테이션으로 스프링이 컴포넌트로 인식하면 Bean으로 등록됩니다. 즉, 클래스패스 포함 → 로딩 가능 → 컴포넌트 인식 → 인스턴스 생성 → Bean 등록의 단계를 거칩니다.

이해한 내용

  • AOP는 객체 지향의 단점을 보완하고 코드 중복을 줄여 유지보수성을 높이는 강력한 방법임을 이해했습니다.
  • 프록시가 스프링 AOP의 핵심이며, 런타임에 동적으로 생성된다는 것을 알게 되었습니다.
  • self-invocation의 문제를 프록시의 동작 원리와 연결하여 명확히 이해했습니다.
  • 멀티모듈 프로젝트에서 Port 패턴과 AOP를 효과적으로 활용하는 방법을 배웠습니다.
  • jar 파일이 자바 실행 가능한 패키지이며, implementation project는 Gradle 의존성을 설정하는 것임을 알게 되었습니다.

실전 적용

  • 향후 프로젝트에서 공통적으로 필요한 로깅, 보안 검사, 트랜잭션 관리 등에 AOP를 적극적으로 활용할 수 있습니다.
  • 특히 멀티모듈 구조에서 모듈 간 의존성을 최소화하고 재사용성을 높이는 데 Port 패턴과 AOP를 조합하여 적용할 계획입니다.
  • this.method() 호출 시 AOP가 적용되지 않는 부분을 인지하고, 이를 피하기 위한 코드 설계나 대안을 고려할 수 있습니다.

추가 학습 계획

  • 스프링 AOP에서 JDK 동적 프록시와 CGLIB 프록시의 차이점과 선택 기준에 대해 더 깊이 공부하고 싶습니다.
  • AOP를 활용한 예외 처리 전략이나 성능 모니터링 구현 방안을 탐색할 예정입니다.
  • 스프링 부트의 다양한 부가 기능(Spring Security, Spring Batch 등)과 AOP의 연계에 대해 학습하고 싶습니다.

참고 자료

  • 대화에서 직접적으로 언급된 참고 자료는 없었으나, AOP, 프록시, 스프링 DI, 멀티모듈 구조 등 관련 개념을 학습하며 맥락을 이해했습니다.