Fox Transactions Basics¶
Fox Transactions 기능을 사용하기 위한 기본적인 사항들을 설명합니다.
Note
Fox Transactions 기능을 사용하기 위해서는 NeoDEEX.Transactions 패키지를 직/간접적으로 참조해야 합니다.
예를 들어 Fox Biz/Data Service Web API 개발을 위해 NeoDEEX.ServiceModel.WebApi 패키지를 사용하는 경우 이 패키지가 NeoDEEX.ServiceModel.Services 패키지를 참조하고, NeoDEEX.ServiceModel.Services 패키지가 다시 NeoDEEX.Transactions 패키지를 참조하기 때문에 Fox Transactions 기능을 Fox Biz/Data Service Web API 에서 사용할 수 있습니다.
이 문서와 관련된 예제 코드는 다음을 참조 하십시요.
기본 사용 방법¶
Fox Transactions 기능을 사용하기 위해서는 FoxComponentBase 추상 베이스 클래스에서 직/간접적으로 파생된 클래스를 사용해야 합니다. 대부분의 경우 FoxComponentBase 클래스 보다는 FoxBizBase 클래스 혹은 FoxDacBase 클래스를 사용하는 것이 좋습니다.
-
FoxDacBase클래스데이터액세스를 위한 베이스 클래스로서 데이터액세스 코드들을 담는 클래스들을 위한 베이스 클래스 입니다. 트랜잭션 옵션으로
Supported가 디폴트로 지정되어 있습니다. 따라서FoxDacBase에서 파생된 클래스의 메서드를 곧바로 호출하면 트랜잭션은 적용되지 않습니다. 하지만FoxBizBase에서 파생된 클래스를 거쳐서 데이터액세스 클래스를 호출하면 이미 시작된 트랜잭션에 참여(enlist)하게 됩니다. -
FoxBizBase클래스비즈니스 로직을 위한 베이스 클래스로서 주로 트랜잭션을 제어하고 비즈니스 로직 코드를 담는 클래스를 위한 베이스 클래스 입니다. 트랜잭션 옵션으로
Required가 디폴트로 지정되어 있기 때문에FoxBizBase에서 파생된 클래스에 메서드를 선언하면 이 메서드는 트랜잭션을 사용하게 됩니다.
DAC 클래스 사용¶
데이터액세스 코드를 별도로 분리하면 데이터 계층의 변화에 유연하게 대처할 수 있습니다. 예를 들어 PostgreSQL 와 같은 관계형 데이터베이스를 사용하다가 MongoDB 와 같은 NoSQL 데이터베이스를 사용하는 경우 데이터액세스 코드만을 수정하면 됩니다. 이러한 이유에서 DAC(Data Access Component) 클래스를 사용하여 데이터액세스 코드를 별도로 작성하는 것이 권장됩니다.
Supported 트랜잭션 옵션¶
FoxDacBase 에서 파생된 데이터액세스 클래스에 트랜잭션 옵션으로 Supported 를 사용하는 것이 일반적입니다. 다음 MemoDac 클래스의 예를 살펴 봅시다.
MemoDac 클래스는 메모 데이터를 데이터베이스 대해 조회/추가/수정/삭제하는 메서들을 포함합니다. GetMemos 메서드는 데이터를 조회하는 메서드이므로 특별한 경우가 아니라면 트랜잭션을 사용할 필요가 없습니다. InsertMemo 메서드는 데이터를 변경하는 메서드이지만 단독으로 호출되는 경우, 1건의 데이터가 변경되는 것이므로 트랜잭션을 사용할 필요가 없습니다. 따라서 MemoDac 클래스에 Required 와 같은 트랜잭션 옵션을 사용하면 불필요한 트랜잭션이 사용되는 것입니다.
데이터베이스 액세스¶
FoxDacBase 클래스에서 파생된 클래스에서 FoxDbAccess 클래스를 사용하고자 하는 경우, DbAccess 속성을 사용하는 것이 좋습니다. DbAccess 속성은 FoxDacBase 클래스에서 제공하는 속성으로써 Fox Transactions 의 전처리(pre-processing) 과정에서 FoxDbAccess 인스턴스가 생성되어 채워지며 메서드 종료 이후 예외 발생 여부에 상관없이 Dispose 됩니다. 즉, 메서드가 종료하면 반드시 데이터베이스 연결이 닫힌다는 것을 보장하는 것과 같습니다.
DbAccess 속성값은 CreateDbInstance 메서드가 반환하는 값으로 초기화됩니다. CreateDbInstance 메서드의 기본 구현은 다음과 같습니다. 즉, 디폴트 연결 문자열을 사용하여 FoxDbAccess 인스턴스를 생성합니다.
DbAccess 속성값을 커스터마이징 하기 위해서는 CreateDbInstance 메서드를 오버라이드 하면 됩니다. 다음 코드는 MyConn 연결 문자열을 사용하는 FoxOracleDbAccess 인스턴스를 생성하고 필요한 설정을 수행한 이후에 반환합니다. 반환된 인스턴스는 DbAccess 속성을 통해 사용할 수 있습니다.
BIZ 클래스 사용¶
비즈니스 로직 코드는 앱의 핵심적인 기능을 구현하는 계층입니다. 따라서 중요한 데이터를 저장하는 등의 작업을 비즈니스 로직으로 구현하고 필요하다면 트랜잭션을 사용해야 합니다. 하지만 단순 조회와 같이 특별한 비즈니스 로직이 없다면 비즈니스 로직 계층을 패스하여 곧바로 DAC 클래스를 호출하는 것도 성능을 향상시키는 좋은 방법입니다.
Required 트랜잭션 옵션¶
다음 예제에서는 Required 옵션을 사용하고 있다는 것을 명시적으로 나타내기 위해 FoxTransactionAttribute 특성을 사용하였습니다만 FoxBizBase 에서 파생된 비즈니스 로직 클래스는 Required 트랜잭션 옵션이 기본적으로 적용됩니다. 다음 MemoBiz 클래스의 InsertMany 메서드는 여러 메모(memo)를 데이터베이스에 저장하기 위해 트랜잭션을 사용하며 저장 도중 오류(예외)가 발생하면 트랜잭션은 롤백됩니다.
효율성 고려¶
단순 조회와 같이 트랜잭션이 필요하지 않은 메서드는 불필요한 트랜잭션이 시작하지 않도록 하는 것이 좋습니다. 예를 들어 다음과 같은 GetMemos 메서드는 단순 조회이므로 트랜잭션이 필요 없습니다. 이러한 경우 메서드 수준에서 Supported 옵션을 사용하여 비즈니스 로직 클래스의 메서드임에도 트랜잭션이 사용되지 않음을 명시합니다.
Why Supported option?
왜 Suppress 옵션이나 None 옵션이 아닌 Supported 옵션을 사용해야 할까요? 이는 다른 비즈니스 로직이 트랜잭션을 사용하면서 MemoBiz.GetMemos 를 호출할 수도 있기 때문입니다. 이 때 Supported 옵션은 호출자가 트랜잭션을 사용하면 해당 트랜잭션에 참여할 수 있기 때문에 훨씬 더 유연한 선택사항 입니다. 명시적으로 반드시 트랜잭션을 사용하지 않아야 하는 경우에만 Suppress 옵션을 사용하고 그 외에는 Supported 옵션을 사용하는 것이 좋습니다. Fox Transcations 가 트랜잭션에 대한 어떤 제어도 수행하지 않기 때문에 고급 트랜잭션 제어를 하고자 할 때에만 드물게 사용됩니다.
트랜잭션을 사용하지 않는 단순 조회에 대해 효율성을 높이는 더욱 좋은 방법은 비즈니스 로직을 거치치 않고 곧바로 DAC 계층을 호출하는 것입니다. 예를 들어, 웹 앱 클라이언트 호출을 처리하는 Web API 에서 단순 데이터 조회는 다음과 같이 DAC 클래스를 곧바로 호출하면 비즈니스 로직 계층을 거치지 않기 때문에 성능을 향상시킬 수 있습니다.
트랜잭션 메서드 호출¶
Fox Transactions 가 제공하는 기능들이 정상적으로 작동하기 위해서는 반드시 수행 환경이 구성되어야 합니다. 이를 위해서는 FoxComponentBase 클래스에서 제공하는 CreateExecution<T> 메서드를 호출하여 수행 프록시와 수행 문맥이 만들어지도록 해야 합니다.
앞서 살펴본 대로 트랜잭션 클래스들은 직/간접적으로 FoxComponentBase 에서 파생된 클래스이므로 다음과 같이 객체 생성 후에 CreateExecution<T> 메서드를 호출할 수 있습니다.
CreateExecution<T> 메서드는 제너릭 타입 T 를 구현하는 수행 프록시 객체를 생성하여 반환합니다. 여기에서 비즈니스 로직 객체(MemoBiz)가 구현하는 인터페이스(IMemoBiz)를 사용하는 것에 주목하십시요.
이제 호출자가 IMemoBiz 인터페이스를 통해 호출을 수행하면 해당 호출은 수행 프록시를 통해 전처리가 수행되고 실제 메서드 구현이 호출될 것입니다.
반드시 수행 프록시를 통해 메서드를 호출하도록 주의 하십시요. 수행 프록시 없이 직접 메서드를 호출하면 Fox Transactions 가 제공하는 기능들, 즉 트랜잭션은 자동으로 시작하거나 커밋/롤백되지 않을 뿐만 아니라 일부 다른 기능들도 작동하지 않습니다. 수행 프록시를 사용해야만 하는 구체적인 이유에 대해서는 Fox Transactions 의 작동 원리를 참고 하십시요.
트랜잭션 메서드에서 다른 트랜잭션 메서드를 호출하는 상황에서도 수행 프록시를 사용해야 함에도 주의 하십시요. 앞서 살펴보았던 MemoBiz 클래스의 InsertMany 메서드에서 MemoDac 클래스의 InsertMemo 메서드를 호출하는 코드에서 CreateExecution<T> 메서드를 사용하여 수행 프록시를 사용했습니다.
만약 CreateExecution<T> 메서드가 생성한 수행 프록시를 사용하지 않고 다음과 같이 객체를 직접 호출하면
Fox Transactions 의 전처리/후처리가 수행되지 않기 때문에 MemoDac.InsertMemo 메서드는 트랜잭션의 성공/실패에 영향을 주는 트랜잭션 투표가 진행되지 않으며 Fox Transactions 가 제공하는 다른 기능들도 정상적으로 작동하지 않습니다.
트랜잭션 시작/참여/커밋/롤백¶
MemoBiz 클래스의 메서드를 호출하면 Required 옵션이 적용되어 새로운 트랜잭션이 시작되거나 기존 트랜잭션에 참여하게 됩니다. ASP.NET Web API 에서 다음과 같이 MemoBiz.InsertMany 메서드를 호출한다면 새로운 트랜잭션이 시작될 것입니다.
코드가 정상적으로 수행되어 예외(exception)를 발생하지 않고 InsertMany 메서드가 종료되면 트랜잭션은 자동으로 커밋됩니다(자동 트랜잭션 참조). 반면 코드 수행 도중 오류가 발생하여 InsertMany 메서드가 예외와 함께 종료되면 트랜잭션은 자동으로 롤백됩니다. 이렇게 예외가 발생하고 호출자에게 전파되어 따라 트랜잭션이 자동으로 롤백되도록 트랜잭션 메서드 내부에서는 try~catch 구문은 사용하지 않는 것이 좋으며 예외 처리는 비즈니스 로직을 호출하는 쪽에서 수행하는 것이 권장됩니다(권장 코딩 패턴 참조).
만약 Required 옵션을 사용하는 다른 비즈니스 로직 클래스가 이 메서드를 호출한다면 MemoBiz.InsertMany 메서드의 코드는 이미 시작된 트랜잭션에 참여하게 됩니다.
이 경우, 트랜잭션의 시작은 SaveDocument 메서드이며 트랜잭션 커밋/롤백 역시 SaveDocument 메서드의 종료와 함께 이루어집니다. 대신 InsertMany 메서드는 트랜잭션의 참가자로서 성공/실패에 투표하게 됩니다.
Summary¶
가장 전형적이며 권장되는 Fox Transactions 의 사용 패턴은 다음 그림과 같습니다.
클라이언트 호출을 받은 Web API 는 Required 옵션을 사용하는 비즈니스 로직 메서드를 호출하며 비즈니스 로직 메서드는 Supported 옵션을 사용하는 DAC 메서드를 호출합니다. 이 과정에서 트랜잭션은 자동으로 시작되고 예외 발생 여부에 따라 커밋/롤백이 결정됩니다. 트랜잭션이 필요없는 단순 조회는 비즈니스 로직을 사용하지 않고 곧바로 DAC 메서드를 호출하여 성능을 고려하는 것이 권장됩니다.
