F-LAB JAVA · 8주차 · Phase 1 · AOP 입문과 동기
🌟 8주차 시작 — 프록시의 진화 (자바·Spring 학습의 클라이맥스)
이 Unit을 끝내면 다음을 답할 수 있어야 한다.
AOP (Aspect Oriented Programming, 관점 지향 프로그래밍) 는 어떤 로직을 핵심 관점 (비즈니스 로직) 과 부가 관점 (트랜잭션·로깅·보안·캐싱) 으로 나눠서 보고 각 관점을 별도로 모듈화하는 프로그래밍 패러다임으로, 여러 클래스에 반복적으로 등장하는 흩어진 관심사 (Crosscutting Concerns) 를 OOP 만으로는 깔끔히 처리하기 어려운 한계를 보완하기 위해 등장했으며 7주차의 @Transactional 이 가장 대표적 사례다.
AOP (Aspect Oriented Programming) 는 관점 지향 프로그래밍 으로 — 어떤 로직을 핵심 관점 + 부가 관점 으로 나눠서 보고 각 관점을 별도로 모듈화 하는 프로그래밍 패러다임이다.
핵심 관점 (Core Concern) — 비즈니스 로직 (주문 처리, 회원 가입, 배송 처리 등 도메인 의미가 있는 핵심 동작) — 클래스마다 다르고 변화가 잦다.
부가 관점 (Cross-cutting Concern) — 로깅·트랜잭션·DB 연결·보안·캐싱·성능 측정 — 여러 클래스에 반복적으로 등장 하며 변화가 적다.
흩어진 관심사 (Crosscutting Concerns) — 여러 클래스를 횡단해서 (cross-cutting) 흩어진 부가 관점들 — OOP 의 상속·캡슐화·다형성 만으로는 깔끔히 한 곳에 모으기 어렵다.
7주차에서 학습한 @Transactional 이 정확히 이 AOP 의 대표 사례 — 트랜잭션 시작·커밋·롤백 (부가 관점) 을 비즈니스 로직 (핵심 관점) 에서 분리해 어노테이션 1줄로 적용 — 다른 부가 관점 (@Cacheable/@PreAuthorize/@Async) 도 같은 메커니즘이다.
8주차의 학습 목표 — 이 @Transactional 이 어떻게 동작하는지의 모든 메커니즘 (프록시 패턴 → 동적 프록시 → ProxyFactory) 을 깊이 파헤쳐 자바·Spring 학습의 클라이맥스에 이르는 것이다.
AOP = 영화 제작의 두 관점:
핵심 관점 (스토리):
- 주인공 등장
- 갈등 발생
- 클라이맥스
- 결말
→ 영화마다 다름 (변화 많음)
→ 영화의 본질
부가 관점 (제작 환경):
- 조명 (모든 신마다)
- 카메라 워크
- 음향
- 자막 / 더빙
→ 모든 영화에 공통
→ 환경적 요소
→ "흩어져 있음"
OOP 의 한계:
- 영화마다 조명 / 카메라 / 음향 직접 설정
- 모든 영화에 같은 설정 복사
- 변경 시 모든 영화 수정
AOP 의 답:
- "스튜디오 자동 시스템" (조명/카메라/음향)
- "관점" 으로 모듈화
- 영화 (비즈니스) = 스토리만 집중
- 환경 = 자동
7주차 @Transactional:
- 비즈니스 로직 = 스토리
- 트랜잭션 = 환경 (조명)
- 어노테이션 1줄 = 자동 적용
ILIC:
- 1020 메서드 = 1020 영화 신
- 모두 @Transactional = 자동 환경
- 비즈니스만 집중
8주차 학습:
- @Transactional 의 동작 메커니즘
- 프록시 패턴 → 동적 프록시 → AOP
- 자바 진영의 추상화 정점
→ AOP = 핵심 관점 + 부가 관점 분리, 흩어진 관심사 해결, @Transactional 의 본질.
1. AOP 정의
2. 7주차 @Transactional 회상
3. 핵심 관점 (비즈니스 로직)
4. 부가 관점 (트랜잭션, 로깅 등)
5. 흩어진 관심사 (Crosscutting Concerns)
6. OOP 의 한계
7. AOP 의 등장 이유
8. Spring 의 AOP 적용 사례
9. 8주차 학습의 의의
AOP (Aspect Oriented Programming):
관점 지향 프로그래밍:
- 어떤 로직을 두 가지 관점으로 분리
- 핵심 관점 + 부가 관점
- 각 관점을 별도 모듈화
→ "관점 (Aspect) 으로 보는 프로그래밍"
Aspect (관점):
의미:
- 어떤 것을 보는 관점 / 측면
- 비즈니스의 한 측면
- 부가 기능의 한 측면
AOP 에서:
- 부가 관점의 모듈화
- 포인트컷 + 어드바이스 (Phase 8 에서 상세)
OOP 와의 관계:
OOP (Object Oriented Programming):
- 객체로 본다
- 클래스 / 상속 / 다형성
- 자바의 기본
AOP (Aspect Oriented Programming):
- 관점으로 본다
- 핵심 + 부가 분리
- OOP 보완 (대체 X)
관계:
- "AOP 는 OOP 를 대체하지 않음"
- "OOP 의 부족한 부분 (횡단 관심사) 을 보조"
→ OOP + AOP = 더 강력
출생 배경:
Xerox PARC 연구:
- 1990년대 후반
- AspectJ 프로젝트
- OOP 의 한계 인식
Spring 의 차용:
- 2003년+ Spring AOP
- AspectJ 문법 차용
- 프록시 방식 사용
→ Spring 의 핵심 기능
ILIC 의 AOP 활용
ILIC = Spring Boot + JPA:
AOP 의 명시적 / 암시적 활용:
- @Transactional (트랜잭션, 7주차)
- @Cacheable (캐시)
- @PreAuthorize (보안)
- @Async (비동기)
- @Loggable (로깅, 커스텀)
모두 AOP:
- 어노테이션 1줄
- 프록시 + 자동 적용
- 비즈니스 로직 깔끔
ILIC 의 1020 메서드:
- @Transactional 1020개
- 모두 AOP
- 매일 사용
AOP 의 정의는?
답:
1. AOP:
두 관점:
Aspect:
OOP 관계:
7주차 학습 응축:
Phase 7 — @Transactional:
- Unit 7.1 프록시 패턴
- Unit 7.2 동작 원리 ★★★
- Unit 7.3 5가지 함정
7주차에서 본 것:
- @Transactional 1줄 = 자동 트랜잭션
- 프록시 + AOP 가 동작 기반
- CGLIB 자식 클래스
- TransactionInterceptor 가로채기
- 7 단계 호출 흐름
@Transactional 의 본질 = AOP:
핵심 관점:
- 비즈니스 로직
- "주문 생성", "배송 처리" 등
부가 관점:
- 트랜잭션 시작 / 커밋 / 롤백
- 자원 관리
- 예외 처리
AOP 의 결과:
- 비즈니스 코드 깔끔
- 트랜잭션 자동
- 어노테이션 1줄
// AOP 적용 전 (수동 트랜잭션, 7주차 Phase 5)
public void processShipment(Long id) {
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
try {
tx.begin(); // 부가 (트랜잭션)
// 핵심 (비즈니스 로직)
Shipment s = em.find(Shipment.class, id);
s.markAsShipped();
em.merge(s);
tx.commit(); // 부가
} catch (Exception e) {
if (tx.isActive()) tx.rollback(); // 부가
throw new RuntimeException(e);
} finally {
em.close(); // 부가
}
}
// AOP 적용 후 (@Transactional, 7주차 Phase 7)
@Transactional // ← 부가 관점 = 어노테이션 1줄
public void processShipment(Long id) {
// 핵심 관점만!
Shipment s = repo.findById(id).orElseThrow();
s.markAsShipped();
}
class Shipment { void markAsShipped() {} }
EntityManagerFactory emf;
class EntityManagerFactory { EntityManager createEntityManager() { return null; } }
class EntityManager {
EntityTransaction getTransaction() { return null; }
<T> T find(Class<T> c, Object id) { return null; }
<T> T merge(T t) { return null; }
void close() {}
}
class EntityTransaction {
void begin() {}
void commit() {}
void rollback() {}
boolean isActive() { return false; }
}
ShipmentRepository repo;
interface ShipmentRepository { java.util.Optional<Shipment> findById(Long id); }
@interface Transactional {}
분리의 효과:
코드:
- 15 줄 → 3 줄
- SoC 완벽
- 가독성 ↑
유지보수:
- 비즈니스 변경 시 = 비즈니스만
- 트랜잭션 변경 시 = 어노테이션 옵션만
- 영향 격리
안전:
- 함정 자동 회피 (rollback / close 누락 X)
- 일관 패턴
// Spring 의 다양한 AOP 어노테이션
@Service
public class ShipmentService {
@Transactional // 트랜잭션
@Cacheable("shipments") // 캐시
@PreAuthorize("hasRole('ADMIN')") // 보안
@Async // 비동기
@Loggable // 커스텀 (로깅)
public Shipment process(Long id) {
// 비즈니스만!
}
}
// 효과:
// - 5 어노테이션 = 5 AOP
// - 모두 자동
// - 비즈니스 코드 깔끔
class Shipment {}
@interface Service {}
@interface Transactional {}
@interface Cacheable { String value(); }
@interface PreAuthorize { String value(); }
@interface Async {}
@interface Loggable {}
8주차 학습의 동기:
7주차에서:
- @Transactional 의 동작 원리 ★★★
- 프록시 + AOP 의 결과 사용
8주차에서:
- 프록시 + AOP 의 모든 메커니즘
- 디자인 패턴의 진화
- 동적 프록시 기술
- ProxyFactory 통합 추상화
9주차에서:
- Spring AOP 실전
- @Aspect / @Around
- 트랜잭션 전파 (★ 정점)
→ 7주차에서 본 결과 →
→ 8-9주차에서 메커니즘 완전 정복
ILIC 의 AOP 학습 의의
ILIC 의 1020 메서드:
- 모두 @Transactional
- 모두 AOP 작동
8-9주차 학습 후:
- 동작 원리 완전 이해
- 함정 회피 깊이
- 디버깅 능력
- 면접 정점
ILIC 의 다른 AOP 활용:
- @Cacheable (Redis)
- @Async (비동기 알림)
- @Loggable (감사 로그)
→ 모두 8-9주차 학습 활용
7주차 @Transactional 이 AOP 인 이유는?
답:
1. @Transactional:
분리:
AOP 의 본질:
8주차 학습:
핵심 관점 (Core Concern):
비즈니스 로직:
- 도메인 의미가 있는 동작
- "주문 처리", "결제", "배송"
- 클래스마다 다름
- 변화가 잦음
애플리케이션의 본질:
- "이 시스템이 무엇을 하는가"
- 매출 / 가치 직결
사례 (ILIC):
ShipmentService:
- process(): 배송 처리
- markAsShipped(): 선적 표시
- calculateFreight(): 운임 계산
CustomerService:
- register(): 고객 등록
- updateProfile(): 정보 변경
- checkCredit(): 신용 검사
BookingService:
- createBooking(): 예약 생성
- confirmBooking(): 예약 확정
- cancelBooking(): 예약 취소
→ 모두 도메인 의미
→ ILIC 의 본질
핵심 관점의 특징:
1. 도메인 의미:
- 비즈니스 용어
- 사용자에게 가치
2. 다양성:
- 클래스마다 다름
- 메서드마다 다름
3. 변화 잦음:
- 비즈니스 요구사항 변경
- 자주 수정
4. 분리 단위:
- 클래스 / 메서드 단위
- OOP 로 잘 표현 가능
OOP 의 강점 (핵심 관점):
- 캡슐화: 도메인 상태 보호
- 상속: 도메인 공통 부분
- 다형성: 다른 도메인 처리
핵심 관점은:
- OOP 로 잘 표현
- 자연스러움
→ 핵심 관점 = OOP 의 영역
// ILIC 의 핵심 관점
@Entity
public class Shipment { // 도메인 객체
@Id Long id;
String blNo;
ShipmentStatus status;
// 핵심 비즈니스 메서드 (도메인 의미)
public void markAsShipped() {
if (status != ShipmentStatus.CONFIRMED) {
throw new IllegalStateException("CONFIRMED 상태만 선적 가능");
}
this.status = ShipmentStatus.SHIPPED;
}
public BigDecimal calculateFreight() {
// 운임 계산 로직 (핵심)
return null;
}
}
@Service
public class ShipmentService {
@Autowired ShipmentRepository repo;
public Shipment process(Long id) { // 핵심 (비즈니스)
Shipment s = repo.findById(id).orElseThrow();
s.markAsShipped();
return s;
}
}
// → 핵심 관점만 (도메인)
// → 부가 관점 X
enum ShipmentStatus { CONFIRMED, SHIPPED }
ShipmentRepository repo;
interface ShipmentRepository { java.util.Optional<Shipment> findById(Long id); }
@interface Entity {}
@interface Id {}
@interface Service {}
@interface Autowired {}
핵심 관점 (비즈니스 로직) 의 의미는?
답:
1. 핵심 관점:
특징:
OOP:
ILIC:
부가 관점 (Cross-cutting Concern):
핵심 외의 부가 기능:
- 로깅
- 트랜잭션
- 보안 (인증/인가)
- 캐싱
- 성능 측정
- 예외 처리
- 모니터링
- 트레이싱
여러 클래스에 공통:
- "횡단" (cross-cutting)
- 흩어져 있음
사례:
로깅:
- 메서드 진입 / 종료
- 인자 / 반환값
- 예외
트랜잭션:
- begin / commit / rollback
- 7주차 @Transactional
보안:
- 권한 검사 (@PreAuthorize)
- 인증 확인
캐싱:
- 결과 캐시 (@Cacheable)
- 만료 정책
성능 측정:
- 실행 시간
- 호출 횟수
→ 모두 부가
부가 관점의 특징:
1. 비도메인:
- 인프라 / 기술
- 비즈니스 외
2. 공통성:
- 여러 클래스
- 비슷한 패턴
- 반복
3. 변화 적음:
- 정책 / 기술 변경 외 안 변함
- 보일러플레이트
4. 횡단:
- 여러 메서드 / 클래스 가로질러
- 모듈화 어려움 (OOP 만으로)
부가 관점의 비율 (수동 시):
메서드별:
- 핵심: 3-5 줄
- 부가: 12-15 줄
- 비율: 부가 > 핵심
본말 전도:
- 비즈니스보다 인프라
- 가독성 ↓
→ AOP 로 분리 필수
분리의 가치:
1. 비즈니스 집중:
- 코드 깔끔
- 의도 명확
2. 일관 적용:
- 모든 메서드 같은 방식
- 누락 X
3. 변경 안전:
- 부가 정책 변경 시
- 한 곳만 (Aspect)
4. 테스트:
- 비즈니스만 테스트
- 부가 분리
ILIC 의 부가 관점
ILIC 의 102 테이블 × 1020 메서드:
부가 관점 (모두 적용):
1. 트랜잭션 (@Transactional)
→ 1020 곳
2. 로깅 (@Loggable, 커스텀)
→ 1020 곳
3. 보안 (@PreAuthorize)
→ 일부 (인증 필요)
4. 캐싱 (@Cacheable)
→ 일부 (조회)
5. 비동기 (@Async)
→ 일부 (알림 등)
AOP 없으면:
- 1020 메서드 × 5 부가 = 5,100 곳 수정 필요
- 일관성 ↓
- 함정 ↑
AOP 있으면:
- 어노테이션 5개 × 1020 = 5,100 어노테이션
- 일관 패턴
- 자동
부가 관점 (트랜잭션, 로깅 등) 의 의미는?
답:
1. 부가 관점:
특징:
사례:
분리 가치:
흩어진 관심사 (Crosscutting Concerns):
여러 클래스를 횡단해서 흩어진 부가 관점:
- 한 곳에 모으기 어려움
- 여러 클래스에 반복 등장
- OOP 만으로는 깔끔히 처리 X
"흩어진" 의 의미:
- 100 클래스 × 같은 코드
- "관심사가 흩어져 있다"
시각화:
핵심 관심사 (각 클래스 다름):
┌──────────────┬──────────────┬──────────────┐
│ ShipmentService │ CustomerService │ BookingService │
├──────────────┼──────────────┼──────────────┤
│ 배송 처리 │ 고객 등록 │ 예약 생성 │
│ (도메인) │ (도메인) │ (도메인) │
└──────────────┴──────────────┴──────────────┘
흩어진 관심사 (모든 클래스에 공통):
┌──────────────┬──────────────┬──────────────┐
│ 로깅 │ 로깅 │ 로깅 │ ← 같은 코드
│ 트랜잭션 │ 트랜잭션 │ 트랜잭션 │ ← 같은 코드
│ 보안 │ 보안 │ 보안 │ ← 같은 코드
└──────────────┴──────────────┴──────────────┘
→ 흩어져 있음 (여러 클래스에)
→ 한 곳에 모을 수 없을까?
OOP 의 도전:
OOP 의 도구:
- 상속: 부모 클래스에 부가 코드?
→ 다중 상속 X (자바)
→ 강한 결합
→ 부적합
- 인터페이스 + 디폴트 메서드:
→ 부가 관점 모듈화 가능?
→ 한계 있음
- 합성:
→ 매번 부가 객체 호출
→ 비즈니스 코드 어지러움
→ OOP 만으로는 한계
흩어진 관심사 사례 3가지:
1. 로깅 (Logging):
- 모든 서비스 메서드의 진입 / 종료
- 호출 깊이 추적
- 인자 / 반환값
- 예외
2. 트랜잭션 (Transaction):
- 모든 비즈니스 메서드의 begin / commit / rollback
- 7주차 @Transactional
3. 보안 (Security):
- 모든 보호된 메서드의 권한 검사
- 인증 / 인가
- 감사 로그
흩어진 관심사 확장:
4. 캐싱:
- 조회 결과 캐시
- 만료 정책
5. 성능 측정:
- 실행 시간
- 호출 횟수
- 메트릭 (Prometheus 등)
6. 예외 처리:
- 공통 예외 변환
- 로깅 + 알림
7. 트랜잭션 격리:
- propagation 정책
- readOnly 최적화
8. 모니터링:
- APM (New Relic, Datadog)
- 트레이싱 (OpenTelemetry)
9. 감사 로그:
- 누가 / 언제 / 무엇을
- 규제 준수
ILIC 의 흩어진 관심사
ILIC 의 1020 메서드:
각 메서드에 공통 부가:
1. @Transactional (트랜잭션)
2. @Loggable (로깅, 커스텀)
3. @PreAuthorize (일부, 보안)
4. @Cacheable (일부, 캐싱)
횡단:
- ShipmentService 메서드
- CustomerService 메서드
- BookingService 메서드
- ...
모두 같은 부가 관점 가짐
"흩어진"
"여러 클래스 횡단"
AOP 의 해결:
- 각 부가 = 한 Aspect 모듈
- 어노테이션 1줄로 적용
- 일관 + 자동
흩어진 관심사 (Crosscutting Concerns) 의 의미는?
답:
1. 흩어진:
반복:
OOP 한계:
사례:
OOP 의 강점:
- 캡슐화: 도메인 상태 보호
- 상속: 공통 부분
- 다형성: 확장
- 클래스 / 인터페이스 / 메서드
핵심 관점 = OOP 가 강함:
- 도메인 표현
- 클래스 단위 분리
OOP 의 한계:
부가 관점 (흩어진 관심사) 처리 어려움:
1. 상속의 한계:
- 단일 상속 (자바)
- 강한 결합
- "이미 다른 부모 있음"
예: ShipmentService extends BaseService
→ 로깅 부모로 못 만듦
2. 합성의 한계:
- 매번 부가 객체 호출
- 비즈니스 코드 어지러움
- 일관성 ↓
예: public void process() {
logger.log("진입"); // 일일이
tx.begin();
// 비즈니스
tx.commit();
logger.log("종료"); // 일일이
}
3. 인터페이스의 한계:
- 부가 코드는 X
- 단순 계약만
횡단 (Crosscutting):
OOP:
- 수직 분류 (상속 트리)
- 클래스 위계 구조
흩어진 관심사:
- 수평 분포 (여러 클래스)
- 위계 무관
- "가로질러" 분포
OOP 의 수직 모듈화 ≠ 수평 모듈화
→ AOP 가 필요
시각적 이해:
OOP (수직 분류):
Animal
/ \
Dog Cat
/ \
Bulldog Persian
→ 같은 부모 → 공통 행동
→ 수직 모듈화
흩어진 관심사 (수평 분포):
Dog → 로깅, 트랜잭션, 보안
Cat → 로깅, 트랜잭션, 보안
Car → 로깅, 트랜잭션, 보안
→ 다른 부모 (다른 도메인)
→ 같은 부가 관점
→ 수평 분포
→ OOP 모듈화 어려움
데코레이터 패턴의 한계:
OOP 의 패턴 (데코레이터):
- 부가 기능 추가 가능
- 같은 인터페이스
- 중첩 가능
하지만:
- 각 메서드마다 데코레이터 클래스 작성
- 100 메서드 → 100 데코레이터
- 보일러플레이트 폭증
→ 패턴 자체는 한 단계 (필요한 자동화 X)
ILIC 의 OOP 한계
ILIC = 102 도메인 객체 (Entity):
- Shipment, Customer, Booking, ...
- 각자 다른 부모 / 도메인
공통 부가 관점:
- 모두 트랜잭션 필요
- 모두 로깅 필요
OOP 만:
- 각 Service 에 부가 코드
- 일일이 작성
- 1020 메서드 × 부가 코드
AOP 의 필요성:
- "관점" 으로 모듈화
- 한 Aspect = 모든 적용
- 어노테이션 1줄
OOP 의 한계는?
답:
1. OOP 강점:
한계:
이유:
결과:
AOP 의 등장:
1990년대 후반 ~ 2000년대:
- OOP 의 한계 인식
- 흩어진 관심사 문제
- "더 좋은 모듈화"
Xerox PARC:
- AspectJ 프로젝트
- 1997년 ~
- 자바 확장
Spring AOP:
- 2003년 ~
- AspectJ 차용
- 프록시 방식
AOP 의 답:
"흩어진 관심사를 Aspect (관점) 로 모듈화":
- 부가 관점 = 한 Aspect
- 적용 지점 = Pointcut
- 적용 코드 = Advice
결과:
- 부가 관점 = 한 곳
- 비즈니스 = 별도
- 깔끔 분리
핵심 통찰:
"부가 기능 + 부가 기능을 어디에 적용할지 의 선택을
합쳐서 하나의 모듈로 만든다.
그게 Aspect."
Aspect = Pointcut + Advice:
- Pointcut: 어디에?
- Advice: 무엇을?
- 합쳐서 = Aspect (관점)
Aspect 의 모듈화 효과:
로깅 Aspect:
- Pointcut: 모든 Service 메서드
- Advice: 진입 / 종료 로깅
트랜잭션 Aspect:
- Pointcut: @Transactional 메서드
- Advice: begin / commit / rollback
보안 Aspect:
- Pointcut: @PreAuthorize 메서드
- Advice: 권한 검사
→ 각 Aspect = 한 모듈
→ 한 곳에 관리
OOP + AOP:
대체 X, 보완 ✓:
- OOP: 핵심 관점 (수직)
- AOP: 부가 관점 (수평)
같이 사용:
- 자바 = OOP + AOP
- Spring = OOP + AOP
- 더 강력
실무:
- OOP 로 도메인 표현
- AOP 로 부가 기능
- 완벽 분리
ILIC = OOP + AOP
ILIC 의 도메인:
OOP (Entity 객체):
- Shipment, Customer, Booking ...
- 102 테이블
- 도메인 의미
- 비즈니스 메서드
ILIC 의 부가 기능:
AOP (Aspect):
- 트랜잭션 (@Transactional)
- 캐싱 (@Cacheable)
- 보안 (@PreAuthorize)
- 비동기 (@Async)
결과:
- 도메인 = OOP 깔끔
- 부가 = AOP 자동
- 1020 메서드 효율 운영
→ ILIC = OOP + AOP 결정체
AOP 의 등장 이유는?
답:
1. OOP 한계:
AspectJ:
답:
OOP + AOP:
Spring 의 AOP 활용:
Spring Framework 자체:
- @Transactional (트랜잭션)
- @Cacheable (캐시)
- @Async (비동기)
- @Retryable (재시도)
- @Validated (검증)
Spring Security:
- @PreAuthorize (사전 인가)
- @PostAuthorize (사후 인가)
- @Secured
Spring Cloud:
- @CircuitBreaker (서킷 브레이커)
- @Retry
- @TimeLimiter
→ AOP 가 곳곳에
// @Transactional (7주차)
@Service
public class ShipmentService {
@Transactional
public void process(Long id) {
// 비즈니스
}
}
// AOP 동작:
// - Pointcut: @Transactional 메서드
// - Advice: 트랜잭션 시작 / 커밋 / 롤백
// - 어노테이션 1줄
@interface Service {}
@interface Transactional {}
// @Cacheable
@Service
public class ShipmentService {
@Cacheable("shipments")
public Shipment getById(Long id) {
return repo.findById(id).orElseThrow();
}
}
// AOP 동작:
// - Pointcut: @Cacheable 메서드
// - Advice:
// 1. 캐시 확인 → 있으면 반환
// 2. 없으면 메서드 호출 → 결과 캐시 저장
class Shipment {}
ShipmentRepository repo;
interface ShipmentRepository { java.util.Optional<Shipment> findById(Long id); }
@interface Service {}
@interface Cacheable { String value(); }
// @PreAuthorize (Spring Security)
@Service
public class ShipmentService {
@PreAuthorize("hasRole('ADMIN')")
public void delete(Long id) {
repo.deleteById(id);
}
}
// AOP 동작:
// - Pointcut: @PreAuthorize 메서드
// - Advice:
// 1. 현재 사용자 권한 확인
// 2. ADMIN → 메서드 호출
// 3. 아니면 → AccessDeniedException
ShipmentRepository repo;
interface ShipmentRepository { void deleteById(Long id); }
@interface Service {}
@interface PreAuthorize { String value(); }
// @Async (비동기 실행)
@Service
public class NotificationService {
@Async
public void sendEmail(String to, String subject) {
// 시간 걸리는 작업
emailService.send(to, subject);
}
}
// AOP 동작:
// - Pointcut: @Async 메서드
// - Advice:
// 1. 별도 스레드에서 실행
// 2. 호출자는 즉시 반환
class EmailService { void send(String to, String s) {} }
EmailService emailService;
@interface Service {}
@interface Async {}
// 커스텀 @Loggable (개발자가 만듦)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggable {}
@Aspect
@Component
public class LoggingAspect {
@Around("@annotation(com.ilic.Loggable)")
public Object log(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
try {
Object result = pjp.proceed();
log.info("{} took {}ms",
pjp.getSignature(),
System.currentTimeMillis() - start);
return result;
} catch (Exception e) {
log.error("{} failed", pjp.getSignature(), e);
throw e;
}
}
}
// 사용
@Service
public class ShipmentService {
@Loggable // 커스텀 AOP
public void process(Long id) { }
}
@interface Target { ElementType[] value(); }
enum ElementType { METHOD }
@interface Retention { RetentionPolicy value(); }
enum RetentionPolicy { RUNTIME }
@interface Aspect {}
@interface Component {}
@interface Around { String value(); }
class ProceedingJoinPoint {
Object proceed() throws Throwable { return null; }
Object getSignature() { return null; }
}
@interface Service {}
@interface Loggable {}
org.slf4j.Logger log;
// ILIC 의 AOP 종합 활용
@Service
public class ShipmentService {
@Autowired ShipmentRepository repo;
// 다중 AOP
@Transactional
@Cacheable("shipments")
@Loggable
@PreAuthorize("hasRole('USER')")
public Shipment getShipment(Long id) {
return repo.findById(id).orElseThrow();
}
@Transactional
@Loggable
@PreAuthorize("hasRole('ADMIN')")
@CacheEvict(value = "shipments", key = "#id")
public void delete(Long id) {
repo.deleteById(id);
}
}
// 한 메서드 = 여러 AOP:
// - 트랜잭션
// - 캐시
// - 로깅
// - 보안
// 모두 어노테이션 1줄씩
// 모두 자동
// 모두 분리
class Shipment {}
ShipmentRepository repo;
interface ShipmentRepository {
java.util.Optional<Shipment> findById(Long id);
void deleteById(Long id);
}
@interface Service {}
@interface Autowired {}
@interface Transactional {}
@interface Cacheable { String value(); }
@interface CacheEvict { String value(); String key(); }
@interface Loggable {}
@interface PreAuthorize { String value(); }
Spring 의 AOP 적용 사례는?
답:
1. Spring 자체:
Spring Security:
커스텀:
혼합:
8주차의 위치:
1주차 OOP →
2주차 JVM →
3주차 함수형 →
4주차 동시성 →
5주차 디자인 패턴 →
6주차 DB 접근 →
7주차 JPA + @Transactional →
8주차 프록시의 진화 ← 여기
9주차 Spring AOP 실전
→ 7주차의 동작 원리 (★★★) 를 깊이 파헤치는 단계
8주차 학습 흐름:
Phase 1 — AOP 입문과 동기 (지금)
- Unit 1.1 AOP 정의 ← 여기
- Unit 1.2 로그 추적기
- Unit 1.3 ThreadLocal
Phase 2 — 디자인 패턴의 진화
- 템플릿 메서드 → 전략 패턴
Phase 3 — 콜백과 프록시의 만남
- 템플릿 콜백 → 프록시 개념
Phase 4 — 프록시 vs 데코레이터
- 의도 구분
Phase 5 — 동적 프록시 기술
- Reflection / JDK / CGLIB
Phase 6 — ProxyFactory (★ 정점)
- Pointcut / Advice / Advisor
5+6+7주차와의 연결:
5주차 (디자인 패턴):
- 템플릿 메서드 / 전략 / 프록시
→ 8주차 Phase 2-4
5주차 (DI / IoC):
- 빈 관리
→ 8주차 Phase 7 (빈 후처리기)
6주차 (JdbcTemplate):
- 템플릿 콜백 패턴
→ 8주차 Phase 3
7주차 (@Transactional):
- AOP 의 결과 사용
→ 8주차에서 메커니즘 파헤치기
→ 모든 학습 응축
9주차 예고:
Phase 7 — 빈 후처리기와 자동 프록시 생성기
Phase 8 — @Aspect 와 AOP 용어
Phase 9 — Spring AOP 실전 패턴
Phase 10 — @Transactional 함정 + 트랜잭션 전파 ★ 정점
특히:
- Internal call 문제 (★★★)
- 트랜잭션 전파 (★★★)
- UnexpectedRollbackException
학습 성취:
8-9주차 완주 후:
- AOP 메커니즘 완전 이해
- @Transactional 동작 원리 (7주차 + 8주차)
- 프록시 기술 (JDK / CGLIB)
- ProxyFactory 활용
- 트랜잭션 전파 마스터
- 면접 시니어 시험 통과
자바 백엔드 시니어 :
- 7주차 + 8주차 + 9주차 응축
- 깊은 이해
- 운영 안정
| Q | 핵심 답변 |
|---|---|
| AOP? | 관점 지향 |
| 핵심 vs 부가? | 비즈니스 vs 트랜잭션 등 |
| 흩어진 관심사? | 횡단 분포 |
| OOP 한계? | 수평 모듈화 어려움 |
| 등장 이유? | 흩어진 관심사 해결 |
| Aspect? | Pointcut + Advice |
| Spring 사례? | @Transactional / @Cacheable 등 |
| OOP + AOP? | 보완 |
| 8주차 학습? | 메커니즘 파헤치기 |
| 9주차? | 실전 + 전파 |
답:
답:
답:
답:
답:
1. AOP = 관점 지향 프로그래밍
2. 흩어진 관심사 (Crosscutting Concerns)
3. OOP + AOP = 자바 백엔드의 강력
이번 Unit에서 AOP 정의를 봤다면, 다음은 로그 추적기 시나리오로 흩어진 관심사를 코드로 본다.
📚 Phase 1 — AOP 입문과 동기
✅ Unit 1.1 AOP 란 무엇인가 ← 여기
⏭ Unit 1.2 로그 추적기
⏭ Unit 1.3 ThreadLocal
🌟 8주차 — 프록시의 진화
📚 Phase 1 (1/3)
⏭ Phase 2-6 (총 14 Unit)
총: 1/17 Unit (6%)
🌟 8주차 시작 — 프록시의 진화 (자바·Spring 학습의 클라이맥스)