□ AOP(Aspect Oriented Programming) 란?
- 프로그램의 핵심 로직과 공통으로 사용되는 부가 로직을 분리하여 관리하는 프로그래밍 기법이다.
- Spring에서 특정 로직(예: 트랜잭션 관리, 로깅, 보안 등)을 메소드 실행 전후에 자동으로 추가한다.
; 특정 지점(Join Point)에서 실행될 코드를 정의(Advice)하고, 어떤 지점에서 실행될지를 설정하는 규칙(Pointcut)을 사용
; 예를 들어, 트랜잭션 관리, 로깅, 보안 로직을 서비스 메소드 실행 전후로 분리하여 코드 중복을 줄이고 유지보수를 쉽게 한다.
□ Proxy 객체 란?
- 실제 객체(원본 객체)에 대한 대리자 역할을 하는 객체로, 호출을 가로채거나 동작을 추가하는 데 사용
- Proxy 객체는 AOP를 구현하는데 사용되는 핵심 도구이다.
- Proxy는 외부 호출 시 동작하도록 설계되어 있다.
- AOP에서 Proxy 객체는 메소드 호출을 가로채고 부가 로직(Advice)을 실행하는 도구
- Spring AOP는 주로 JDK 동적 Proxy (인터페이스 기반)와 CGLIB 프록시 (클래스 기반)를 사용
□ Proxy 객체 생성과 동작
1. Proxy 객체 생성
a. 빈 후처리 단계에서 AOP 적용 여부를 판별한다
b. 해당 클래스에 AOP Advice가 적용될 가능성이 있다면 Proxy 객체가 생성된다
즉, 서비스에 일부 메소드만 Transactional이 걸려 있어도 Proxy가 생성됨
◆ Advice : Rollback / Commit 기능 담당
- AOP에서 특정 시점에서 실행되는 부가 로직을 의미
- ‘@Transactional’ Annotation이 선언된 method 또는 class에 대해 트랜잭션 처리와 관련된 부가 로직을 적용하는 AOP 기능
c. Proxy 객체는 Spring Bean으로 등록된다
2. Proxy 객체 주입
- Controller 등에 주입 받을 때는 이미 “Proxy인지 원본인지” 정해진 객체가 와 있다
3. Proxy 객체 동작 : CglibAopProxy.class 참고
- 가로챈 요청의 Bean 객체를 감싸며 동작한다
- ‘@Transactional’이 적용된 메서드는 Advice가 적용되고, 아니면 원본 메소드를 호출한다.
◆ Bean 後 처리 : Bean을 초기화하기 전후에 커스터마이징 할 수 있는 메커니즘으로, AOP 프록시 객체 생성, 의존성 주입 후 작업 등이
이루어진다
□ @Transactional 이란?
- `@Transactional`은 Spring에서 Transaction을 관리하기 위해 사용하는 Annotation이다.
이 Annotation은 Database 작업 중 오류가 발생했을 때, 작업 내역을 자동으로 Rollback/Commit하는 데 사용된다.
□ @Transactional 과 Proxy 객체의 관계
- ‘@Transactional’ 은 Proxy 객체의 AOP 기능을 통해 작동한다.
- Spring이 생성하는 Proxy 객체는 트랜잭션 관리를 담당하는 코드가 포함되어 있다
- 이 Proxy는 트랜잭션 처리 외에도 Spring AOP의 다른 부가 기능(로깅, 보안 등)을 처리할 수 있다.
□ @Transactional 과 접근 제한자
▷ 접근 제한자와 AOP 적용
- Proxy는 외부 호출 시 동작하도록 설계되어 있기 때문에, 내부 호출은 Proxy를 거치지 않음으로 AOP가 적용되지 않는다.
- `@Transactional` + `public` : 외부 호출일 경우에만 트랜잭션 적용. 내부에서 호출 시 적용되지 않음
- `@Transactional` + `private` : 프록시를 거치지 않아 트랜잭션이 적용되지 않음
■ 해결 방법 : 자기 참조 방식으로 호출하거나, 별도의 클래스로 분리해야 한다.
- 자기 참조 방식은 추천하지 않는다! (`this.methodB()` → `otherService.methodB()` )
- 내부 메소드 호출에서도 AOP를 적용하려면 해당 메소드를 다른 서비스(Bean)에서 호출하거나, 설계를 재구성해야 한다.
■ 예시
Q. 최상위 서비스 메소드에서 호출되는 여러 개의 하위 메소드가 있을 때, 그 중 일부에만 롤백 처리가 필요하다면?
A. 롤백 처리가 필요한 메소드만 다른 서비스/클래스로 분리해서 `@Transactional` + `public` 을 건다.
- 사용 예시
```java public class MainService { ... public void test1() { subService.nonTransactional(); } public void test2() { subService.transactional(); } public void test3() { subService.callPrivateTransactional(); } } public class SubService { public void nonTransactional(){ transactional(); } @Transactional public void transactional() { privateMethod(); } @Transactional public void callPrivateTransactional() { privateTransactional(); } private void privateMethod(){ } @Transactional(propagation = Propagation.NEW) private void privateTransactional(){ } } ```
|
메소드 호출 | 트랜잭션 적용 여부 | 특징 |
test1() | 없음 |
|
test2() | 적용 | `privateMethod` 은 트랜잭션 일부로 포함 |
test3() | 적용 | `privateTransactional()`은 직접 호출되므로 AOP 적용 안 됨 |
- Spring 6.0 부터는 protected와 default 메서드에도 @Transactional 이 적용되도록 바뀜
□ @Transactional 과 예외 처리
- ‘@Transactional’은 예외가 메서드 경계를 벗어나 전파될 때만 Transaction을 롤백 상태로 만든다
▷ 롤백 대상인 예외
- 추가적인 설정 없이 롤백 되는 예외는 ‘Error’, `RuntimeException`(Exception>Unchecked) 이다.
; `rollbackFor=RuntimeException.class` → 불필요
▷ 롤백 대상이 아닌 예외
- `CheckedException`, `UncheckedException` (런타임예외 제외)는 예외 발생 시 롤백 되지 않는다.
; rollbackFor 설정을 통해 롤백 대상에 추가할 수 있다.
▷ 예시
```java public class ParentService { ... @Transactional(nonRollbackFor=RuntimeException.class, rollbackFor=IOExcpetion.class) public void test(){ childService.runtimeRollback(); childService.checkedRollback(); } } public class ChildService { ... public void runtimeRollback() { // ... throw new RuntimeException("롤백 대상이 아니라고!"); } public void checkedRollback() throws IOException { try { ... } catch (IOException e){ throw e; } } } ```
|
- ‘RuntimeException’ 발생시 상위 메소드로 예외는 전파되지만, 트랜잭션은 롤백되지 않고 커밋된다.
- ‘IOException’ 발생시 rollbackFor 설정에 의해 롤백된다.
⭐ 발표자 : 이현진님
□ AOP(Aspect Oriented Programming) 란?
- 프로그램의 핵심 로직과 공통으로 사용되는 부가 로직을 분리하여 관리하는 프로그래밍 기법이다.
- Spring에서 특정 로직(예: 트랜잭션 관리, 로깅, 보안 등)을 메소드 실행 전후에 자동으로 추가한다.
; 특정 지점(Join Point)에서 실행될 코드를 정의(Advice)하고, 어떤 지점에서 실행될지를 설정하는 규칙(Pointcut)을 사용
; 예를 들어, 트랜잭션 관리, 로깅, 보안 로직을 서비스 메소드 실행 전후로 분리하여 코드 중복을 줄이고 유지보수를 쉽게 한다.
□ Proxy 객체 란?
- 실제 객체(원본 객체)에 대한 대리자 역할을 하는 객체로, 호출을 가로채거나 동작을 추가하는 데 사용
□ Proxy 객체 생성과 동작
1. Proxy 객체 생성
a. 빈 후처리 단계에서 AOP 적용 여부를 판별한다
b. 해당 클래스에 AOP Advice가 적용될 가능성이 있다면 Proxy 객체가 생성된다
즉, 서비스에 일부 메소드만 Transactional이 걸려 있어도 Proxy가 생성됨
◆ Advice : Rollback / Commit 기능 담당
- AOP에서 특정 시점에서 실행되는 부가 로직을 의미
- ‘@Transactional’ Annotation이 선언된 method 또는 class에 대해 트랜잭션 처리와 관련된 부가 로직을 적용하는 AOP 기능
c. Proxy 객체는 Spring Bean으로 등록된다
2. Proxy 객체 주입
- Controller 등에 주입 받을 때는 이미 “Proxy인지 원본인지” 정해진 객체가 와 있다
3. Proxy 객체 동작 : CglibAopProxy.class 참고
- 가로챈 요청의 Bean 객체를 감싸며 동작한다
- ‘@Transactional’이 적용된 메서드는 Advice가 적용되고, 아니면 원본 메소드를 호출한다.
◆ Bean 後 처리 : Bean을 초기화하기 전후에 커스터마이징 할 수 있는 메커니즘으로, AOP 프록시 객체 생성, 의존성 주입 후 작업 등이
이루어진다
□ @Transactional 이란?
- `@Transactional`은 Spring에서 Transaction을 관리하기 위해 사용하는 Annotation이다.
이 Annotation은 Database 작업 중 오류가 발생했을 때, 작업 내역을 자동으로 Rollback/Commit하는 데 사용된다.
□ @Transactional 과 Proxy 객체의 관계
- ‘@Transactional’ 은 Proxy 객체의 AOP 기능을 통해 작동한다.
- Spring이 생성하는 Proxy 객체는 트랜잭션 관리를 담당하는 코드가 포함되어 있다
- 이 Proxy는 트랜잭션 처리 외에도 Spring AOP의 다른 부가 기능(로깅, 보안 등)을 처리할 수 있다.
□ @Transactional 과 접근 제한자
▷ 접근 제한자와 AOP 적용
- Proxy는 외부 호출 시 동작하도록 설계되어 있기 때문에, 내부 호출은 Proxy를 거치지 않음으로 AOP가 적용되지 않는다.
- `@Transactional` + `public` : 외부 호출일 경우에만 트랜잭션 적용. 내부에서 호출 시 적용되지 않음
- `@Transactional` + `private` : 프록시를 거치지 않아 트랜잭션이 적용되지 않음
■ 해결 방법 : 자기 참조 방식으로 호출하거나, 별도의 클래스로 분리해야 한다.
- 자기 참조 방식은 추천하지 않는다! (`this.methodB()` → `otherService.methodB()` )
- 내부 메소드 호출에서도 AOP를 적용하려면 해당 메소드를 다른 서비스(Bean)에서 호출하거나, 설계를 재구성해야 한다.
■ 예시
Q. 최상위 서비스 메소드에서 호출되는 여러 개의 하위 메소드가 있을 때, 그 중 일부에만 롤백 처리가 필요하다면?
A. 롤백 처리가 필요한 메소드만 다른 서비스/클래스로 분리해서 `@Transactional` + `public` 을 건다.
- 사용 예시
```java
public class MainService {
...
public void test1() {
subService.nonTransactional();
}
public void test2() {
subService.transactional();
}
public void test3() {
subService.callPrivateTransactional();
}
}
public class SubService {
public void nonTransactional(){
transactional();
}
@Transactional
public void transactional() {
privateMethod();
}
@Transactional
public void callPrivateTransactional() {
privateTransactional();
}
private void privateMethod(){
}
@Transactional(propagation = Propagation.NEW)
private void privateTransactional(){
}
}
```
- Spring 6.0 부터는 protected와 default 메서드에도 @Transactional 이 적용되도록 바뀜
□ @Transactional 과 예외 처리
- ‘@Transactional’은 예외가 메서드 경계를 벗어나 전파될 때만 Transaction을 롤백 상태로 만든다
▷ 롤백 대상인 예외
- 추가적인 설정 없이 롤백 되는 예외는 ‘Error’, `RuntimeException`(Exception>Unchecked) 이다.
; `rollbackFor=RuntimeException.class` → 불필요
▷ 롤백 대상이 아닌 예외
- `CheckedException`, `UncheckedException` (런타임예외 제외)는 예외 발생 시 롤백 되지 않는다.
; rollbackFor 설정을 통해 롤백 대상에 추가할 수 있다.
▷ 예시
```java
public class ParentService {
...
@Transactional(nonRollbackFor=RuntimeException.class, rollbackFor=IOExcpetion.class)
public void test(){
childService.runtimeRollback();
childService.checkedRollback();
}
}
public class ChildService {
...
public void runtimeRollback() {
// ...
throw new RuntimeException("롤백 대상이 아니라고!");
}
public void checkedRollback() throws IOException {
try {
...
} catch (IOException e){
throw e;
}
}
}
```
- ‘RuntimeException’ 발생시 상위 메소드로 예외는 전파되지만, 트랜잭션은 롤백되지 않고 커밋된다.
- ‘IOException’ 발생시 rollbackFor 설정에 의해 롤백된다.
⭐ 발표자 : 이현진님