Skip to content

Latest commit

 

History

History
250 lines (182 loc) · 16.4 KB

week03.md

File metadata and controls

250 lines (182 loc) · 16.4 KB

3. 트랜잭션

  • 여러개의 작업을 하나로 묶은 것

  • 롤백 = 묶은 작업 중 하나라도 잘못되면, 작업이 실행되기 이전의 상태로 돌립니다.

3.1 트랜잭션 사용 이유

  • 데이터베이스를 수정하는 작업에서는 반드시 사용해야 합니다.
    b) 왜냐하면 데이터 베이스를 수정하다가 오류가 나면 데이터 베이스에 잘 못된 데이터가 들어갈 수 있습니다. 이 때 롤백기능을 사용해서 잘못된 데이터가 추가되지 않도록 만들어야 합니다.

  • 한번에 이루어져야 하는 작업들을 관리할 때 사용합니다.
    예시

@Transactional
public void Service(){
  minusPoint();
  sendPoint();
  plusPoint();
}

위 작업에서 포인트를 주고받습니다. 만약 포인트를 다른 사람엑 전송하기 위해 minuPoint()를 실행한 후 오류가 난다면, 포인트는 전송되지 않고 사라집니다.
따라서 포인트를 주고 받는 행위에 포함되는 모든 작업을 트랜잭션으로 묶어서 작업 도중에 오류가 난다면 롤백이 일어날 수 있도록 해야합니다.

3.2 트랜잭션 성질

  1. 원자성
  • 한 트랜잭션 내에서의 작업은 모두 하나로 간주합니다.
  • 따라서 트랜잭션에 속하는 작업들의 실행 결과는 모두 성공하거나 모두 실패하거나 둘 중 하나입니다.
  1. 일관성
  • 트랜잭션은 일관성있는 데이터베이스 상태를 유지합니다.
    (data integrity 만족)
  1. 격리성
  • 동시에 실행되는 트랜잭션들이 서로에게 영향을 미치지 않도록 격리해야 합니다.
  1. 지속성
  • 트랜잭션을 성공적으로 마치면 결과가 항상 저장되어야 합니다.

3.2 트랜잭션 설정 방법

  • 스프링에서는 트랜잭션 처리를 다양한 방법으로 지원합니다.

    • xml 방식
    • JavaConfig 방식
    • 어노테이션(annotaion) 방식
  • 그중 어노테이션 방식으로 @Transactional 을 선언하여 사용하는 방법이 일반적이며, 이 방식으로 생성된 트랜잭션을 선언적 트랜잭션이라고 부릅니다.

3.3 @Transactional 을 이용한 선언적 트랜잭션

  • 클래스, 메서드 코드 위에 @Transactional 어노테이션이 추가되면 해당 클래스에 프록시 기능이 적용된 프록시 객체가 생성됩니다.

  • 이 프록시 객체는 @Transacitonal 이 포함된 메서드가 호출되었을 때, PlatformTransactionalManager 를 사용하여 트랜잭션을 시작하고, 정상 여부에 따라서 Commit 또는 Rollback을 합니다.

3.4 여러개의 트랜잭션이 경쟁할 때 발생할 수 있는 문제

  1. Dirty Read
  • 트랜잭션 A가 어떤 값을 1에서 2로 변경하고 아직 커밋하지 않은 상황에서 트랜잭션 B가 같은 값을 읽는다면, 트랜잭션 B는 해당 값을 2로 받아옵니다.

  • 하지만 트랜잭션 B가 값을 읽어간 후, 트랜잭션 A가 롤백된다면 트랜잭션 B는 잘못된 값을 가져가게 된 것입니다.

  • 트랜잭션이 완료(commit)되지 않은 상황에서 데이터에 접근을 허용할 때 발생할 수 있는 데이터 불일치입니다.

  1. Non-Repeatable Read
  • 트랜잭션 A가 어떤 값을 읽은 후, 한번 더 읽는 작업을 한다고 가정합니다.

  • 만약 첫번째 읽기 작업에서 해당 값을 1로 읽어온 후, 트랜잭션 B가 해당 값을 1에서 2로 바꾼 후 커밋해버린다면, A는 첫번째 읽기 작업을 통해서 얻어온 값과 두번재 읽기 작업을 통해서 얻어 온 값이 달라집니다.

  • 한 트랙잭션에서 똑같은 쿼리를 두번 실행했을 때 발생할 수 있는 데이터 불일치입니다.

  • Dirty Read 보다는 일어날 확률이 적습니다.

  1. Phantom Read
  • 트랜잭션 A가 어떤 쿼리를 사용하여 특정 범위의 값[0, 1, 2, 3, 4] 를 읽었다고 가정합니다.

  • 이후 A는 똑같은 쿼리를 한번더 실행할 것인데, 그 사이에 트랜잭션 B가 앞서 [0, 1, 2, 3, 4] 가 들어있던 테이블에 값 [5, 6, 7]을 추가한다면, A가 이후 똑같은 쿼리를 실행했는데도 쿼리의 결과로 반환되는 값이 달라집니다.

  • 한 트랜잭션에서 일정 범위의 레코드를 두번이상 읽을 때 나타나는 데이터 불일치입니다.

3.5 트랜잭션 속성

  • 스프링에서는 위와 같은 트랙재션 경쟁을 방지할 수 있는 속성을 지원합니다.
  1. isonlation(격리수준)
  • 트랜잭션에서 일관성이 없는 데이터를 허용하는 수준을 말합니다.

    1. DEFAULT = 기본 격리 수준(기본 설정, DB의 isolation level을 따릅니다.)
    2. READ_UNCOMMITED (level 0) = 커밋되지 않은(트랜잭션이 처리중인) 데이터에 대해서 읽기 허용
    • Dirty Read 발생 가능
    1. READ_COMMITED ( level 1) = 트랜잭션이 커밋한 확정 데이터만 읽기 허용
    • Dirty Read 발생하지 않습니다.
    1. REPEATABLE_READ( level 2) = 트랜잭션이 완료될 때까지 해당 트랜잭션의 SELECT 문장에서 사용하는 모든 데이터에 shared lock 이 걸리기 때문에, 다른 사용자는 해당 데이터에 대한 수정이 불가능합니다.
    • 한 트랜잭션이 읽은 데이터는 다른 트랜잭션이 수정, 삭제가 불가능하기 때문에 같은 쿼리를 두번 하더라도 똑같은 결과를 얻을 수 있습니다.
    • Non-Repeatable Read 발생하지 않습니다.
    1. SERIALIABLE( level 3) = 데이터의 일관성 및 동시성을 위해서 MVCC(Multi Vision Concurrency Control) 을 사용하지 않습니다.
    • 트랜잭션이 완료될 때까지, 해당 트랜잭션의 SElECT 문에서 사용하는 모든 데이터에 대해 shared lock 이 걸립니다. 따라서 다른 사용자는 해당 영역에 대항하는 데이터에 대해 수정, 입력이 불가능합니다.
    • Phantom Read 발생하지 않습니다.
  • 격리 수준이 올라갈 수록 성능 저하의 우려가 있습니다.

  • 예시

@Transactional(isolation = Isolation.DEFAULT)
public void somthing(int a){
  ...
}


  1. propagation (전파 옵션)
  • 한 트랜잭션에서 다른 트랜잭션을 호출하는 상황에서 선택할 수 있는 옵션

    1. REQUIRED = 디폴트
    • 부모 트랜잭션 내에서 실행하며, 부모 트랜잭션이 없는 경우 새로운 트랜잭션을 생성합니다.
    1. SUPPORTS
    • 이미 시작된 트랜잭션이 있으면 참여하고 그렇지 않으면 트랜잭션없이 진행되게 합니다.
    1. REQUIRES_NEW
    • 부모 트랜잭션을 무시하고 무조건 새로운 트랜잭션이 생성됩니다.
    • 이미 진행중인 트랜잭션이 있으면, 트랜잭션 생성을 잠시 보류시킵니다.
    1. MANDATORY
    • REQUIRED 와 비슷하게, 이미 시작된 트랜잭션이 있으면 참여합니다.
    • 이미 시작된 트랜잭션이 없으면, 새로 시작하는 대신 예외를 발생시킵니다.
    • 혼자서 독립적으로 트랜잭션을 진행하면 안되는 경우에 사용합니다.
    1. NOT_SUPPORTED
    • 트랜잭션을 사용하지 않도록 강제합니다.
    • 이미 실행중인 트랜잭션이 존재하면 예외를 보류시킨다.
    1. NEVER
    • 트랜잭션을 사용하지 않게 한다.
    • 이미 진행 중인 트랜잭션이 있으면 예외를 발생시킨다.
    1. NESTED
    • 이미 진행중인 트랜잭션이 있으면, 중첩 트랜잭션을 시작합니다.

    • 중첩 트랜잭션 = 트랜잭션안에 트랜잭션을 만드는 것

    • 중첩 트랜잭션은 먼저 시작된 트랜잭션(부모 트랜잭션)의 커밋과 롤백에는 영향을 받지만 자신의 커밋, 롤백은 부모 트랜잭션에 영향을 주지 않습니다.

    • EX. 쇼핑몰에서 주문트랜잭션에 로그를 남기는 작업이 있는 경우

    • 이 로그를 남기는 작업이 실패한다고 해서 메인 작업까지 롤백되면 안됩니다.

    • 하지만 주문작업이 실패했을 때는 로그를 남기지 않는 것이 맞습니다.

    • 이 때 중첩 트랜잭션을 사용하면 됩니다.

  1. readOnly 속성
  • 트랜잭션을 읽기전용 속성으로 지정할 수 있습니다.

  • 성능 최적화를 위해서 or 특정 트랜잭션 작업안에서 쓰기 작업이 일어나는 것을 막기 위해서 사용할 수 있습니다.

  • 일부 트랜잭션의 경우 일기전용 속성을 무시하고 쓰기 작업을 허용할 수도 있습니다.

  • readOnly 속성이 지정된 트랜잭션에서 INSERT, DELETE, UPDATE 작업과 같이 쓰기 작업이 일어나면 예외가 발생합니다.

  • aop/tx 스키마로 트랜잭션 선언할 때는 이름 패턴을 이용해서 읽기 전용 속성으로 만드는 경우가 많다. 보통 get이나 find같은 이름의 메소드를 읽기 전용 메소드로 만들어서 사용하면 편합니다.

  • @Transactional을 사용하여 트랜잭션을 만드는 경우는 일일이 읽기 전용 속성을 지정해줘야 합니다. read-only 속성 또는 readOnly 속성을 true로 지정하면 읽기 전용으로 지정됩니다.

  • true인 경우에는 INSERT, UPDATE, DELETE 발생시 예외 발생하며, 디폴트는 false로 되어 있습니다.

  • 예시

@Transaction(readOnly = true)
  1. 트랜잭션 롤백 예외 (rollback-for, rollbackFor, rollbackForClassName)
  • 선언적 트랜잭션에서는 런타임에서 예외(오류)가 발생하면 롤백합니다.

  • 반면에 예외가 전여 발생하지 않거나 체크 예외가 발생하면 커밋합니다.

  • 체크 예외가 커밋 대상인 이유 = 체크 예외가 예외적인 상황일 때 발생하기 보다는 리턴 값을 대신해서 비즈니스적인 의미를 담은 결과를 반환하는 용도로 사용되기 때문입니다.

  • 스프링에서는 데이터 액세스 때 발생하는 예외는 런타임 예외로 발생하기 때문에, 런타임 예외가 일어났을 때에만 롤백이 일어납니다.

  • 하지만 롤백이 일어나는 대상을 바꿀 수 있습니다.
    ex. 체크 예외이지만 롤백이 일어나야하는 경우가 있다면, XML의 roolback-for 애트리뷰트나 어노테이션 @rollback 혹은 rollbackForClassName 속성을 사용해서 예외를 지정하면 됩니다.
    rollback-for 애트리뷰트 나 rollbackForClassName 속성에는 예외 이름을 지정하면되고, @rollbackFor는 예외 대상 클래스에 넣어주면됩니다.

  • @Transaction 어노테이션에서 rollbackFor 속성을 이용하여 지정하는 경우에는 클래스 이름 대신 클래스를 직접 사용해도 됩니다.
    ex.

@Transactional(readOnly = true, rallbackFor = NoSuchMemberException.class)
  • @Transactional 어노테이션에서 롤백 관련 속성
  • rollbackFor: 특정 예외가 발생했을 때 강제로 Rollback
    예: @Transactional(rollbackFor = Exception.class)
  • noRollbackFor: 특정 예외 발생시 Rollback 하지 않음 예: @Transaction(noRollbackFor = Exception.class)
  1. timeout 속성
  • timeout 속성으로 지정한 시간내에 해당 트랜잭션이 완료되지 않은 경우 rollback 수행합니다.

  • -1이일 경우 timeout 속성을 지정하지 않은 것이며, default에 해당합니다.

  • ex) Transactional(timeout = 10)




4. 스프링 시큐리티

  • 스프링 기반의 어플리케이션의 보안(인증, 인가, 권한 등)을 담당하는 프레임워크

  • 스프링 시큐리티를 사용하지 않으면, 자체적으로 보안을 위한 로직을 구현해야한다.
    하지만 스프링 시큐리티를 사용하면 보안과 관련한 옵션을 제공하기 때문에, 우리는 이 옵션만 사용하면 쉽게 보안을 구현할 수 있다.

  • 스프링 시큐리티는 filter 기반으로 동작하기 때문에 spring MVC 와 분리되어 동작한다.

4.1 보안관련 용어

  • 접근 주체(Principal): 보호된 리소스에 접근하려는 유저

  • 인증(Authenticate): 보호된 리소스에 접근하려는 유저에 대해서 이 유저가 누구인지 확인하는 작업, 해당 작업을 수행해도 되는 유저인지를 확인하는 작업(ex. Form 기반 로그인)

  • 인가(Authorize): 해당 리소스에 대해 접근가능한 권한을 가지고 있는지 검사하는 것, 인증 이후에 이루어집니다.

  • 권한: 인증된 주체가 애플리케이션의 동작을 수행할 수 있도록 허락되었는지를 결정

    • 권한 승인이 필요한 부분으로 접근하려면, 먼저 인증 과정을 통해서 주체가 인증되어야 한다.
    • 즉, 인가 과정에서 해당 리소스에 대한 권한을 가지고 있는지를 확인한다.

4.5 Spring 시큐리티 구조(아키텍처)

image

  • 위 그림 = Form 기반 로그인에 대한 흐름을 보여줍니다.
  1. 사용자가 Form 을 이용하여 로그인하기 위한 정보를 입력하고 인증 요청을 보냅니다.(Http Request)

  2. AuthenticationFilter가 HttpServletRequest에서 사용자가 보낸 아이디와 패스워드를 꺼냅니다.

  • 보안을 위해서 아이디와 패스워드의 유효성 검사를 해줍니다.(아이디 혹은 패스워드가 null 인지 등).
  • 인증을 담당하는 AuthentiacationManager 인터페이스 구현체에게, HttpServeletRequest 에서 꺼낸 사용자 아이디와 패스워드를 인증용 객체(UsernamePasswordAuthenticationToken) 로만들어서 전달합니다.
  1. AuthenticationFilter에게 Authentiaction 객체(인증용 객체, UsernamePasswordAuthenticationToken)을 전달받습니다.

  2. 실제 인증을 할 AuthenticationProvider에게 Autication 객체를 다시 전달합니다.

  3. DB에서 사용자 인증정보를 가져올 UserDetailsSercice 객체에게 사용자 아이디를 넘겨주고, DB에서 인증에 사용할 정보(아이디, 암호화된 패스워드, 권한 등)를 UserDetails라는 객체로 전달받습니다.

  4. AuthenticationProvider는 UserDetails객체를 전달 받은 후, 실제 사용자의 입력정보와 UserDetails 객체를 가지고 인증합니다. 7, 8, 9, 10. 인증이 ㅅ완료되면 사용자 정보를 가진 Authentication 객체를 SecurityContextHolder 에 담은 후 AuthenticationSuccessHandle를 실행합니다.(실패시 AuthenticationFailHandle을 실행합니다.)

4.6 Security filter

image

  1. SecurityContextPersistenceFilter : SEcurityContextRespository에서 SecurityContext를 가져오가너 저장하는 역할을 합니다.

  2. LogoutFilter : 설정된 로그아웃 URL로 오는 요청을 감시하며, 해당 유저를 로그아웃 처리를 합니다. (UsernamePassword)

  3. AuthenticationFilter : (아이디와 비밀번호를 사용하는 form 기반 인증) 설정된 로그인 URL로 오는 요청을 감시하며, 유저 인증 처리를 합니다.

  • AuthenticationManager를 통한 인증을 실행합니다.
  • 인증 성공 시, 얻은 Authentication 객체를 SecurityContext에 저장 후 AuthenticationSuccessHandler 실행합니다.
  • 인증 실패 시, AuthenticationFailureHandler 실행합니다.
  1. DefaultLoginPageGeneratingFilter : 인증을 위한 로그인폼 URL을 감시합니다.
  2. BasicAuthenticationFilter : HTTP 기본 인증 헤더를 감시하여 처리합니다.
  3. RequestCacheAwareFilter : 로그인 성공 후, 원래 요청 정보를 재구성하기 위해 사용됩니다.
  4. SecurityContextHolderAwareRequestFilter : HttpServletRequestWrapper를 상속한 SecurityContextHolderAwareRequestWapper 클래스로 HttpServletRequest 정보를 감쌉니다.
  • SecurityContextHolderAwareRequestWrapper 클래스는 필터 체인상의 다음 필터들에게 부가정보를 제공합니다.
  1. AnonymousAuthenticationFilter : 이 필터가 호출되는 시점까지 사용자 정보가 인증되지 않았다면 인증토큰에 사용자가 익명 사용자로 나타납니다.
  2. SessionManagementFilter : 이 필터는 인증된 사용자와 관련된 모든 세션을 추적합니다.
  3. ExceptionTranslationFilter : 이 필터는 보호된 요청을 처리하는 중에 발생할 수 있는 예외를 위임하거나 전달하는 역할을 합니다.
  4. FilterSecurityInterceptor : 이 필터는 AccessDecisionManager 로 권한부여 처리를 위임함으로써 접근 제어 결정을 쉽게해줍니다.

https://sjh836.tistory.com/165 [빨간색코딩]