본문 바로가기
스터디정리방

[기록] 220112

by gogumi 2022. 1. 16.
1. Weaving 

분리한 관점을 여러 차례 모듈에 삽입하는 것을 AOP에서는 위빙(Weaving: 엮기)이라고 부른다. 

즉 공통 코드(Advice)를 핵심 로직 코드에 삽입하는 것이 Weaving이다. 

Aspect를 target에 제공하여 새로운 프록시 객체를 생성하는 과정을 말한다. 

Compile-time Weaving, Load-time weaving, Run-time weaving 세가지 방식의 Weaving이 존재하고, Spring AOP에서는 CGLIB Proxy, JDK Dynamic Proxy를 이용한 Run-time Weaving 방식을 제공한다. 

 

Spring AOP는 사용자의 특정 호출 시점에 IoC 컨테이너에 의해 AOP를 할 수 있는 Proxy Bean을 생성해준다. 동적으로 생성된 Proxy Bean은 타깃 메소드가 호출되는 시점에 부가기능을 추가할 메소드를 가로채어 부가기능을 주입해준다. 이처럼 호출 시점에 동적으로 위빙을 한다 하여 Runtime Weaving이라고 한다. 

 

2. Spring AOP 

Spring에서는 Proxy를 바탕으로 관심사를 추출하는 AOP를 제공한다. 

Proxy를 이용한 런타임 위빙(Runtime Weaving)을 통해서 관심사를 추출할 수 있다. 

Spring AOP에서는 이러한 기능을 2가지 방법으로 구사한다. 

1. JDK Dynamic Proxy 

2. CGLIB Proxy 

3. JDK Dynamic Proxy

JDK Dynamic Proxy는 1.3버전부터 생긴 기능이며, Interface를 기반으로 Proxy를 생성해주는 방식이다. 때문에 Interface를 강제화 한다는 단점이 있다. 

Dynamic Proxy는 Invocation Handler를 상속받아서 실체를 구현하게 되는데, 

이 과정에서 특정 Object에 대해 Reflection을 사용하기 때문에 성능이 조금 떨어지는 크리티컬한 단점이 있다. 

4. CGLIB (Code Generator Library) Proxy

CGLIB는 코드 생성 라이브러리로서 런타임에 동적으로 자바 클래스의 프록시를 생성해주는 기능을 제공한다. 이 때 CGLIB는 바이트코드를 조작하는 프레임워크인 ASM을 사용한다.  

아래는 CGLIB의 주요 구성요소이다. 

CGLIB는 프록시 생성과 관련된 모듈은 Enhancer 클래스, Callback 인터페이스 그리고 CallbackFilter 인터페이스다. 이 세가지만 있으면 쉽게 프록시 객체를 생성할 수 있게 된다. 

CGLIB Proxy Enhancer 클래스를 바탕으로 Proxy를 구현하는 방식이다. 

 방식은 JDK Dynamic Proxy와는 다르게 Reflection을 사용하지 않고, Extends(상속) 방식을 이용해서 Proxy화 할 메소드를 오버라이딩하는 방식이다. 기본적으로 바이트코드를 조작해서 바이너리가 만들어지기 때문에 JDK Dynamic Proxy보다 성능적으로 더 우세하다. 다만, final 객체 혹은 private 접근자로 된 메소드는 Override가 지원되지 않기 때문에 제약적인 Proxy 구현이 가능하다. 

Spring AOP Proxy를 생성하는 과정에서 자체 검증 로직을 통해 타깃의 인터페이스 유무를 판단한다.  이때 만약 타깃이 하나 이상의 인터페이스를 구현하고 있는 클래스라면 JDK Dynamic Proxy 방식으로 생성되고 인터페이스를 구현하지 않은 클래스라면 CGLIB의 방식으로 AOP 프록시를 생성해준다.

5. AOP

누군가 AOP 가 무엇이냐고 묻는다면?
: AOP는 관점 지향 프로그래밍의 약자로써 기존의 OOP에서 기능별로 클래스를 분리했음에도 불구하고, 여전히 트랜잭션, 자원해제, 성능테스트, 메소드처럼 공통적으로 반복되는 중복코드가 발생하는 단점이 생겨서 이를 해결할 수 있도록 개발 코드에서는 비즈니스 로직에 집중하고, 실행 시 비즈니스 로직의 앞과 뒤에서 원하는 지점에 해당 공통 관심사를 수행할 수 있게 하면서 중복 코드를 줄일 수 있는 방식이라고 답한다.

에스펙트란 무엇일까? 에스펙트는 객체지향 언어의 클래스와 비슷한 개념이라고 생각하면 이해가 쉽다.
에스펙트는 그 자체로 애플리케이션의 도메인 로직을 담은 핵심 기능은 아니지만, 많은 오브젝트에 걸쳐서 필요한 부가기능을 추상화 해놓은 것이다.

추가적으로 아래의 AOP개념과 용어를 덧붙여 설명하면 Best..

aop 개념은 IOC/DI 개념과 더불어 스프링에서 아주 중요한 핵심 개념중의 하나이기 때문에, 스프링 공식 문서를 통해 AOP 정의에 대해 다시 짚어보고, 개념과 용어에 익숙해지기 위해 머릿속에 그림을 그리면서 다시 정리를 해보자.

Joinpoint: A joinpoint is a candidate point in the Program Execution of the application where an aspect can be plugged in. This point could be a method being called, an exception being thrown, or even a field being modified. These are the points where your aspect’s code can be inserted into the normal flow of your application to add new behavior.

Advice: This is an object which includes API invocations to the system wide concerns representing the action to perform at a joinpoint specified by a point.

Pointcut: A pointcut defines at what joinpoints, the associated Advice should be applied. Advice can be applied at any joinpoint supported by the AOP framework. Of course, you don’t want to apply all of your aspects at all of the possible joinpoints. Pointcuts allow you to specify where you want your advice to be applied. Often you specify these pointcuts using explicit class and method names or through regular expressions that define matching class and method name patterns. Some AOP frameworks allow you to create dynamic pointcuts that determine whether to apply advice based on runtime decisions, such as the value of method parameters.

The following image can help you understand Advice, PointCut, Joinpoints. enter image description here

6. AOP 개념과 용어

Aspect : 여러 객체를 관통하는 ‘공통 관심 사항’을 구현한것을 의미합니다.
Spring 에서는 설정을 통해 일반 클래스에 넣는 방식(schema-based approach) 혹은 어노테이션을 활용한 방식으로 클래스에 aspect를 줄수 있습니다. (JointPoint와 PointCut을 묶어서 Aspect로 관리)

Join point
: 특정 작업이 시작되는 시점을 나타내는 포인트로, 메서드 호출이나 예외발생 등의 시점들을 의미합니다.
(어떤 시점(진입전,진입후,예외를 던진 뒤,결과를 던진 뒤 등)에 해야 할 일을 지정)

Advice
: 특정 join point에서 실행되는 action을 뜻하며, 실행 시점에따라 ‘around’, ‘before’, ‘after’의 타입들을 가지고 있습니다.

Pointcut : Join point의 부분집합으로써, 실제 Advice가 실행되는 Join point들의 집합을 의미합니다.
(JoinPoint가 적용이 되는 대상)

Target object : advise가 적용되어질 타깃 객체를 의미합니다.

AOP proxy
: Aspect를 구현하기위해 AOP 프레임워크에서 만들어낸 객체를 의미합니다.

Introduction
: proxy 객체에 메소드나 필드를 추가한것을 의미합니다.

weaving
: Aspect를 Target object에 적용하는것. 컴파일시, 로드타입시, 런타임시 적용시킬수 있으나 Spring에서는 런타임때 적용시킵니다.

7. AOP설정

강제로 CGLIB 프록시를 사용하려면 요소의 proxy-target-class 속성 값을 true로 설정

<aop:config proxy-target-class="true">

@AspectJ 자동 프록시 지원을 사용할 때 강제로 CGLIB 프록시를 사용하려면 요소의 'proxy-target-class'속성을 true로 설정

<aop:aspectj-autoproxy proxy-target-class="true"/>  

spring-boot properties 아래와 같이 설정

spring.aop.proxy-target-class=true
Spring AOP는 JDK 동적 프록시 또는 CGLIB를 사용하여 지정된 대상 객체에 대한 프록시를 만든다.

JDK 동적 프록시 java의 리플렉션을 이용해서 객체를 만든다. CGLIB 경우에는 바이트코드를 조작해 프록시 객체를 만든다.

Spring boot 는 기본적으로 transaction 대상의 aop를 동작시킬 프록시를 cglib 프록시를 사용하게 설정 해놨다.

그리고 성능 또한 jdk 프록시보다는 cglib가 빠르다!!! 우리가 느낄정도? 아니지만 일반적으로 cglib가 예외를 발생시킬 가능성이 낮다고 한다.

프록시 할 대상 객체가 하나 이상의 인터페이스를 구현하는 경우 JDK 동적 프록시가 사용됩니다.타겟 타입에 의해 구현 된 모든 인터페이스는 프록시화된다.

대상 객체가 인터페이스를 구현하지 않으면 CGLIB 프록시가 생성된다.

CGLIB 프록시 (예를 들어, 인터페이스에 의해 구현 된 것뿐만 아니라 대상 클래스에 대해 정의 된 모든 메소드를 프록시하는 경우)를 사용하도록 강제 할 수 있다.

*But, spring boot는 인터페이스로 구현해도 아래의 옵션이 디폴트기 때문에 AOP구현시 내부적으로 cglib 방식으로 동작한다.

spring.aop.auto(default true)  
spring.aop.proxy-target-class(default true)  

-@Configurable 과 LTW(Low Time Weaver)

: 스프링프레임워크 2.5 부터 지원된 Entity와 같은 도메인 모델에서 DI를 받기 위한 기술.
특정 도메인 객체에 @Configurable 를 선언하고 도메인 내 필드에 @Autowired를 이용해 의존성을 주입시키면 Spring bean이 주입되는 설정이 가능함.
(스프링 환경에서 IOC 대상이 아닌 일반객체도 별도의 코드 작성없이 스프링 컨테이너로부터 DI 받을 수 있는 방법)

// code
@Data
@Configurable
public class Entity {
    @Autowired
    private EntityRepository repository;
    
    private Long entityNo;
    private String description;
}
// test code
@Test
public void newEntity() {
    Entity entity = new Entity();
    assertThat(entity.getRepository(), is(notNullValue()));
}

-> DDD를 지원하기 위한 작업.(커맨드 패턴 등 활용)

8. DDD

: DDD(Domain-Driven Design) 또는 도메인 주도 설계라고 부른다. 도메인 패턴을 중심에 놓고 설계하는 방식을 일컫는다.

-특징

도메인 그 자체와 도메인 로직에 초점을 맞춘다. 일반적으로 많이 사용하는 데이터 중심의 접근법을 탈피해서 순수한 도메인의 모델과 로직에 집중하는 것을 말한다.

보편적인(ubiquitous) 언어의 사용이다. 도메인 전문가와 소프트웨어 개발자 간의 커뮤니케이션 문제를 없애고 상호가 이해할 수 있고 모든 문서와 코드에 이르기까지
동일한 표현과 단어로 구성된 단일화된 언어체계를 구축해나가는 과정을 말한다. 이로서 분석 작업과 설계 그리고 구현에 이르기까지 통일된 방식으로 커뮤니케이션이 가능해진다.

소프트웨어 엔티티와 도메인 컨셉트를 가능한 가장 가까이 일치시키는 것이다. 분석 모델과 설계가 다르고 그것과 코드가 다른 구조가 아니라,
도메인 모델부터 코드까지 항상 함께 움직이는 구조의 모델을 지향하는 것이 DDD의 핵심원리이다.

-> 그렇다면 데이터 주도 설계란 무엇이며 왜 사용해야할까?

: 데이터 주도 설계란 객체가 가져야 할 데이터에 초점을 두고 설계를 하는 방식을 일컫는다.
데이터 주도 설계 에서는 객체 자신이 포함하고 있는 데이터를 조작하는 데 필요한 행동을 정의한다.

도메인 모델의 적용 범위를 구현까지 확장하여 도메인 지식을 구현 코드에 반영한다.
공통의 언어(유비쿼터스 언어)를 사용하여 도메인과 구현을 충분히 만족하는 모델을 만든다.

실제 코드로 구현 가능한 현실성 있는 도메인 모델 분석과 그것을 추상화하는 설계이다.
"설계를 하라, 그 다음에 구축하라"가 아니다.

9. JDK Dynamic Proxy와 CGLIB

Spring AOP는 사용자의 특정 호출 시점에 IoC 컨테이너에 의해 AOP를 할 수 있는 Proxy Bean을 생성해준다.

동적으로 생성된 Proxy Bean은 타깃의 메소드가 호출되는 시점에 부가기능을 추가할 메소드를 자체적으로 판단하고 가로채어 부가기능을 주입해준다.

이처럼 호출 시점에 동적으로 위빙을 한다 하여 런타임 위빙(Runtime Weaving)이라 한다.

  • Weaving : AOP에서 Joinpoint들을 Advice로 감싸는 과정을 Weaving이라고 한다.Weaving 하는 작업을 도와주는 것이 AOP 툴이 하는 역할
  • Spring AOP는 런타임 위빙의 방식을 기반으로 하고 있다.
  • Spring에선 런타임 위빙을 할 수 있도록 상황에 따라 JDK Dynamic Proxy와 CGLIB 방식을 통해 Proxy Bean을 생성을 해준다.

🤷‍♂️ How? 구분

  • 프록시 대상 객체가 하나 이상의 인터페이스를 구현하는 경우 JDK Dynamic Proxy가 사용된다.
    • 대상 유형에 의해 구현된 모든 인터페이스가 프록시된다.
  • 대상 개체가 인터페이스를 구현하지 않으면 CGLIB 프록시가 생성된다.
  • Why?
    • JDK 프록시는 인터페이스 기반이며 인터페이스가 없다는 것은 JDK 프록시가 불가능하다는 것을 의미하기 때문이다.
  • ProxyFactoryBean의 proxyTargetClass속성이 false로 설정된 경우에도 CGLIB 기반 프록시가 생성된다. (이외에 CGLIB으로 프록시 생성하는 설정이 더 있음)

10. JDK Dynamic Proxy

  • JDK 다이내믹 프록시란 java.lang.reflect.Proxy 클래스를 사용함을 의미한다.
    • 리플렉션이란 클래스 자체의 원시적인 코드에 접근할 수 있도록 지원하는 Java API이다.
  • 타깃의 인터페이스를 기준으로 Proxy를 생성해준다.
Object proxy = Proxy.newProxyInstance(loader      // ClassLoader
                                     ,interfaces  // Class<?>[]
                                     ,handler     // InvocationHandler
                                  );
  • JDK 다이내믹 프록시는 제공된 인터페이스의 정보를 통해 인터페이스의 구현체를 프록시 객체로 생성해주고
  • 최종적으로 이 프록시 객체에 InvocationHandler 기능을 확장한 프록시 객체가 반환
  • 필요한 부가기능은 InvocationHandler를 직접 구현하여 제공해줘야 한다.
public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

JDK Dynamic Proxy 동작원리

  1. 호출된 Method 정보를 리플렉션 정보로 변환하여
  2. InvocationHandler.invoke() 메소드에 제공해주고 InvocationHandler는 Method 정보를 통해
  3. 최종적으로 타깃의 메소드를 호출한다.

💡 JDK Dynamic Proxy 구현체는 인터페이스를 상속받아야하고, @Autowired를 통해 생성된 Proxy Bean을 사용하기 위해선 반드시 인터페이스의 타입으로 지정해줘야 한다. 다음의 코드에 JDK Dynamic Proxy 구현체를 생성하려 하면 에러가 발생한다.

@Controller
public class UserController{
  @Autowired
  private MemberService memberService; // <- Runtime Error 발생...
  ...
}

@Service
public class MemberService implements UserService{
  @Override
  public Map<String, Object> findUserId(Map<String, Object> params){
    ...isLogic
    return params;
  }
}

11. CGLIB

  • CGLib은 Code Generator Library의 약자로, 클래스의 바이트코드를 조작하여 Proxy 객체를 생성해주는 라이브러리이다.
  • 런타임에 Extends(상속) 방식을 이용해서 Proxy화 할 메서드를 오버라이딩 한다.
    • Final 메서드는 재정의할 수 없으므로 Advice할 수 없다. (단점)
  • Spring은 CGLib을 사용하여 인터페이스가 아닌 타깃의 클래스에 대해서도 Proxy를 생성해주고 있다.
  • CGLib은 Enhancer라는 클래스를 통해 Proxy를 생성할 수 있다.
Enhancer enhancer = new Enhancer();
         enhancer.setSuperclass(MemberService.class); // 타깃 클래스
         enhancer.setCallback(MethodInterceptor);     // Handler
Object proxy = enhancer.create(); // Proxy 생성
public interface MethodInterceptor extends Callback {
  Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
}

💡 JDK Dynamic Proxy보다 CGLib의 MethodProxy이 예외를 발생시킬 가능성이 적다고 하여 Springboot에서는 CGLIB를 기본 프록시 객체 생성 라이브러리로 채택하게 되었다.


정리

  • JDK Dynamic Proxy는 인터페이스를 구현하여 Proxy를 생성해주고, Spring은 인터페이스가 아닌 클래스를 가지고 Proxy를 생성해주기 위해 CGLIB 방식을 지원하고 있다.
  • CGLIB 클래스를 상속받아 Proxy를 생성해준다는 점과 JDK Dynamic Proxy보다 예외를 덜 발생시킨다는 점에서 Spring Boot에선 기본 Proxy 생성 방법으로 사용하고 있다.
  • 또한, JDK Dynamic Proxy는 Spring AOP의 AOP 기술의 근간이 되는 방식이기 때문에 Spring에서 사용되는 AOP의 기술들은 Proxy 메커니즘을 따르고 있다. 즉 CGLib이든 JDK Dynamic Proxy든 Proxy 메커니즘을 따른다는 점을 인지하자.
  • JDK Dynamic Proxy에서 InvocationHandler, CGLIB 에서 MethodInterceptor는 Spring AOP에서 JoinPoint 개념이다.
  • 그리고 위에서 특정 조건에 의해 필터링 하는 MethodMatcher는 Spring AOP에서 PointCut 개념이다.
  • 마지막으로 Proxy 로직이 실행되는 JDK Dynamic Proxy에 invoke 메서드, CGLIB 에서 Intercept 메서드는 Spring AOP에서 Advice 라는 개념이다.

💡 CGLIB 프록시와 동적 프록시 간의 성능 차이는 거의 없다.

12. 트랜잭션

트랜잭션은 데이터베이스 관리 시스템 또는 유사한 시스템에서 상호작용의 단위이다.

예를 들어보자. 만약 쇼핑몰에서 결제를 하는 사이에 아래와 같은 일이 벌어진다면?

  • 해당 판매자가 상품의 가격을 바꿔버려서, 잘못된 금액이 결제됨
  • 같은 상품을 다른 사람도 구매해서, 상품 재고는 1개인데 2명에게 결제됨
  • 결제가 완료되기 직전에 네트워크가 끊겨서, 돈은 나갔지만 구매완료는 되지 않음

위의 예외적 상황을 막기 위해서, 다음과 같은 조치가 필요할 것이다.

  • 내가 결제중일 때에는 해당 상품의 정보를 바꿀 수 없게 함
  • 내가 결제중일 때에는 해당 상품을 다른 사람이 결제하지 못하게 함
  • 내 구매가 오류로 완료되지 않았다면, 결제된 금액을 환불 처리함

"결제는 다른 사람과 독립적으로 이루어지며, 과정 중에 다른 연산이 끼어들 수 없다.
오류가 생긴 경우 연산을 취소하고 원래대로 되돌린다. 성공할 경우 결과를 반영한다."

 

여기서 결제는 트랜잭션의 예시로 든 것이다. 트랜잭션 역시, 위의 원칙을 바탕으로 한다.

어떤 연산에 트랜잭션이 보장된다면, DB에 의도치 않은 값이 저장되거나 조회되는 것을 막을 수 있다.

13. @Transactional 애노테이션

@Transactional은 클래스나 메서드에 붙여줄 경우, 해당 범위 내 메소드가 트랜잭션이 되도록 보장해준다.

선언적 트랜잭션이라고도 하는데, 직접 객체를 만들 필요 없이 선언만으로도 관리를 용이하게 해주기 때문. 

SpringBoot에서는 선언적 트랜잭션에 필요한 여러 설정이 이미 되어있어 더 쉽게 사용할 수 있다.

 

도서를 Repository를 통해 가져오는 서비스 레이어의 코드

@RequiredArgsConstructor
@Service
public class BookService {
    private final BookRepository bookRepository;

    @Transactional(readOnly = true)
    public List<BookResponseServiceDto> getBooks() {
        return bookRepository.findAll()
                .stream()
                .map(BookResponseServiceDto::new)
                .collect(Collectors.toList());
    }

    @Transactional(readOnly = true)
    public BookResponseServiceDto getBook(Long bookId) {
        final Book book = bookRepository.findById(bookId)
                .orElseThrow(() -> new BookNotFoundException(bookId));

        return new BookResponseServiceDto(book);
    }
}

 

getBook() 메소드가 실행될 경우 해당 메서드는 아래의 속성을 가진다.

  • 연산이 고립되어, 다른 연산과의 혼선으로 인해 잘못된 값을 가져오는 경우가 방지된다.
  • 연산의 원자성이 보장되어, 연산이 도중에 실패할 경우 변경사항이 Commit되지 않는다.

위의 속성이 보장되기 때문에 해당 메소드를 실행하는 도중 메서드 값을 수정/삭제하려는 시도가 들어와도 값의 신뢰성이 보장된다. 또한, 연산 도중 오류가 발생해도 rollBack해서 DB에 해당 결과가 반영되지 않도록 할 수 있다. 


14. @Transactional의 작동 원리와 흐름

@Transactional이 붙은 메소드를 호출할 경우, 코드에는 어떤 일이 벌어질까?

@Transactional이 클래스나 메소드에 붙을 때 Spring은 해당 메소드에 대한 프록시를 만든다.

트랜잭션의 경우, 트랜잭션의 시작과 연산 종료시의 커밋 과정이 필요하므로, 프록시를 생성해 해당 메소드의 앞뒤에 트랜잭션의 시작과 끝을 추가하는 것이다.

 

또한, 스프링 컨테이너는 트랜잭션 범위의 영속성 컨텍스트 전략을 기본으로 사용한다.

영속성 컨텍스트 엔티티를 영구 저장하는 환경이라는 뜻이다. 

영속성 컨텍스트 전략이란 트랜잭션 범위와 영속성 컨텍스트의 생존 범위가 같다는 뜻으로 트랜잭션이 시작할 때 영속성 컨텍스트를 생성하고 트랜잭션이 끝날 때 컨텍스트를 종료한다.

 

서비스 클래스에서 @Transactional을 사용할 경우, 해당 코드 내의 메소드를 호출할 때 영속성 컨텍스트가 생긴다는 뜻이다. 영속성 컨텍스트는 트랜잭션 AOP가 트랜잭션을 시작할 때 생겨나고, 메소드가 종료되어 트랜잭션 AOP가 트랜잭션을 커밋할 경우 영속성 컨텍스트가 flush되면서 해당 내용이 반영된다. 이후 영속성 컨텍스트 역시 종료되는 것이다.

flush란 영속성 컨텍스트 내용이 DB에 반영되는 것이다.

이러한 방식으로 영속성 컨텍스트를 관리해 주기 때문에, @Transactional을 쓸 경우 트랜잭션의 원칙을 정확히 지킬 수 있다.

 

15. 트랜잭션 사용시 주의해야할 점

 

Spring은 기본적으로 코드 삽입 방법중 프록시 객체 사용이 선택되고, 그렇기에 interface가 반드시 필요하다.

따라서 코드를 작성했을 때 우리가 만든 메서드를 한번 감싸서, 메서드 위 아래로 코드를 삽입한다.

 

그렇기에 같은 클래스 안에 있는 메소드를 호출했을 때, interface를 사용한 객체에서 프록시로 감싸진 코드가 호출되는 것이 아니라, 내부의 코드가 수행되기 때문에 @Transactional 어노테이션 기능은 수행되지 않는다.

 

이를 해결하기 위해서는 @Transactional 메서드를 내부적으로 사용하지 않거나, 의존성 주입을 이용하여 Proxy 인스턴스를 자체적으로 가져와 사용하는 방법이 있다.

@Service
public class BooksImpl implements Books {
  @Autowired
  private Books self;
  
  public void addBooks(List<String> bookNames) {
    bookNames.forEach(bookName -> self.addBook(bookName)); // this 가 아닌 변수 self 로
  }
  
  @Transactional
  public void addBook(String bookName) {
    Book book = new Book(bookName);
    bookRepository.save(book);
    book.setFlag(true);
  }
}


출처: https://mommoo.tistory.com/92 [개발자로 홀로 서기]

 

16. 트랜잭션 관리방법의 종류

 프로그램에 의한 트랜잭션 관리

코드에서 직접적으로 트랜잭션 매니저를 통해서 트랜잭션 캐시, 커밋, 롤백등을 수행하는 방법이다. 내부구현을 외부에 노출시키지 않는다는 장점이 있지만, 가독성을 떨어트리고 실수할 가능성이 많다.

 

선언적 트랜잭션 관리

1. 어노테이션으로 트랜잭션 선언

  - @Transactional 선언

2. AOP설정으로 트랜잭션 선언

3. Spring 트랜잭션 속성

propagation 트랜잭션 개시할지 등 전파행위에 관한 속성.
isolation 트랜잭션 격리레벨에 관한 속성으로 기본값은 Default레벨이며 실제 사용하는 데이터베이스(JDBC) 등의 기본값을 따릅니다.
readOnly 트랜잭션을 읽기전용으로 지정하는 속성. 최적화 관점에서 지원되는 프로터티이므로 현재 트랜잭션 상태에따라 다르게 동작할 수 있습니다.
timeout 트랜잭션의 타임아웃(초단위)을 지정하는 속성으로 지정하지 않을 경우 사용하는 트랜잭션 시스템의 타임아웃을 따릅니다.
rollbackFor Checked 예외 발생시에 롤백을 수행할 예외를 지정하는 속성.
rollbackForClassName rollbackFor와 동일하지만 문자열로 클래스명을 지정하는 속성.
noRollbackFor Spring의 트랜잭션은 기본적으로 Runtime예외만 롤백처리를 수행하지만 Runtime예외중 특정 예외는 롤백을 수행하지 않아야 할 경우 사용하는 속성.
noRollbackForClassName noRollbackFor와 동일하지만 문자열로 클래스명을 지정하는 속성.

4. 전파행위 : 트랜잭션을 개시할지 혹은 기존 트랜잭션을 이용할지 등 트랜잭션 경계를 설정할때 이용하는 속성이다.

설정 가능한 전파행위 목록

MANDATORY 트랜잭션이 존재할 경우 해당 트랜잭션을 이용하며 존재하지 않을 경우 예외발생.
NESTED 트랜잭션이 존재할 경우 중첩된 트랜잭션을 개시하고 존재하지 않을 경우는 REQUIRED와 동일하게 동작.
NEVER 트랜잭션이 존재할 경우 예외발생.
NOT_SUPPORTED 트랜잭션이 존재할 경우 중단(Suspend)해서 트랜잭션을 이용하지 않음.
REQUIRED 트랜잭션이 존재하는 경우 해당 트랜잭션을 그대로 하며 개시된 트랜잭션이 없는 경우 트랜잭션 개시.
REQUIRES_NEW 항상 신규트랜잭션을 개시함. 트랜잭션이 존재하는 경우 해당 트랜잭션을 중단하고 새로운 트랜잭션 개시.
SUPPORTS 트랜잭션이 존재할 경우 해당 트랜잭션을 이용하고 존재하지 않을 경우는 트랜잭션을 이용하지 않음.

 

References

스프링 면접 질문
스프링 aop 공식 문서
스택오버플로우 joinpoint and pointcut diffrence
스프링 aop 원리 jdk dynamic proxy(reflection기반) & cglib proxy(상속기반)
스프링 aop proxy-target-class 속성 설정
스프링 aop CGLIB PROXY 방식 동작 이유
Spring Boot는 왜 CGLIB 방식을 디폴트 전략으로 선택했을까?
스프링 aop 개념과 트랜잭션 처리
AOP 활용 예제
Spring @Configurable
Low Time Waver 적용기
DDD - Domain Driven Design

Spring AOP의 원리 - CGlib vs Dynamic Proxy — 천천히 올바르게 (tistory.com)

JDK Dynamic Proxy와 CGLIB의 차이점은 무엇일까? | Moon`s Development Blog (gmoon92.github.io)

CGLIB를 이용한 프록시 객체 만들기 :: 자바캔(Java Can Do IT) (tistory.com)

https://docs.spring.io/spring-framework/docs/4.0.1.RELEASE/spring-framework-reference/htmlsingle/#aop-understanding-aop-proxies

@Transactional 사용시 주의점

트랜잭션 관리의 이해

'스터디정리방' 카테고리의 다른 글

[기록] 220126  (0) 2022.02.03
[기록] 220119  (0) 2022.01.25
[기록] 220105  (0) 2022.01.09
[기록] 211229  (0) 2022.01.02
[기록] 211222  (0) 2021.12.26

댓글