Skip to content

선언적 트랜잭션

Fox Transacitons 는 메서드의 트랜잭션 사용 여부를 AOP(Aspect Oriented Programming)를 통해 선언적(declarative)으로 지정할 수 있도록 해줍니다. 즉, FoxTransactionAttribute 특성을 메서드/클래스에 지정하여 메서드가 호출될 때 트랜잭션을 시작할 것인지 기존 트랜잭션에 참여(enlist)할 것인지를 지정할 수 있습니다.

이 문서와 관련된 예제 코드는 다음을 참조 하십시요.

트랜잭션 처리 예제

다음 코드는 전형적인 트랜잭션 작업을 위해 Fox Transactions 코드를 호출하는 ASP.NET Web API 코드를 보여줍니다. 이 API 는 클라이언트로부터 여러 메모를 전달받아 데이터베이스에 저장하기 위해 비즈니스 로직 컴포넌트(MemoBiz 클래스)를 호출합니다.

// Web API Controller Class
[Route("[controller]")]
[ApiController]
public class MemoController : ControllerBase
{
    [HttpPost("save")]
    public IActionResult NewMemos(Memo[] memos)
    {
        MemoBiz biz = new();
        IMemoBiz itf = biz.CreateExecution<IMemoBiz>();
        var newIds = itf.InsertMany(memos);
        return Ok(new { newIds });
    }
}

MemoBiz 비즈니스 로직 컴포넌트는 FoxTransactionAttribute 특성을 사용하여 트랜잭션 옵션으로 Required 을 사용합니다. 아직 트랜잭션이 시작하지 않았으므로 Fox Transactions 엔진은 새로운 트랜잭션을 시작하게 됩니다. InsertMany 메서드는 BeginTrans, CommitTrans, RollbackTrans 와 같은 트랜잭션 제어 코드가 포함되어 있지 않음에 주목하십시요. 트랜잭션의 시작은 FoxTransactionAttribute 특성에 의해 선언적으로 처리됩니다.

또한 트랜잭션이 커밋될 것인지 롤백 될 것인지는 예외 발생 여부에 따라 결정됩니다. 코드 수행 중 예외가 발생하면 트랜잭션은 롤백되며 그렇지 않다면 커밋됩니다. 이는 FoxAutoCompleteAttribute 특성이 선언되어 있기 때문입니다. 상세한 내용은 자동 트랜잭션(Automatic Transaction) 문서를 참고 하십시요.

// Biz Component Class
[FoxTransaction(FoxTransactionOption.Required)]
public class MemoBiz : FoxBizBase, IMemoBiz
{
    [FoxAutoComplete]
    public List<int> InsertMany(Memo[] memos)
    {
        using MemoDac dac = new();
        IMemoDac itf = dac.CreateExecution<IMemoDac>();
        List<int> savedIds = [];
        foreach (var memo in memos)
        {
            int newId = itf.InsertMemo(memo);
            savedIds.Add(newId);
        }
        return savedIds;
    }
}

MemoBizInsertMany 메서드는 반복적으로 데이터 액세스 컴포넌트인 MemoDac 클래스의 Insert 메서드를 호출하여 메모 데이터를 데이터베이스에 기록합니다. MemoDac 클래스는 Supported 옵션을 사용하여 기존 트랜잭션에 참여합니다. 즉, MemoBiz.InsertMany 메서드에서 시작된 트랜잭션에 MemoDac.Insert 메서드가 참여하며 두 코드에서 수행되는 데이터베이스 작업은 하나의 트랜잭션에서 수행됩니다.

// Dac Component Class
[FoxTransaction(FoxTransactionOption.Supported)]
public class MemoDac : FoxDacBase, IMemoDac
{
    [FoxAutoComplete]
    public int Insert(Memo memo)
    {
        object? result = this.DbAccess.ExecuteQueryScalar("memo.insert", memo) 
            ?? throw new InvalidOperationException("InsertMemo failed: result is null.");
        int newId = Convert.ToInt32(result);
        return newId;
    }
}

FoxTransaction 특성

FoxTransactionAttribute 특성은 Fox Transactions 에서 트랜잭션을 제어하는 핵심적인 요소입니다. 이 특성을 통해 메서드가 호출될 때 새로운 트랜잭션을 시작할 것인지 기존 트랜잭션에 참여(enlist)할 것인지, 아니면 트랜잭션과 무관하게 트랜잭션 바깥에서 코드가 수행될 것인지를 지정할 수 있습니다.

FoxTransactionAttribute 특성은 클래스 혹은 메서드에 명시될 수 있으며 클래스에 명시된 FoxTransactionAttribute 특성은 메서드에 자동으로 적용되며 메서드에 명시된 FoxTransactionAttribute 특성은 클래스 수준의 FoxTransactionAttribute 특성을 오버라이드 합니다.

[FoxTransaction(FoxTransactionOptions.Required)]
public class MyBizClass : FoxBizClass
{
    public void InsertData()
    {
        // TransactionOptions.Required 적용됨
    }

    public void UpdateData()
    {
        // TransactionOptions.Required 적용됨
    }

    [FoxTransaction(FoxTransactionOptions.Supported)]
    public void QueryData()
    {
        // TransactionOptions.Supported 적용됨
    }
}

FoxTransactionAttribute 특성은 FoxTransactionOption 열거 타입을 반드시 명시해야 하며 추가적으로 다음과 같은 속성을 명시할 수 있습니다.

  • IsolationLevel 속성

    트랜잭션이 시작될 때 트랜잭션이 사용할 격리 수준(isolation level)을 설정합니다. 디폴트 값은 ReadCommitted 입니다. 이외에 사용 가능한 격리 수준은 System.Transactions 네임스페이스의 IsolationLevel 열거 타입을 참조 하십시요.

    Note

    데이터베이스마다 지원하는 트랜잭션 격리 수준은 다릅니다. 예를 들어 Oracle 은 RepeatableRead 격리 수준을 지원하지 않지만 SQL Server 는 지원 됩니다. 따라서 액세스 대상 데이터베이스가 지원하는 트랜잭션 격리 수준을 확인하십시요.

    호환성

    COM+ 와 System.Transactions 가 생성하는 트랜잭션의 기본 격리 수준은 Serializable 입니다. Serializable 은 가장 높은 수준의 격리 수준이지만 그 비용(cost)도 가장 높습니다. 예를 들어 SQL Server 의 경우 Serializable 격리 수준에서 SELECT 문장도 row 혹은 인덱스에 잠금을 수행합니다. 이러한 높은 비용 때문에 Fox Transactions 는 일반적으로 사용되는 ReadCommitted 격리 수준을 디폴트로 사용합니다.

  • Timeout 속성

    트랜잭션에 대한 타임아웃 시간을 초 단위로 설정합니다. 디폴트 값은 60 초 입니다.

    Warning

    트랜잭션 타임아웃 시간이 초과되더라도 곧바로 트랜잭션이 중단고 예외가 발생하지 않습니다. 메서드 호출이 종료되는 시점에서 트랜잭션 결과를 결정할 때 타임아웃 시간이 고려되고 타임아웃이 초과되면 트랜잭션은 롤백됩니다.

FoxTransactionOption 열거 타입

FoxTransactionOption 열거 타입은 메서드가 시작될 때 트랜잭션을 제어하는 다양한 옵션을 나타냅니다.

  • Required 옵션

    메서드가 시작될 때 기존 트랜잭션이 존재하면 이 트랜잭션에 참여하며 트랜잭션이 시작되지 않았다면 새로운 트랜잭션을 시작합니다. Required 옵션은 대부분 비즈니스 로직 메서드에서 주로 사용하며 FoxBizBase 클래스에서 디폴트 값으로 사용하는 트랜잭션 옵션 입니다.

  • RequiresNew 옵션

    메서드가 시작될 때 기존 트랜잭션의 존재 여부와 상관 없이 항상 새로운 트랜잭션을 시작합니다. 만약 이미 트랜잭션이 시작된 상태라면 이 트랜잭션과 상관없는 새로운 트랜잭션이 시작되며 새로운 트랜잭션은 중첩(nested) 트랜잭션이 아닙니다. RequiresNew 옵션은 기존 트랜잭션 문맥에 상관없이 별도의 트랜잭션을 사용해야 할 때에만 드물게 사용됩니다.

  • Supported 옵션

    메서드가 시작될 때 기존 트랜잭션이 존재하면 이 트랜잭션에 참여합니다. 트랜잭션이 시작되지 않았다면 트랜잭션과 무관하게 코드가 수행됩니다. Supported 옵션은 데이터 액세스 메서드에서 주로 사용하며 FoxDacBase 클래스에서 디폴트 값으로 사용하는 트랜잭션 옵션 입니다.

  • Suppress 옵션

    메서드는 항상 트랜잭션과 무관하게 코드를 수행합니다. 기존에 트랜잭션이 시작되었더라도 메서드 코드는 트랜잭션 바깥에서 코드를 수행합니다. Suppress 옵션은 로그 기록과 같이 메인 트랜잭션에 포함되지 않는 데이터 액세스를 수행하고자 할 때 가끔 사용됩니다. 실제로 NeoDEEX 의 FoxDbLogger 클래스는 트랜잭션 롤백 시에 로그가 데이터베이스에 기록되지 않는 것을 피하기 위해 Suppress 옵션을 사용합니다.

  • None 옵션

    Fox Transactions 은 트랜잭션에 대한 어떠한 제어도 수행하지 않습니다. 일반적으로 이 옵션은 사용되지 않으며 고급 트랜잭션 제어를 하고자 할 때에만 드물게 사용됩니다.

FoxTransaction 특성 상속

FoxTransactionAttribute 특성은 베이스 클래스로부터 상속될 수 있습니다. FoxBizBaseFoxDacBase 클래스는 다음과 같은 FoxTransactionAttribute 선언을 포함합니다.

[FoxTransaction(FoxTransactionOption.Required)]
public abstract class FoxBizBase : FoxComponentBase
{
    // ......
}

[FoxTransaction(FoxTransactionOption.Supported)]
public abstract class FoxDacBase : FoxComponentBase
{
    // ......
}

따라서 이 두 클래스에서 파생된 클래스는 베이스 클래스에서 선언된 FoxTransactionAttribute 특성을 상속받습니다. 물론 메서드 수준에서 상속된 FoxTransactionAttribute 특성을 오버라이드 할 수 있습니다.

public class MyBizClass: FoxBizBase
{
    public void Method1()
    {
        // Requried 옵션 적용됨
    }

    [FoxTransaction(FoxTransactionOption.RequiresNew)]]
    public void Method2()
    {
        // RequiresNew 옵션 적용됨    
    }
}

파생 클래스에서 명시적으로 FoxTransactionAttribute 가 선언되면 베이스 클래스의 선언을 오버라이드 합니다. 예를 들어, 다음 코드에 MyBizClassRequiesNew 옵션이 적용됩니다.

1
2
3
4
5
6
7
8
[FoxTransaction(FoxTransactionOption.RequiresNew)]
public class MyBizClass: FoxBizBase
{
    public void Method1()
    {
        // RequriesNew 옵션 적용됨
    }
}

Summary

Fox Transactions 은 FoxTransactionAttributeFoxAutoCompleteAttribute 와 같은 특성을 사용하여 트랜잭션을 선언적(declarative)으로 제어할 수 있습니다. FoxTransactionAttribute 특성은 메서드가 트랜잭션을 시작할 것인지 기존 트랜잭션에 참여(enlist)할 것인지를 설정하며 FoxAutoCompleteAttribute 특성은 트랜잭션의 결과에 영향을 주어 트랜잭션을 커밋하거나 롤백하는데 사용합니다. 이들 특성을 통해 개발자는 트랜잭션에 관련된 코드를 전혀 작성하지 않고 트랜잭션으 시작하고 수행 결과에 따라 커밋하거나 롤백할 수 있습니다.