login register Sysop! about ME  
qrcode
    최초 작성일 :    2007년 08월 01일
  최종 수정일 :    2007년 08월 21일
  작성자 :    Spacesj
  편집자 :    SpaceSj(김 석중)
  읽음수 :    16,830

강좌 목록으로 돌아가기

필자의 잡담~

이번 강좌는 태오닷넷에서 활동 중인 김석중님의 강좌입니다. 인터페이스에 관한 기본적인 이야기들을 설명해본다고 합니다. ~

뜬금없이 ASP.NET 강좌는 안쓰고 Interface 얘기를 꺼냈습니다. ^^ ASP.NET 게시판의 질문에 답변을 하다보면 무엇인가 조금 부족하다 생각하는 부분이 몇가지 있는데요. 가능하다면 그에 관련해서 전부 강좌를 진행해 보고 싶지만, 나름 유용하게 사용하고 써먹을(?) 수 있는 것부터 정리해 보기로 했습니다.

이 강좌는 아래와 같은 순서로 되어있습니다.

1. 시작하기
2. IDisposable Interface
3. IDisposable Interface Example
4. 맺음말

 

1. 시작하기

프로그램을 하다보면 다른 시스템과의 통신이 필요하고 그에 여러 얘기를 하면서, 'Interface가 어쩌고, 멀 넘겨야 하고..' 등등의 얘기가 나오게 되지요. 이런 얘기를 토대로 추측을 해보면 'Inteface란 약속/규약이구나.'라고 생각을 하실 수 있으실겁니다. 그렇다면 .NET Framework에 존재하는 Interface는 무엇일까요?

인터페이스(interface)

계약을 정의하는 참조 형식입니다. 다른 형식은 특정 작업을 지원할 수 있도록 인터페이스를 구현하는데, 이러한 인터페이스는 해당 인터페이스를 구현하는 클래스나 다른 인터페이스에서 제공해야 하는 멤버를 지정합니다. 클래스와 마찬가지로, 인터페이스의 멤버는 메서드, 속성, 인덱서, 이벤트 등이 될 수 있습니다. 참고 항목: 계약(contract), 인덱서(indexer), 속성(property), 참조 형식(reference type)

- MSDN 발췌

 Interface (C#) - MSDN

우선 MSDN에서 발췌한 Interface의 설명을 보면 위와 같습니다. 말이 좀 어려워서 이해하기 난해하지만, 간단하게 설명을 하면 Class/Interface에서 공통적인 멤버(속성, 멤버, 이벤트 등)를 사용할 수 있도록 만들어 놓은곳이라는 뜻으로 이해하시면 조금 쉬울거라 생각이 드네요.


그림 1. System Namespace Interface (MSDN)

위의 이미지는 System Namespace의 일부 Interface를 보여주고 있습니다. 보시다 시피 Interface는 기본적으로 영문 대문자 "I"를 Prefix로 가집니다. 일부 틀린부분들도 있지만 대부분은 이 규격을 따르고 있죠.

으아~~ 단순히 System Namespace에만 저렇게 있는데 Framework의 모든 Interface를 나열하면 너무 많아 이 강좌는 쓰고 또 쓰다가 Framework가 변경되면 또 쓰고, 변경하고 해야 할 것 같습니다. ^^;; 그래서 우리가 알게 모르게 많이 사용하고 있는 Dispose 메서드를 멤버로 가지고 있는 IDisposable Interface에 대해서 알아보고 Interface란 이런거구나 하고 감을 잡으셨으면 합니다. 사실 저도 저많은 Interface 중에 몇가지나 알고 있는지 모르겠습니다. 너무 많아서.. ㅋㅋ

 

2. IDisposable Interface


그림 2. IDisposable Interface (MSDN)

으~~ 넌 누구냐?

가비지 수집기는 관리되는 개체가 더 이상 사용되지 않을 경우 해당 개체에 할당된 메모리를 자동으로 해제하지만 가비지 수집이 언제 발생할지 예측할 수 없습니다. 또한 가비지 수집기는 창 핸들, 열린 파일 및 스트림과 같이 관리되지 않는 리소스를 인식하지 못합니다.

이 인터페이스의 Dispose 메서드를 가비지 수집기와 함께 사용하여 관리되지 않는 리소스를 명시적으로 해제합니다. 개체가 더 이상 필요하지 않을 경우, 개체의 소비자는 이 메서드를 호출할 수 있습니다.

- MSDN 발췌

라고 설명이 되어 있습니다. 설명그대로 입니다. 할당된 메모리는 가비지 수집기(Garbage Collector)에 의해 사용되지 않음을 판단한 후 자동으로 해제 합니다. 하지만 위에서 밝힌데로 관리되지 않은 리소스 인식을 못할 수 도 있고 언제 실행이 될지도 모르기 때문에 이 Interface를 상속받고 멤버를 사용함으로써 명시적으로 해제를 시키는 작업을 하게 됩니다.

 가비지 수집 (Garbage Collection)

 Finalize 및 Dispose를 구현하여 관리되지 않는 리소스 정리 - MSDN

자 그럼 IDisposable Interface의 멤버는 무엇이 있는지 확인해 보겠습니다.


그림 3. IDisposable Interface Member (MSDN)

오홀~~ 딱 1개 밖에 없습니다. Dispose 메서드만 존재하는데요. Dispose 메서드는 잘 아실거라 생각합니다. 할당된 메모리를 해제 할 수 있도록 하는 메서드구요. IDisposable Interface를 상속받으면 Class의 다른 멤버들과 함께 Dispose 메세드가 꼭 존재 하여야 합니다.

그럼 IDisposable Interface가 직접 쓰이는 Class를 한 번 찾아보겠습니다. 이게 어떻게 사용이 되고 있고 정말 Dispose 메서드가 존재 하는지 까지 확인을 해보도록 하지요. 대표적으로 우리가 많이 쓰는 SqlConnection을 보겠습니다.


그림 4. SqlConnection Class (MSDN)

'머야~~ IDisposable Interface가 쓰인곳을 본다면서' 라고 성급하게 생각하시면 안됩니다. ^^;; SqlConnection Class는 DbConnection Class 상속받기 때문에 DbConnection Class를 보셔야 합니다. 보도록 하지요.


그림 5. DbConnection Class (MSDN)

DbConnection Class를 보게되면 IDisposable Interface를 상속받게 되어있습니다. 즉 SqlConnection Class는 IDisposable Interface를 상속받는 DbConnection Class를 다시 상속받게 되어 있다고 알겠죠?

여기서 잠깐, DbConnection Class에서 상속받는 IDbConnection Interface도 IDisposable Interface를 상속받습니다. 이래저래 IDisposable Interface를 빠져나갈 공간이 없네요. ^^;


그림 6. DbConnection Class Member (MSDN)


그림 7. SqlConnection Class Member (MSDN)

그림 8,9에서 보시다시피 각각의 클래스에 Dispose 메서드가 정의되어 있음을 확인할 수 있습니다. 즉 IDisposable Interface를 상속받는 모든 클래스에는 Dispose 메서드가 포함이 되어 있지요.

자 이제 간단한 웹사이트를 생성하고, 클래스 파일을 추가하여 어떻게 동작을 하는지 보겠습니다.

 

3. IDisposable Interface Example

웹사이트를 추가해서 테스트를 진행해 보겠습니다. 완벽한 Class가 목적이 아닌 IDisposable Interface를 확인하기 위한 것이니 소스가 좀~~ 그래도 양해부탁드립니다. ^^


그림 8. 웹 사이트 생성

우선 웹사이트를 생성하고, App_Code 폴더를 생성합니다.


그림 9. InterfaceTest Class 추가

App_Code 폴더에 InterfaceTest Class 파일을 생성합니다. 그리고 Interface Class 파일의 내용은 아래와 같습니다.

// Using 구문 제외

public class InterfaceTest : IDisposable
{
    public InterfaceTest()
    {
    }
}

소스 1. InterfaceTest.cs

너무 간단하죠? 단순히 class에 IDisposable을 상속받도록 합니다. 빌드해보겠습니다.


그림 10. 빌드 오류

어허.. 오류가 떡하니 발생을 합니다. 오류 내용을 보시면 아시겠나요? ^^ 위에서 설명을 드린데로 Interface를 상속받는 Class는 Interface의 멤버를 전부 구현해주어야 합니다. 즉 IDisposable Interface의 멤버인 Dispose 메서드를 추가해서 다시 빌드하겠습니다.


그림 11. Dispose 메서드 추가 1


그림 12. Dispose 메서드 추가 2

그림 11, 12와 같이 해당 Interface를 우클릭하여 메뉴를 선택하면 해당 Interface의 멤버들이 전부 작성이 됩니다. 이것 참 쉽지 않습니까? ^^ 물론 직접 입력을 하셔도 됩니다.


그림 13. Dispose 메서드 추가 후 빌드 성공

자 이제 정상적으로 빌드가 되었습니다. 하지만 이 상태로는 이 Class의 Dispose 메서드가 정상적으로 작동을 하는지 알아볼 수 없습니다. 소스를 또 몇가지 수정을 해보도록 하지요. ^^

// Using 구문 제외

public class InterfaceTest : IDisposable
{
    private static int nCount = 0;

    public InterfaceTest()
    {
        nCount++;
    }

    public void Dispose()
    {
        nCount--;
    }

    public static int GetCount()
    {
        return nCount;
    }
}

소스 2. 수정된 InterfaceTest.cs

완성된 InterfaceTest.cs 의 모습입니다. 정적Int변수 nCount는 인스턴스 생성이 될때마다 +1, Dispose 메서드가 호출되서 해제될때마다 -1을 하게 됩니다. GetCount 메서드는 nCount의 값을 넘겨주게되어 있습니다.

사실 말이죠. Framework의 Dispose 메서드는 단순하게 위와 같이 사용하지는 않습니다. Reflector로 까보면, Dispose 메서드내에 무척 많은 코드가 존재하구요. 아니면 Close 메서드 등으로 대체해서 사용도 하고 있습니다. 또 bool 값을 체크하여 사용하도록 Dispose(bool bValue) 와 같이 Override 해서 사용도 하고 있구요. 여기서는 테스트를 위해서 이렇게 사용을 한다고 아셨으면 좋겠습니다. ^^

여기까지 Class를 구성하였고, 이제 WebForm에서 이들에 대한 테스트를 할 수 있도록 구성하겠습니다.


그림 14. Default.aspx

.aspx에는 Label 3개와 Button 3개를 올려놓았습니다. .aspx.cs에는 Button3개의 Click 이벤트만 추가를 하여 각각을 테스트 할 수 있도록 구성하겠습니다.

// Using 구문 제외

protected void Page_Load(object sender, EventArgs e)
{
    this.ClearLabel();
}

// ① Test1 Button Click
protected void btnTest1_Click(object sender, EventArgs e)
{
    this.SetLabelText(this.Label1, "Test1 Button Click<br>인스턴스 전");

    InterfaceTest iTest = new InterfaceTest();

    this.SetLabelText(this.Label2, "인스턴스 후");

    this.SetLabelText(this.Label3, "Dispose 메서드 호출 후");
}

// ② Test2 Button Click
protected void btnTest2_Click(object sender, EventArgs e)
{
    this.SetLabelText(this.Label1, "Test2 Button Click<br>인스턴스 전");

    InterfaceTest iTest = new InterfaceTest();

    this.SetLabelText(this.Label2, "인스턴스 후");

    iTest.Dispose();

    this.SetLabelText(this.Label3, "Dispose 메서드 호출 후");
}

// ③ Test3 Button Click
protected void btnTest3_Click(object sender, EventArgs e)
{
    this.SetLabelText(this.Label1, "Test3 Button Click<br>인스턴스 전");

    using (InterfaceTest iTest = new InterfaceTest())
    {
        this.SetLabelText(this.Label2, "인스턴스 후");
    }

    this.SetLabelText(this.Label3, "Dispose 메서드 호출 후");
}

// ④ Label strMessage 출력
private void SetLabelText(Label label, string strMessage)
{
    label.Text = string.Format("{0} : {1}", new object[] { strMessage, InterfaceTest.GetCount() });
}

// ⑤ Label Clear
private void ClearLabel()
{
    this.Label1.Text = string.Empty;
    this.Label2.Text = string.Empty;
    this.Label3.Text = string.Empty;
}

소스 3. Default.aspx.cs

Click Event이외의 메서드는
④ 해당 Label에 메세지를 출력합니다.
⑤ 페이지가 로딩될때 Label의 내용을 초기화합니다.
어려운 부분은 없으시죠?

그리고 버튼 이벤트의 SetLabelText는 각각의 상태에 따라 nCount의 변화를 위해서 공통적으로 쓰였습니다. 그럼 버튼 하나씩 Click해서 결과 화면과 함께 내용을 설명하겠습니다.


그림 15. Default.aspx - Test1 Button Click

① 인스턴스를 생성하고 Dispose 메서드를 호출하지 않아 nCount수가 1로 나오고 있습니다. 이렇게 사용을 하게 되면 언제 가비지 수집기가 동작을 할지 모르게 되겠죠? 그래서 대부분 ②와 같이 명시적으로 Dispose 메서드를 호출합니다.


그림 16. Default.aspx - Test2 Button Click

② 가장 정석적인 방법을 사용하였습니다. 인스턴스 생성 후 Dispose 메서드를 호출하여 메모리에서 해제합니다. 그러므로 인스턴스 전과 Dispose 메서드 호출후에 nCount 값이 동일해 졌습니다.


그림 17. Default.aspx - Test3 Button Click

③ 오호. 여기에는 Dispose 메서드를 호출하지 않았는데도 ②와 같은 상황이 발생하였습니다. 이건 using 구문을 사용하여 using 구문내에서만 인스턴스 생성후 구문이 끝날때 Dispose 메서드를 자동 호출하였기 때문입니다.

①, ②는 평상시에 많이 사용을 하고 계실거라 생각이 되어 추가 설명은 하지 않겠습니다. 하지만 ③은 조금 더 설명을 드려야 할 것 같습니다. '도대체 using이 먼데 Dispose를 자동 호출 하는 것이야?' 라고 생각을 하시겠지요?

C#의 using은 두가지 기능을 수행합니다. Namespace를 사용할 수 있게 하는 지시문으로써, 또 하나는 개체의 범위를 설정하여 사용할때의 구문으로써 사용이 가능합니다. ③ 에서 사용한 using은 두번째 설명에 해당이 됩니다. using을 사용하여 인스턴스를 생성하고 using이 끝날때 Dispose를 호출하여 해제를 합니다.

그렇다면 using을 사용해서 메모리를 자동 해제 하고자 할때 해당 Class에 Dispose 메서드만 있으면 될까요? 한 번 해보도록 하지요. InterfaceTest.cs에서 IDisposable Interface 부분을 삭제하고 Default.aspx를 실행해 보겠습니다.


그림 18. Default.aspx - Error

으히~~ 떡하니 에러가 발생을 하네요. 에러 내용중 오류 메세지를 보시면, 'CS1674: 'InterfaceTest': using 문에 사용된 형식은 암시적으로 'System.IDisposable'로 변환할 수 있어야 합니다.' 이렇게 나옵니다. 즉 using 구문을 사용하여 메모리 해제를 하려면 Class에 IDisposable Interface를 사용하여 Dispose 메서드를 구성해야 합니다. ^^

앞서 본 SqlConnection도 같은 방법으로 사용이 가능합니다. SqlConnection Class가 상속받는 DbConnection Class에 IDisposable Interface 상속이 되어 있으므로..

using (SqlConnection con = new SqlConnection())
{
    // SQL 연결 처리 구문 (생략)
}


소스 4. using을 사용한 SqlConnection

위와 같이 사용이 가능합니다.

휴우~~ 여기까지 소개를 드려야 할것 같습니다. 원래 강좌의 목적은 Interface를 알아보자라는 목적이었는데, 더 많은 내용이 들어갈 것 같아 이건 아니다 라는 생각이 드는군요. 차후 또 기회가 된다면 (제가 게으름만 부리지 않는다면.. -_-) 여기서 못다한 얘기들을 꺼내보겠습니다.

 

5. 맺음말

여기까지가 IDisposable Interface를 이용한 Interface 알아보기 였습니다.

Interface에 대해서 저도 얼마전까지는 그다지 신경을 쓰지 않았고, '이거 머냐? 꼭 알아야 하나?'라는 생각이 참 많았습니다. 하지만 여러 작업들을 하면서 Class를 만들고 확장을 하다보니 Framework의 Class를 상속받아서 사용을 하게 되는데요. 그 상속된 Class를 까보면 꼭 Interface가 있더라는 것이지요. '그럼 이넘은 무엇인가?' 라는 생각에 또 무작정 달려보니 '아하~ 이넘은 이런걸 하고, 오호~ 저넘은 저런걸 하는구나!' 하고 알게 되어서..

혹시 저와 같은 길을 걸으시는 분이 계시다면 도움이 될까 해서 강좌로 정리를 해보았습니다. ^^ 마침 관련된 질문을 해주시는 분도 계셨구요.

여기서 언급한 IButtonControl, IDisposable, 잠시 보인 IDbConnection, ICloneable, IPostBackEventHandler외에도 정말 무척이나 많은 Interface가 존재합니다. Convert.ToInt16 등을 사용하기 위한 IConvertible, 어떤 두개체를 비교하기 위한 Compare메서드를 가지고 있는 IComparer, 컬렉션에 Count 속성을 가지고 있는 ICollection, DataSource Control을 만들기 위한 IDataSourceDesigner 등 그냥 무심코 사용을 하고 있었지만, 사실은 그렇게 사용을 위해 미리 다 정의가 되어있었다는 사실을 알게 되실거라 생각을 합니다.

전 차후에 다른 강좌로 다시 찾아뵐것을 약속드립니다. ^^

감사합니다.


authored by


 
 
.NET과 Java 동영상 기반의 교육사이트

로딩 중입니다...

서버 프레임워크 지원 : NeoDEEX
based on ASP.NET 3.5
Creative Commons License
{5}
{2} 읽음   :{3} ({4})