Skip to content

데이터베이스 연결 관리

데이터베이스 액세스에서 연결 관리(connection management)는 매우 중요합니다. 데이터베이스 액세스에서 연결을 여는 작업은 높은 비용을 요구합니다. 데이터베이스 서버는 클라이언트와의 연결을 구성하고 관리하는데 많은 서버 자원을 할당하기 때문입니다. 따라서 데이터베이스 연결을 불필요하게 길게 유지하거나 너무 잦은 연결 열기/닫기는 앱 효율성은 떨어뜨리며 성능에도 좋지 못한 영향을 줍니다.

Fox DB Access 는 데이터베이스에 대한 연결 관리 기능을 가지고 있습니다. 즉, 데이터베이스 액세스에 필요한 부분에서만 연결을 열고 불필요하게 길게 연결이 유지 되지 않도록 해줍니다. 이 문서에서는 FoxDbAccess 클래스(및 그 파생 클래스들)가 제공하는 연결 관리에 대해 살펴볼 것입니다.

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

연결 관련 메서드/속성

FoxDbAccess 클래스는 연결에 관련된 몇가지 메서드와 속성을 제공합니다. 이들은 데이터베이스 연결을 열기 위한 Open 메서드, 연결을 닫기 위한 Close 메서드 그리고 연결 객체를 반환하는 DbConnection 속성입니다.

Open 메서드는 그 의미가 매우 명확하므로 상세한 설명은 필요 없습니다. Close 메서드 역시 상세한 설명이 필요없을 만큼 명확하지만 dispose 패턴과 관련이 있습니다. FoxDbAccess 클래스는 IDispose 인터페이스를 통해하여 dispose 패턴을 제공하며 객체가 해제될 때 Close 메서드를 호출합니다. 따라서 using 문을 사용하면 예외 발생 여부와 상관없이 데이터베이스 연결이 닫히도록 코드를 작성할 수 있습니다.

using FoxDbAccess dbAccess = FoxDbAccess.CreateDbAccess();
// Database 액세스 코드 (생략)

DbConnection 속성은 FoxDbAccess 객체가 내부적으로 생성하여 유지하는 IDbConnection 객체를 반환합니다. IDbConnection 객체는 CreateDbAccess 메서드에 의해 생성된 구체 클래스에 따라 다릅니다. 예를 들어 CreateDbAccess 메서드가 구성 설정에 의해 FoxSqlDbAccess 인스턴스를 생성했다면 DbConnection 속성이 반환하는 IDbConnection 객체는 실제로 SqlConnection 객체입니다. 비슷하게 PostgreSQL 에 대한 구성 설정을 사용한다면 DbConnection 속성은 NpgsqlConnection 객체가 반환됩니다.

FoxDbAccess 에서 파생된 구체 클래스들은 DbConnection 속성 대신 Connection 속성을 제공합니다. 두 속성이 반환하는 객체는 동일한 객체이지만 DbConnection 속성은 데이터베이스에 독립적인 IDbConnection 타입을 반환하지만 Connection 속성은 실제 구체적인 타입인 SqlConnection, OracleConnection, NpgsqlConnection 등의 타입을 반환합니다.

예를 들어 PostgreSQL 을 사용하는 구성 설정을 사용한다고 가정해 봅시다. 이 경우 다음과 같은 Assert 호출을 수행할 수 있습니다.

1
2
3
4
5
6
using FoxDbAccess dbAccess = FoxDbAccess.CreateDbAccess();
FoxNpgsqlDbAccess? npgsqlDbAccess = dbAccess as FoxNpgsqlDbAccess;
Debug.Assert(npgsqlDbAccess != null);
Debug.Assert(dbAccess.DbConnection is NpgsqlConnection);
Debug.Assert(npgsqlDbAccess.Connection is NpgsqlConnection);
Debug.Assert(Object.ReferenceEquals(dbAccess.DbConnection, npgsqlDbAccess.Connection));

Note

DbConnection 속성은 FoxSqlDbAccess, FoxNpgsqlDbAccess 와 같은 구체 클래스들에서 자동 완성(IntelliSense) 목록에 표시되지 않습니다. 하지만 이 속성은 자동 완성에 표시되지 않도록 설정되어 있을 뿐 속성 접근시 오류를 유발하지 않습니다.

자동 연결

FoxDbAccess 클래스의 데이터베이스 액세스 메서드들(Execute- 시리즈 메서드들)은 자동으로 연결을 열고 닫을 수 있습니다. 즉, 명시적으로 Open 메서드를 호출하지 않으면 쿼리 수행전에 데이터베이스 연결을 열고 SQL 혹은 저장 프로시저를 수행한 후 연결을 닫습니다.

예를 들어, 다음 코드는는 FoxDbAccess 인스턴스를 생성한 후 Open 메서드 호출 없이 ExecuteSqlDataSet 메서드를 호출하여 쿼리를 수행합니다.

1
2
3
using FoxDbAccess dbAccess = FoxDbAccess.CreateDbAccess();
DataSet ds = dbAccess.ExecuteSqlDataSet("SELECT * FROM mytable");
int rowCount = ds.Tables[0].Rows.Count;

모든 Execute- 시리즈 메서드들은 쿼리 수행전에 데이터베이스 연결 상태가 Open 이 아니라면 먼저 Open 메서드를 호출하여 연결을 수행합니다. 이후 쿼리를 수행하고 데이터베이스로부터 수행 결과를 수신합니다. 쿼리 수행 전에 연결 상태에 따라 암시적으로 Open 메서드를 호출했었다면 Close 메서드를 호출하여 데이터베이스 연결을 닫고 메서드를 종료합니다. 즉, FoxDbAccess 클래스는 데이터베이스 액세스전에 연결 상태를 확인하고 연결되지 않은 경우 암시적으로 Open/Close 작업을 수행해 주는 것입니다.

암시적인 Close 작업은 쿼리 수행 도중 발생할 수 있는 오류 상황에서도 항상 수행됩니다. 암시적으로 Open 된 연결에 대해 FoxDbAccess 는 오류와 무관하게 데이터베이스 연결이 닫힌다는 것을 보장합니다. 개발자는 데이터베이스 연결이 열리고 닫는 것에 대해 크게 걱정할 필요없이 Execute- 시리즈의 메서드만을 호출할 수 있습니다.

수동 연결

자동 연결이 데이터베이스 연결을 관리해주기 때문에 편리한 점을 가지고 있지만 모든 상황에서 장점만을 가지고 있는 것은 아닙니다. 예를 들어, 연속적으로 2회의 쿼리를 수행하는 상황을 고려해 봅시다.

1
2
3
4
using FoxDbAccess dbAccess = FoxDbAccess.CreateDbAccess();
int rowCount1 = (int)dbAccess.ExecuteSqlScalar("SELECT COUNT(*) FROM table1");
int rowCount2 = (int)dbAccess.ExecuteSqlScalar("SELECT COUNT(*) FROM table2");
// rowCount 값 활용 (생략)

위 코드는 매우 잘 작동합니다. 하지만 효율적인 면에서나 성능적인 면에서는 좋지 못한 코드입니다. 2회의 ExecueSqlScalar 메서드 호출에서 암시적인 데이터베이스 연결 Open/Close 가 2회 발생하기 때문입니다.

보다 효율적인 코드는 다음과 같이 명시적으로 OpenClose 메서드를 호출하는 것입니다.

using FoxDbAccess dbAccess = FoxDbAccess.CreateDbAccess();
dbAccess.Open();
try
{
    int rowCount1 = (int)dbAccess.ExecuteSqlScalar("SELECT COUNT(*) FROM table1");
    int rowCount2 = (int)dbAccess.ExecuteSqlScalar("SELECT COUNT(*) FROM table2");
    // rowCount 값 활용 (생략)
}
finally
{
    dbAccess.Close();
}

Execute 시리즈 메서드들은 데이터베이스 연결이 이미 Open 상태라면 암시적인 Open 메서드 호출을 수행하지 않습니다. 또한 암시적인 Open 이 수행되지 않았으므로 암시적인 Close 메서드 호출 또한 수행하지 않습니다. 따라서 위와 같은 코드는 1회의 Open/Close 만이 수행됩니다.

명시적으로 Open 메서드를 호출하는 경우 try~finally 구문을 사용하여 Close 메서드를 호출해 주는 것이 좋습니다. 물론 using 문에 의해 FoxDbAccess 객체가 해제(dispose)될 때 닫히지 않은 연결을 닫아줄 것입니다. 하지만 dispose 패턴에 의존하는 것은 Close 메서드를 명시적으로 호출하는 것보다 데이터베이스 연결이 Open 된 상태로 오랫동안 유지되며 데이터베이스 서버 자원이 낭비될 수 있습니다. 작은 규모의 앱에서는 이러한 코딩 패턴이 큰 문제가 아닐 수 있지만 다수의 사용자를 갖는 대규모 앱에서는 서버 자원을 빠르게 소진시킬 수 있으므로 주의해야 합니다.

기타 사항

DataReader 고려 사항

ExeucteSqlReader, ExecuteSpReaderDataReader를 반환하는 Execute- 시리즈 메서드들은 앞서 언급한 자동 연결에서 추가적인 고려 사항이 발생합니다. DataReader 를 통해 데이터를 읽는 동안 데이터베이스 연결이 유지되어야 하기 때문입니다. 데이터베이스 연결이 되지 않은 상태로 DataReader 를 반환하는 Execute- 메서드들은 쿼리 수행전에 데이터베이스 연결 상태가 Open 이 아니라면 먼저 Open 메서드를 호출하여 연결을 수행합니다. 그리고 CommandBehavior 설정을 CloseConnection 으로 지정합니다. 따라서 DataReader 를 닫을 때 데이터베이스 연결도 같이 닫힙니다.

한편, 명시적으로 Open 메서드가 호출된 이후에 DataReader 를 반환하는 Execute- 메서드들이 호출되면 CommandBehavior 설정은 수행되지 않습니다. 따라서 DataReader 가 닫히더라도 연결이 닫히는 경우는 없습니다.

트랜잭션 고려 사항

Execute- 시리즈 메서드들은 연결의 Open 여부에 따라 자동으로 연결을 수행하지만 그렇지 않은 메서드들도 존재합니다. 트랜잭션을 시작/커밋/롤백하는 BeingTrans/CommitTrans/RollbackTrans 메서드들은 데이터베이스 연결이 Open 상태가 아니라면 예외를 발생합니다. 따라서 트랜잭션을 시작하기 전에는 반드시 데이터베이스 연결을 수행해야 합니다.

using FoxDbAccess dbAccess = FoxDbAccess.CreateDbAccess();
dbAccess.Open();
try
{
    dbAccess.BeginTans();
    try
    {
        // 데이터 액세스 코드 (생략)
        dbAccess.CommitTrans();
    }
    catch 
    {
        dbAccess.RollbackTrans();
        throw;
    }
}
finally
{
    dbAccess.Close();
} 

Note

Fox Transaction 이 제공하는 선언적 트랜잭션을 사용하면 프레임워크에 의해 트랜잭션이 시작하고 종료되므로 위 코드와 같이 복잡한 try~catch~finally 구문 내에서 명시적으로 커밋과 롤백을 수행할 필요가 없습니다. 프레임워크에 의해 데이터베이스 연결이 열리거나 닫히며 트랜잭션 역시 자동으로 시작되고 예외 발생 여부에 따라 트랜잭션은 커밋되거나 롤백됩니다.

Summary

FoxDbAccess 는 편리한 데이터베이스 액세스를 위해 데이터베이스 연결을 자동으로(암시적으로) 열거나(open)하고 닫아(close)줍니다. 하지만 데이터베이스 액세스를 여러차례 수행해야 하거나 BeginTrans 메서드를 사용하여 트랜잭션을 사용해야 하는 경우에는 수동으로(명시적으로) Open/Close 메서드를 호출하여 데이터베이스 연결을 관리해 주어야 합니다.