Java Spring - AOP

김재현·2022년 7월 29일
0

Programmers

목록 보기
9/28

AOP (Aspect Orient Programming, 관점 지향 프로그래밍)

  • 하나의 프로그램 패러다임
  • aspect는 '관점'보다는 기능이나 부가기능, 혹은 관심(concern)이라고 생각하는 것이 더 유사한 해석인 것 같다.
  • 부가기능이 여러군데에 흩어져 있다면 리소스가 많이 할애된다. 수정할 것이라도 하나 있다면 일일이 찾아서 수정해줘야 하기 때문.
    어떤 로직을 기준으로 핵심적인 기능, 부가적인 기능으로 나누어서 보고 그 기능을 기준으로 각각 모듈화하겠다는 것.
    핵심기능은 비즈니스 로직에 집중하고 부가기능은 별도로 분리하여 부가기능을 담당하는 모듈에서 자체적으로 관리하고 발전시킬 수 있도록 한다.

  • 각 레이어에 있는 것들 중에서 공통되는, 로깅, 보안, 트랜잭션 메니지먼트과 같이 공통으로 고민해야하는 부분이 존재한다.
    이것을 매번 중복적으로 코드를 일일이 쓰는 것이 아니라, 횡단으로 각각의 레이어를 걸쳐 적용시켜주는 부가기능이 있는데, 이것들을 Cross Cutting Concerns이라고 부른다.
  • AOP는 각 레이어의 핵심기능, 로직과 부가기능을 분리해서 프로그램을 만들 수 있게 해주는 방식으로 핵심 비즈니스로직과 부가기능을 분리시켜주되, 쉽게 부가기능을 추가할 수 있게 해주는 것이 핵심.

종류

  • compile 시점 .
    AspectJ등 프레임워크를 사용한다.
    소스코드가 컴파일되기 전에 공통구현코드(부가기능)을 소스에 삽입하는 방식.

  • class loading 시점.
    클래스 로딩할 때 바이트 코드에 부가기능을 삽입한다.

  • runtime 시점.
    만들어진 객체의 proxy를 생성, 프록시 객체가 공통기능 등을 호출.
    타깃이 하나 이상의 인터페이스를 구현하고 있는 클래스라면 JDK Dynamic Proxy, 인터페이스 구현하지 않았다면 CGLIB.

    • JDK Dynamic Proxy. 인터페이스 기반.
      타깃의 인터페이스를 자체적인 검증 로직을 통해 ProxyFactory에 의해 타깃의 인터페이스를 상속한 Proxy 객체 생성
    • CGLIB Proxy. 클래스 기반.
      클래스의 바이트 코드를 조작하여 Proxy 객체를 생성.
      둘의 차이점

JDK Proxy

  • Proxy.newProxyInstance()를 이용해 생성 가능.
    imvocationHandler을 구현해야 함. 부가기능이 이것을 통해서 구현됨.
    만들어진 프록시 객체는 타겟이 되는 인터페이스가 됨.
  • Spring AOP는 다양한 방법으로 사용 가능하다.
    Spring AOP를 사용하기 위해서는 프로젝트에 셋업을 해주어야 함.
    pom.xml에서 정의해주어야 함.
  • spring-aop aspectjweaver 모듈 제공.
  • @AspectJ support 어노테이션을 많이 사용함.
    런타임에 적용된다.

Spring AOP

주요 개념

  • Target : 핵심 기능을 담고 있는 모듈로, 부가기능을 부여할 대상.
    AOP를 적용할 대상.
  • JoinPoint : Advice, AOP가 적용될 수 있는 위치.
    타겟 객체가 구현한 인터페이스의 모든 메서드가 이것이 될 수 있음.
    끼어들 수 있는 지점. 메서드 진입 지점, 생성자 호출 시점, 필드에서 값을 꺼내올 때 등 다양한 시점에 적용가능.
  • PointCut : 어드바이스를 적용할 타겟의 메서드를 선별하는 정규 표현식.
    여러 JoinPoint 중에 부가기능을 적용시킬지 상세한 스펙을 정의한 것. 'A란 메서드의 진입 시점에 호출할 것'과 같이 더욱 구체적으로 Advice가 실행될 지점을 정할 수 있음.
    포인트컷 표현식은 execution으로 시작하고 메서드의 Signature를 비교하는 방법을 주로 이용.
  • Aspect : 어드바이스 + 포인트컷.
    스프링에서는 Aspect를 빈으로 등록해서 사용.
  • Advice : 타겟의 특정 조인포인트에 제공할 부가기능.
    실질적으로 어떤 일을 해야할 지에 대한 것, 실질적인 부가기능을 담은 구현체.
    • 다양한 어드바이스가 존재.
  • Weaving : 타겟의 조인 포인트에 어드바이스를 적용하는 과정.
    포인트컷이 메서드를 선별하고 적용된 뒤 AOP가 적용되는 것을 위빙이라고 부름.
    AOP를 전체적으로 적용하는 것이라고 생각하면 됨.

적용

  • @Aspect @Component 사용해야 함.
  • AOP적용을 위해서는 EnableAspectJAutoProxy 사용해야 함.
  • 포인트컷
    • 포인트컷 지정자 : 타겟 오브젝트의 여러 조인포인트 중에서 어드바이스를 어떻게 적용시킬지 AOP에 알려주는 키워드. execution()이 기본.
      "executon(접근제한자 리턴타입 사용영역 클래스 메서드(아규먼트)"
    • execution PCD


    • 어노테이션 사용
      @Pointcut()
      메서드에다 적용 가능하면 void여야만 함.
  • 포인트 컷을 미리 모듈화 시켜놓는 것도 가능.
  • spring의 AOP는 등록된 빈에 의해 만들어진 객체에만 프록시 객체가 만들어져 적용 가능.
    어드바이스를 만들어도 아무 데나 적용할 수 있는 것은 아님.

Transaction Manager

  • Platform transacion manager을 사용해 트랜잭션 관리를 해 줌.
    다양한 구현체를 보유함.

  • TransactionTemplate은 등록해주어야 함.

Transactional

@Transactional

  • @Transactional은 클래스나 메서드에 붙여줄 경우, 해당 범위 내 메서드가 트랜잭션이 되도록 보장해준다.
  • 선언적 트랜잭션이라고도 하는데, 직접 객체를 만들 필요 없이 선언만으로도 관리를 용이하게 해주기 때문.
  • SpringBoot에서는 선언적 트랜잭션에 필요한 여러 설정이 이미 되어있는 탓에, 더 쉽게 사용할 수 있다.
  • @Transactional의 효과
    • 연산이 고립되어, 다른 연산과의 혼선으로 인해 잘못된 값을 가져오는 경우가 방지된다.
    • 연산의 원자성이 보장되어, 연산이 도중에 실패할 경우 변경사항이 Commit되지 않는다.
  • 위의 속성이 보장되기 때문에 해당 메서드를 실행하는 도중 메서드 값을 수정/삭제하려는 시도가 들어와도 값의 신뢰성이 보장된다.
    연산 도중 오류가 발생해도 rollback해서 DB에 해당 결과가 반영되지 않도록 할 수 있다.

트랜잭션 전파(Transaction propagation)

  • 특정 메소드 a안에 트랜잭션이 처리되는 과정 중에 또 다른 트랜잭션 b가 처리되는 것.
  • @Transactional안에서 또 다른 메서드를 호출했는데 그 메서드에도 @Transactional이 적용되어 있는 경우.

트랜잭션 전파의 종류

트랜잭션 전파의 종류

  • @Transactional default값은 REQUIRED

  • @Transactional 뒤에 괄호를 붙이고 propagation을 넣어 사용. 종류도 선택할 수 있다.

REQUIRED

  • 부모 트랜잭션이 존재한다면 모두 부모 트랜잭션에 합류함. 부모 트랜잭션이 없다면 새로운 트래잭션을 형성하고, 중간에 롤백이 발생한다면 모두 하나의 트랜잭션이기 때문에 진행사항이 모두 롤백된다.
  • 호출하는 트랜잭션이 없으면 새로 만든다.
  • 없으면 만들고 있으면 사용한다.

REQUIRED_NEW

  • REQUIRED_NEW propagation로 설정해놓으면 항상 새로운 트랜잭션이 시작된다.
  • 선행하던 트랜잭션이 있어도, 해당 트랜잭션 메서드가 반환되고 전에 잠시 중단되고 새로운 트랜잭션이 시작된다. 새 트랜잭션이 종료되고 나면 기존 트랜잭션이 진행되고, 종료된다.
  • 각각의 트랜잭션이 롤백되더라도 서로 영향을 주지 않는다.

MANDATORY

  • 반드시 부모 트랜잭션이 존재해야 하며, 부모 트랜잭션에 합류시킴.
    부모 트랜잭션이 없다면 예외를 발생시킨다.

NESTED

  • 부모 트랜잭션이 존재한다면 중첩 트랜잭션을 형성. 중첩된 트랜잭션 내부에서 롤백 발생시 해당 중첩 트랜잭션의 시작 지점까지만 롤백된다. 중첩 트랜잭션은 부모 트랜잭션이 커밋될 때 같이 커밋됨.
    부모 트랜잭션이 존재하지 않는다면 새 트랜잭션 형성.

NEVER

  • 트랜잭션을 생성하지 않는다. 부모 트랜잭션이 존재한다면 예외를 발생시킴.

SURPPORT

  • 부모 트랜잭션이 있다면 합류합니다. 진행중인 부모 트랜잭션이 없다면 트랜잭션을 생성하지 않습니다.
  • 트랜잭션이 없다면 호출하는 메서드가 트랜잭션이 만들어지지 않은 상태로 호출된다.
    트랜잭션이 있다면 그 트랜잭션을 묶고, 없다면 만들지 않는다.
  • 있으면 사용하나 없어도 만들지 않는다.

NOT_SUPPORTERD

  • 부모 트랜잭션이 있다면 보류시킵니다. 진행중인 부모 트랜잭션이 없다면 트랜잭션을 생성하지 않습니다.
  • 트랜잭션이 필요치 않다는 것을 의미. SUPPORT와 달리 진행 중인 트랜잭션이 있다면 해당 메서드가 반환되기 전까지 잠시 중단, 메서드 실행이 종료되고 나서 기존 트랜잭션을 계속 진행하게 됨.

트랜잭션 격리(Transaction Isolation Level)

트랜잭션 격리

  • 세로가 레벨, 가로가 현상.
  • 선행 트랜잭션에서 작업을 하고 있는데 후행 트랜잭션이 데이터를 변경할 경우, 자신의 트랜잭션 안에서 변경된 내용만 읽어야지 다른 트랜잭션에 의해 변경된 것은 영향을 받지 않게 되어야 개별 트랜잭션이 독립되었다고 할 수 있음.
  • 독립성, 격리성의 단계를 나눈 것이 트랜잭션 아이솔레이션 레벨.
  • Isolation Level의 선택은 동시성과 무결성을 고려해서 선택해야 한다.
    동시성을 증가시키면 데이터 무결성에 문제가 발생하고, 데이터 무결성을 유지하면 동시성이 떨어지게 된다.
    또한 레벨을 높일수록 발생하는 비용이 증가한다.

Level

  • READ_UNCOMMITTED : 커밋되지 않고 처리 중인 데이터를 다른 트랜잭션에서 읽는 것을 허용. 다른 트랜잭션에서 데이터를 변경해버리면 사용 트랜잭션에서의 데이터도 변경됨.
  • READ_COMMITED : 커밋되어 확정된 데이터만 읽는 것을 허용.
  • REPEATABLE_READ : 먼저 발생한 트랜잭션이 읽은 데이터는 트랜잭션이 종료될때까지 다른 트랜잭션이 이용할 수 없음. 같은 데이터를 한 트랜잭션 내에서 여러 번 쿼리했을 때 일관성이 있는 결과가 반환됨. (UPDATE, DELETE 방지)
  • SERIALIZABLE : 기본적으로 REPEATABLE같지만, 데이터가 추가되는 것도 막아줌. (INSERT 방지)
  • DEFAULT : 사용하는 DBMS에서 설정한 isolation level을 따름.
    • MySQL은 REPEATABLE_READ이 default

낮은 Isolation Level을 사용할 때 발생하는 현상들

  • dirty reads

    • 어떤 트랜잭션에서 아직 실행이 끝나지 않은 다른 트랜잭션에 의한 변경사항을 보게 되는 경우.
    • 커밋되지 않은 수정중인 데이터를 다른 트랜잭션에서 읽을 수 있도록 허용할 때 발생하는 현상.
  • non-repeatable reads

    • 한 트랜잭션 내에서 같은 쿼리를 두번 수행할 때, 다른 트랜잭션이 값을 수정하고 삭제하면 두 번째 쿼리가 아까와 다른 값을 반환함. 일관되지 않은 상황이 발생.
    • 한 트랜잭션에서 똑같은 SELECT를 수행했을 때 항상 같은 결과를 반환해야 한다는 Repeatable Read 정합성에 어긋남.
  • phantom reads :

    • 한 트랜잭션 안에서 전체를 조회할 때 첫 번째 쿼리에서 없던 유형의 레코드가 두번째 쿼리에서 나타나는 현상.
    • 트랜잭션 도중 새로운 레코드 삽입을 허용하기 때문에 나타남.
  • 스프링에서 레벨 설정 가능.

0개의 댓글