Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7주차](양재승, 김수환, 권용현) #9

Open
sunwootest opened this issue Aug 8, 2023 · 2 comments
Open

[7주차](양재승, 김수환, 권용현) #9

sunwootest opened this issue Aug 8, 2023 · 2 comments
Assignees
Labels

Comments

@sunwootest
Copy link
Collaborator

sunwootest commented Aug 8, 2023

  • 7주차
    • 6.5 ~ 6.6장
    • 6.7 ~ 6.9장
    • 7.1 ~ 7.4장
@sheepseung
Copy link

sheepseung commented Aug 13, 2023

chapter 6. AOP

  • 스프링 AOP

    자동 프록시 생성

    프록시 팩토리 빈 방식의 접근 방법의 한계(문제점)

    • 부가 기능을 타깃 오브젝트마다 새로 만들어야 함 ⇒ ProxyFactoryBean의 어드바이스를 통해 해결
    • 타깃 오브젝트마다 거의 비슷한 내용의 ProxyFactoryBean 빈 설정정보를 추가해야 됨(코드 중복)

    중복 문제의 접근 방법(해결 방안 제시)

    그동안 배웠던 중복 문제의 해결 방법으로는 크게 바뀌는 부분과 바뀌지 않는 부분을 구분하여 분리하고 전략 패턴과 DI를 적용하여 해결했다.

    • JDBC API를 사용 ⇒ 템플릿과 콜백, 클라이언트로 나눔
    • 프록시 클래스 코드 ⇒ 다이내믹 프록시(어드바이스와 포인트 컷)

    위와 같은 방법으로 반복적인 ProxyFactoryBean 설정 문제를 자동 등록 기법으로 해결할 수 있을까? 또는 프록시가 자동으로 빈으로 생성되게 할 수 있을까?

    빈 후기처리를 이용한 자동 프록시 생성기(해결 방안 : DefaultAdvisorAutoProxyCreator 사용)

    스프링은 컨테이너로서 제공하는 기능 중에서 변하지 않는 핵심적인 부분외에는 대부분 확장할 수 있도록 확장 포인트를 제공한다.

    그 중 위 문제를 해결할 확장 포인트는 BeanPostProcessor 인터페이스를 구현해서 만든 빈 후기처리기다. 빈 후기 처리기는 스프링 빈 오브젝트로 만들어진 후 빈 오브젝트를 다시 가공할 수 있게 해준다.

    빈 후기처리기 중 살펴 볼 DefaultAdvisorAutoProxyCreator는 어드바이저를 이용한 자동 프록시 생성기다. 빈 후기 처리기 자체를 빈으로 등록하기만 빈 오브젝트가 생성될 때마다 빈 후기처리기에 보내서 후처리 작업을 요청한다.

    • 빈 오브젝트의 프로퍼티를 강제로 수정/초기화 가능
    • 만들어진 빈 오브젝트를 바꿔치기 가능
    • 설정을 참고해서 만든 오브젝트가 아닌 다른 오브젝트를 빈으로 등록 가능

    ⇒ 스프링이 생성하는 빈 오브젝트의 일부를 프록시로 포장하고, 프록시를 빈으로 대신 등록 가능

    빈 후기처리기를 이용한 프록시 자동생성

    빈 후기처리기를 이용한 프록시 자동생성

    DefaultAdvisorAutoProxyCreator 는 등록된 모든 어드바이저의 포인트컷을 이용해 전달받은 빈이 프록시 적용 대상인지 확인하고, 맞다면 내장된 프록시 생성기에게 현재 빈에 대한 프록시를 만들게 하고 만들어진 프록시에 어드바이저를 연결한다. ⇒ 문제 해결(프록시 빈 자동 등록)

    확장된 포인트컷

    포인트컷은 클래스 필터와 메소드 매처 두 가지 기능을 갖고 있다.

    public interface Pointcut{
    	ClassFilter getClassFilter();
    	MethodMatcher getMethodMatcher();
    }

    즉 포인트컷은 먼저 프록시를 적용할 클래스인지 판단하고, 적용 대상이라면 어드바이스를 적용할 메소드인지 확인하는 식으로 동작한다.

    ProxyFactoryBean에서는 굳이 클래스 레벨의 필터는 필요 없었지만,DefaultAdvisorAutoProxyCreator를 사용하기 위해서는 클래스와 메소드 모두 선정 알고리즘을 모두 갖고 있는 포인트컷이 필요하다.


    포인트컷 표현식 이용

    포인트컷 표현식의 필요성

    앞에서 사용했던 포인트컷은 메소드의 이름과 클래스의 이름 패턴을 각각 클래스 필터와 메소드 매처 오브젝트로 비교해서 선정하는 방식이었다.

    클래스와 메타정보를 리플렉션 API를 통해서 제공 받을 수 있기 때문에 이보다 더 복잡하고 세밀한 기준을 적용하려 해도 구현할 수 있다. 하지만 리플렉션 API를 이용하면 다음과 같은 문제점이 있다.

    • 코드 작성이 어려움
    • 조건이 달라질 때마다 포인트컷 구현 코드를 수정해야됨

    포인트컷 표현식

    포인트컷은 클래스 필터, 메소드 매처 두 가지를 각각 제공해야 한다.

    AspectJExpressionPointcut은 클래스와 메소드의 선정 알고리즘을 포인트컷 표현식을 이용해 한 번에 지정할 수 있게 해준다. 또한 간단한 문자열로 복잡한 선정조건을 쉽게 만들어낼 수 있는 표현식을 지원한다.

    AspectJ포인트컷 표현식은 포인트컷 지시자를 이용해 작성한다. 가장 대표적으로 사용되는 것이 excution() 이다.

    스크린샷 2023-08-12 오후 5.58.27.png

    [ ] : 옵션항목(생략 가능)

    : OR 조건

    사용 예)

    excution(int minus(int, int))

    ⇒ int 타입의 리턴 값, minus라는 메소드 이름, 두 개의 int 파라미터를 가진 모든 메소드를 선정하는 포인트컷 표현식


    AOP란 무엇인가?

    비지니스 로직을 담은 UserService에 트랜잭션을 적용해온 과정을 정리해보자

    • 트랜잭션 추상화

      ⚠️문제점 : 특정 트랜잭션 기술에 종속됨

      • 인터페이스와 DI를 통해 무엇을 하는지 남기고, 그 작업을 어떻게 하는지 구체적인 구현은 분리
    • 프록시와 데코레이터 패턴

      ⚠️문제점 : 여전히 비지니스계층에 트랜잭션 코드가 남아있음

      • 트랜잭션을 처리하는 코드는 데코레이터에 담아서 클라이언트와 비지니스 로직을 담은 타깃 클래스 사이에 존재하도록 구현
      • 클라이언트는 데코레이터를 거쳐서 타깃에 접근
    • 다이나믹 프록시와 프록시 팩토리 빈

      ⚠️문제점 : 프록시 클래스를 만들 때 인터페이스의 모든 메소드마다 트랜잭션 코드가 들어감

      • 프록시 클래스 없이도 프록시 오브젝트를 런타임 시에 만들어주는 JDK 다이나믹 프록시 기술 적용

        스크린샷 2023-08-13 오후 5.02.02.png

      ⚠️문제점 : 동일한 기능의 프록시를 여러 오브젝트에 적용할 경우 오브젝트 단위의 중복은 해결 못함

      • 스프링의 프록시 팩토리 빈을 이용해서 다이나믹 프록시 생성 방법에 DI 도입

        스크린샷 2023-08-13 오후 5.02.55.png

    • 자동 프록시 생성 방법과 포인트 컷

      ⚠️문제점 : 트랜잭션 적용 대상이 되는 빈마다 일일이 프록시 팩토리 빈을 설정해야됨

      • 스프링 컨테이너의 빈 생성 후처리 기법 활용

      • 트랜잭션 부가기능 적용 대상에 대한 정보는 포인트컷에 독립적으로 분리

        스크린샷 2023-08-13 오후 5.03.33.png

    • 부가기능 모듈화

      ⚠️문제점 : 관심사가 같은 부가 기능 모듈을 모을 필요성

      • 어드바이스와 포인트컷을 결합한 어드바이저 모듈로 관리

        스크린샷 2023-08-13 오후 5.04.03.png

    AOP : Aspect 지향 프로그래밍

    독립적인 모듈화가 불가능한 트랜잭션 경계설정과 같은 부가기능을 모듈화하는 방법은 기존의 객체지향 설계 패러다임과는 구분되는 새로운 특징이 있다고 판단하여 **애스펙트(aspect)**라는 새로운 이름으로 불리게 되었다.

    애스펙트는 **어드바이스(부가될 기능을 정의한 코드)**와 **포인트컷(어디에 적용할 지 결정)**을 함께 갖고 있다. 어드바이저는 아주 단순한 형태의 애스펙트라고 볼 수 있다.

    애스펙트는 애플리케이션을 구성하는 한 가지 측면 이라고 생각할 수 있다.

    스크린샷 2023-08-13 오후 4.01.59.png

    핵심기능은 순수하게 그 기능을 담은 코드로만 존재하고, 독립적으로 살펴볼 수 있도록 구분된 면에 존재하게 된 것이다.

    이렇게 애플리케이션의 핵심적인 기능에서 부가적인 기능을 분리해서 애스펙트라는 모듈로 설계하는 방법을 애스펙트 지향 프로그래밍(Aspect Oriented Programming), 줄여서 AOP라고 한다. AOP는 OOP를 돕는 보조적인 기술이다.

    AOP 적용기술

    프록시를 이용한 AOP

    • 프록시로 만들어서 DI로 연결된 빈 사이에 적용해 타깃의 메소드 호출 과정에 참여해서 부가기능을 제공
    • 독립적으로 개발한 부가기능 모듈을 다양한 타깃 오브젝트의 메소드에 다이내믹하게 적용해주기 위해 가장 중요한 역할을 하는 것이 프록시
    • 스프링 AOP는 프록시 방식의 AOP라고 할 수 있음

    바이트코드 생성과 조작을 통한 AOP

    • AspectJ는 프록시처럼 간접적인 방법이 아니라, 타깃 오브젝트를 뜯어 고쳐서 부가 기능을 직접 넣어주는 방법을 사용
    • 컴파일된 타깃의 클래스 파일 자체를 수정하거나 클래스가 JVM에 로딩되는 시점을 가로채서 바이트코드를 조작하는 복잡한 방법을 사용

    ⇒ 스프링과 같은 컨테이너가 사용되지 않는 환경에서도 손쉽게 AOP의 적용이 가능함

    ⇒ 오브젝트의 생성, 필드 값의 조회와 조작, 스태틱 초기화 등의 다양한 작업에 부가 기능을 부여해 줄 수 있음

    but 일반적인 AOP를 적용하는 데는 프록시 방식의 스프링 AOP로도 충분함. 그 이상의 기능이 필요할 경우에만 AspectJ 사용

  • 트랜잭션 속성

트랜잭션 4가지 속성

트랜잭션이라고 모두 같은 방식으로 동작하는 것이 아니다. DefaultTransactionDefinition이 구현하고 있는 TransactionDefinition 인터페이스는 트랜잭션 동작방식에 영향을 줄 수 있는 4가지 속성을 정의하고 있다.

  • 트랜잭션

    전파트랜잭션의 경계에서 이미 진행 중인 트랜잭션이 있을 때 또는 없을 때 어떻게 동작할 것인가를 결정하는 방식

    • PROPAGATION_REQUIRED : 진행 중인 트랜잭션이 없으면 새로 시작하고, 있으면 이에 참여함
    • PROPAGATION_REQUIRES_NEW : 항상 새로운 트랜잭션을 시작함
    • PROPAGATION_NOT_SUPPORTED : 트랜잭션 없이 동작할 수 있음. 진행중인 트랜잭션이 있어도 무시
  • 격리수준

    모든 DB 트랜잭션은 격리수준(isolation level)을 갖고 있다. 격리수준은 기본적으로 DB에 설정되어 있지만 JDBC 드라이버나 DataSource 등에서 재설정할 수 있고, 필요하다면 트랜잭션 단위로 격리수준을 조정할 수 있다.

  • 제한시간

    트랜잭션을 수행하는 제한시간(timeout)을 설정할 수 있다. DefaultTransactionDefinition의 기본 설정은 제한시간이 없다.

  • 읽기전용

    읽기전용(read only)으로 설정해두면 트랜잭션 내에서 데이터를 조작하는 시도를 막아줄 수 있다. 또한 데이터 액세스 기술에 따라서 성능이 향상될 수도 있다.

포인트컷과 트랜잭션 속성의 적용 전략

프록시 방식 AOP는 같은 타깃 오브젝트 내의 메소드를 호출할 때는 적용되지 않는다

프록시 방식의 AOP에서는 프록시를 통한 부가 기능의 적용은 클라이언트로부터 호출이 일어날 때만 가능하다. (여기서 클라이언트는 인터페이스를 통해 타깃 오브젝트를 사용하는 다른 모든 오브젝트)

반대로 타깃 오브젝트가 자기 자신의 메소드를 호출할 때는 프록시를 통한 부가기능의 적용이 일어나지 않는다. 따라서 같은 오브젝트 안에서의 호출은 새로운 트랜잭션 속성을 부여하지 못한다는 사실을 의식하고 개발할 필요가 있다.

타깃 안에서의 호출에는 프록시가 적용되지 않는 문제를 해결할 수 있는 방법은 두 가지가 있다.

  1. 스프링 API를 이용해 프록시 오브젝트에 대한 레퍼런스를 가져온 뒤에 같은 오브젝트의 메소드 호출도 프록시를 이용하도록 강제하는 방법(별로 추천되지 않음)
  2. AspectJ와 같은 타깃의 바이트코드를 직접 조작하는 방식의 AOP 기술을 적용하는 방법

트랜잭션 경계설정의 일원화

트랜잭션 경계설정의 부가기능을 여러 계층에서 중구난방으로 적용하는 건 좋지 않다. 일반적으로 특정 계층의 경계를 트랜잭션 경계와 일치시키는 것이 바람직하다.

비즈니스 로직을 담고 있는 서비스 계층 오브젝트의 메소드가 트랜잭션 경계를 부여하기에 가장 적절한 대상이다.

@genius00hwan
Copy link

genius00hwan commented Aug 13, 2023

[6.7] 트랜잭션 어노테이션과 포인트컷

트랜잭션?

하나의 작업으로 취급하기 위해 여러 작업을 하나로 묶은 것

포인트컷?

일종의 필터 역할로, 어떤 메소드에 어드바이스를 적용할지 결정하는 기준을 정의하는 역할을 한다.

어드바이스?

어드바이스는 핵심 비즈니스 로직에 적용되는 부가 기능 사항을 정의하여 등록한 빈 객체

@Trasactional

포인트컷과 트랜잭션 속성을 이용해 트랜잭션을 일괄적으로 적용하는 방식은 세밀하게 튜닝된 트랜잭션 속성의 경우 포인트컷과 트랜잭션 속성 사용으로는 적합하지 않다.

  • 설정 파일에서 속성을 부여하는 대신,
    직접 타킷에 속성정보를 가진 어노테이션을 지정해 세밀하게 트랜잭션 속성을 적용할 수 있다.
  • @transactional 타겟은 메소드와 타입
  • 메소드, 클래스, 인터페이스에 사용할 수 있다.
  • @transactional이 부여된 모든 오브젝트를 자동으로 타겟 객체로 인식한다.
  • TransactionAttributeSourcePointcut을 사용하여 스스로 표현식과 같은 선정 기준을 갖지 않게 할 수 있다.
  • @transactional이 부여된 메소드 및 객체를 모두 찾아 포인트컷 선정 결과로 반환한다.
  • 포인트컷을 통과시켜 트랜잭션 속성을 이용해 선정 메소드 및 타입을 필터링

트랜잭션 속성과 포인트컷

  • 메소드 이름 패턴을 통해 부여되는 일괄적인 트랜잭션 속성정보 대신 @transaction 엘리먼트에서 트랜잭션 속성을 가져오는AnnotationTransactionAttributeSource를 사용할 수 있다.
  • @transactional을 사용하면 메소드 및 타입마다 다르게 설정할 수 있어 매우 유연한 트랜잭션 속성 설정을 할 수 있으며 포인트컷과 트랜잭션 속성을 어노테이션 하나로 지정할 수 있다.
  • 메소드마다 @transaction을 넣어 유연한 속성 제어는 가능하겠지만, 코드가 지저분해지고 동일한 속성 정보를 가진 애노테이션을 반복적으로 사용하게 된다.

4 단계 대체정책

  • 타겟 메소드 (Target Method):
    @transactional 어노테이션을 사용하여 트랜잭션을 적용할 메소드를 지정한다.
    이 메소드에서 예외가 발생하면 예외 처리와 롤백을 관리할 수 있다.

  • 타겟 클래스 (Target Class):
    @transactional 어노테이션을 사용하여 트랜잭션을 적용하는 대상 클래스를 지정한다.
    트랜잭션을 관리하고자 하는 비즈니스 로직이 포함된 클래스를 의미한다.

  • 선언 메소드 (Advisor Method):
    @transactional 어노테이션의 fallback 옵션에서 지정한 메소드를 호출하는 역할을 수행한다.
    예외가 발생한 경우에 호출되며, 해당 메소드가 적절한 처리를 수행할 수 있도록 한다.

  • 선언 타입 (Advisor Type):
    선언 메소드가 속한 클래스의 타입을 지정한다.
    @aspect 어노테이션을 사용하여 Aspect 클래스를 생성하고, 해당 클래스 내에 선언 메소드를 정의하여 예외 상황에 대한 처리를 구현한다.

	[1] // 선언 타입
public interface Service { 
	[2] // 선언 메소드
	void method1(); 
	[3]
	void method2();
}

	[4] // 타겟 클래스
public class Servicelmpl implements Service {
	[5] // 타겟 메소드
	public void method1() (
	[6]
	public void method2() {
}
  • 타깃 클래스, 선언 타입에만 @transactional을 붙이면 해당 클래스 및 타입에 있는 메소드는 공통적으로 클래스 및 선언 레벨에 있는 @transactional 엘리먼트를 공유한다.
  • 인터페이스에 @transactional 을 두면 구현 클래스가 바뀌더라도 트랜잭션 속성을 유지할 수 있다는 장점이 있다.
  • 프록시 방식이 아닌 트랜잭션을 적용하면 인터페이스의 @transactional은 무시되기 때문에, 타깃 클래스에 @transactional을 넣는 방식을 권장한다.

@transactional 적용

  • @transactional 사용은 포인트컷과 트랜잭션 속성 지정하는 것보다 단순하고 직관적이기 때문에 @transactional을 주로 사용한다.
  • @transactional을 사용하지 않아도 컴파일에 아무런 문제가 없고, 롤백이 발생하고서야 빼 먹은 사실을 알 수 있다.
  • 일부 데이터 접근 기술은 트랜잭션이 시작되지 않으면 DAO에서 예외가 발생하나 JDBC의 경우 트랜잭션 없이도 DAO를 동작시킬 수 있다.
@Transactional
public interface UserService {
    void add(User user);
    void deleteAll();
    void update(User user1);
    void upgradeLevels();
    // 메소드 레벨에 @Trasactional이 없으므로 타입레벨에 부여된 디폴트 속성이 부여된다.

    @Transactional(readOnly = true)
    User get(String id);

    @Transactional(readOnly = true)
    List<User> getAll();
    // 메소드 레벨에 부여됐기 때문에 메소드 마다 반복된다.
}

[6.8] 트랜잭션 지원 테스트

선언적 트랜잭션과 트랜잭션 전파 속성

트랜잭션 전파속성?

  • 트랜잭션 전파 속성은 여러 트랜잭션 메소드들이 서로 상호작용할 때 어떻게 동작할지를 정의하는 설정
  • 트랜잭션 메소드에서 다른 트랜잭션 메소드를 호출할 때, 어떤 방식으로 트랜잭션이 전파되고 관리될지 결정

REQUIRED로 전파속성 지정

  • 현재 실행 중인 트랜잭션이 없을 경우, 새로운 트랜잭션을 시작
  • 현재 실행 중인 트랜잭션이 있을 경우, 해당 트랜잭션에 참여하여 작업을 수행

add 메소드에 트랜잭션 전파속성을 지정

  • 다른 메소드에서 만들어진 트랜잭션의 경계에 포함되게 한다.
  • 덕분에 불필요한 중복 (add()메소드가 하나만 존재하면 됨)을 피할 수 있다.

선언적 트랜잭션 : AOP를 이용해 코드 외부에서 트랜잭션 기능을 부여해주는 방법
프로그램에 의한 트랜잭션 : TransactionTemplate나 개별 데이터 트랜잭션 API를 사용해 직접 코드 안에서 사용하는 방법

특수한 상황을 제외하고, 선언적 트랜잭션 방법을 사용하는 것을 추천함

트랜잭션 동기화

  • AOP 덕에 트랜잭션 부가기능을 간단히 애플리케이션 전반에 적용할 수 있다.
  • 트랜잭션 매니저를 (PlatformTransactionManager 인터페이스를 구현) 통해 구체적인 트랜잭션 기술 종류에 상관없이 일관된 트랜잭션 제어를 할 수 있다.
  • TransactionSynchronization을 사용하는 트랜잭션 동기화는 같은 DB Connection을 공유하게 한다.

트랜잭션 동기화 기술은 같은 DB Connection을 공유하는 특성을 이용하여 트랜잭션 전파 속성에 따라 참여할 수 있도록 한다.

트랜잭션 매니저를 이용한 트랜잭션 제어

트랜잭션 매니저를 사용해 테스트 데이터를 조작하는 등의 작업을 수행할 수 있다.
트랜잭션 매니저를 사용하면 테스트에서 트랜잭션의 시작, 커밋, 롤백 등을 조작하여 테스트 환경을 조정할 수 있다.

public class UserServiceTest {
    ...
    @Test
    public void transactionSync(){
        DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition();
        TransactionStatus txStatus = transactionManager.getTransaction(txDefinition);

        userService.deleteAll();
        userService.add(users.get(0));
        userService.add(users.get(1));

        transactionManager.commit(txStatus);
    }
}

실행되는 트랜잭션은 3개

  • 각 메소드마다 트랜잭션이 독립적으로 작용함
  • 트랜잭션 매니저를 사용하여 세 개의 메소드를 하나로 묶을 수 있다.
  • 기존의 트랜잭션이 없으니 새로운 트랜잭션을 실행하고 트랜잭션 정보를 반환한다.
  • 만들어진 트랜잭션을 다른 곳에서도 사용할 수 있게 동기화 한다.

트랜잭션 동기화 검증

스프링 트랜잭션 추상화가 제공하는 트랜잭션 동기화 기술과 트랜잭션 전파 속성 덕에 테스트도 트랜잭션으로 묶을 수 있다.
이를 통해 효과적인 테스트를 만들 수 있다.

public class UserServiceTest {
    ...
    @Test
    public void transactionSync(){
        userDao.deleteAll(); // 초기화
        assertThat(userDao.getCount(), is(0));

        DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition();
        TransactionStatus txStatus = transactionManager.getTransaction(txDefinition);

        userService.add(users.get(0));
        userService.add(users.get(1));
        assertThat(userDao.getCount(), is(2));

        transactionManager.rollback(txStatus);

        assertThat(userDao.getCount(), is(0));
    }
}

롤백이 잘 되는 지 테스트 할 수 있다.

  • 테스트 코드에서 트랜잭션을 시작해 놓으면 직접 호출하는 DAO 메소드도 하나의 트랜잭션으로 묶을 수 있다.
  • 트랜잭션 결과나 상태를 조작하면서 테스트하는 것도 가능하다.
  • 트랜잭션 속성에 따라 여러 메소드를 조합해 어떤 결과가 나오는 지도 미리 검증 가능하다.

롤백 테스트

테스트가 끝나면 롤백 하는 테스트

  • 복잡한 데이터를 바탕으로 동작하는 통합 테스트에서는 DB의 상태가 중요하다.
  • 테스트 수행으로 데이터 추가, 삭제 등이 발생 해 다른 테스트에 영향을 줄 수 있다.
  • 테스트 수행 시 마지막에 롤백을 해서 테스트 실행 전 상태로 되돌릴 수 있다.
public class UserServiceTest {
    ...
    @Test
    public void transactionSync() {
        DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition();
        TransactionStatus txStatus = transactionManager.getTransaction(txDefinition);
        try {
            userService.deleteAll();
            userService.add(users.get(0));
            userService.add(users.get(1));
        } finally {
            transactionManager.rollback(txStatus);
        }
    }
}
  • 롤백테스트는 테스트를 진행하며 조작한 데이터를 모두 롤백 해준다.
  • 여러 개발자가 하나의 DB를 공유할 수 있게 해준다.
  • 적절한 격리 수준만 보장하면 동시에 여러 개의 통합 테스트를 진행할 수 있다.

테스트에서 사용하는 트랜잭션 어노테이션

스프링은 어노테이션을 통해 테스트를 편리하게 만들 수 있는 여러 기능을 제공한다.

** @transactional **

  • @transactional 을 사용해 트랜잭션 경계설정을 자동으로 설정 할 수 있다.
    ( 디폴트는 REQUIRED )
  • 테스트에서 진행하는 작업을 하나의 트랜잭션으로 묶어 줄 수 있다.

@Rollback

  • @transactional 을 사용하는 애플리케이션 클래스와 테스트 클래스의 디폴트 속성은 동일
  • 테스트 클래스는 테스트 수행 후 자동으로 롤백된다.
  • 테스트 수행 후 롤백을 원하지 않을 때 @Rollback 사용할 수 있다.
    ( @Rollback의 기본값은 true이기 때문에 false로 선언해야 롤백 되지 않는다. )

@TransactionConfiguration

  • @Rollback은 메소드 레벨에서만 사용 가능하다.
  • 클래스 레벨에서 적용하기 위해서는 @TransactionConfiguration을 사용해야 한다.
  • @Rollback과 같이 false로 선언해야 롤백되지 않는다.
  • @TransactionConfiguration을 사용한 클래스에서, 특정 메소드는 롤백하고 싶다면 해당 메소드에만 @Rollback 사용하면 된다.
    ( 클래스보다 메소드가 우선순위가 더 높다. )
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/applicationContext.xml")
@Transactional
@TransactionConfiguration(defaultRollback = false)
public class UserServiceTest {
    @Test
    @Rollback //롤백
    public void add(){}
}

6.8.10 @NotTransactional과 Propagation.NEVER

  • @transactional이 있는 클래스에서 특정 메소드를 트랜잭션 동작하고 싶지 않을 때 사용
@Transactional(propagation = Propagation.NEVER)

효과적인 DB 테스트

  • 테스트 내에서 트랜잭션을 제어할 수 있는 네 가지 애노테이션을 활용해 편리하게 통합 테스트 코드를 만들 수 있다.
  • DB가 사용되는 통합 테스트는 롤백 테스트로 만드는 것을 권장 한다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants