메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

IT/모바일

닷넷, 인터페이스 이해하기

한빛미디어

|

2003-09-08

|

by HANBIT

14,597

저자: 닉 해리슨(Nick Harrison), 역 한동훈

‘인터페이스’는 닷넷에서 처음으로 소개되었지만 이 개념을 이해하기엔 다소 혼란스러운 면이 있다. 인터페이스는 함수들의 서명을 정의하는 일종의 계약이며 닷넷 프레임워크 전반에 걸쳐서 일반적으로 잘 알려진 행위 또는 메소드를 정의하는데 사용되었다. 예를 들어 복제될 수 있는 닷넷 형식(Type)과 복제를 수행하는 방법을 정의하기 위해 ICloneable 인터페이스를 정의하는 것처럼 닷넷 형식(Type)의 특정 함수와 동작을 알려주거나 해당 동작에 대한 메소드와 속성을 정의하기 위해 인터페이스를 사용한다. 닷넷은 하나의 객체를 상속받을 수도 있지만 인터페이스를 여러 개를 상속받아 구현할 수 있다. 즉, 공통된 메소드를 정의하기 위해 여러 개의 인터페이스를 사용할 수 있으며, 인터페이스를 사용하는 객체는 공통된 메소드를 정의해야 한다. 클래스가 다른 클래스를 상속 받으면 부모 클래스로부터 모든 메소드와 속성을 자동으로 물려받는다. 이처럼 상속 받은 클래스의 용도는 필요에 따라 다른 기능을 구현하기 위해 메소드를 재정의(override)하거나 기능을 확장하는 것이다. 클래스가 인터페이스를 구현할 때 컴파일러는 클래스에서 정의해야 하는 메소드, 속성들을 표기하고 인터페이스를 준수했는지 확인한다.

본 기사에서는 닷넷에서 인터페이스가 어떻게 사용되는지 살펴보고, 왜 인터페이스를 정의해서 사용해야 하는지 논의한 후, 직접 인터페이스를 정의하고 구현하는 예제를 단계별로 살펴볼 것이다.

미처 생각하지 못한 것들

닷넷을 공부하다 보면, 닷넷 프레임워크 전반에 걸쳐 인터페이스를 보게 된다. 이전 기사에서는 실제 데이터베이스 종류에 관계없이 사용할 수 있는 데이터베이스 액세스 레이어를 구축하는데 있어 인터페이스가 얼마나 중요한가를 소개했었다. 인터페이스를 사용한 것 중에 우리에게 익숙한 예로는 foreach 루프가 있다.
foreach (type identifier in expression) statement
위와 같은 간단한 루프 구조는 실제로 인터페이스에 기반을 둔 것이다. foreach을 사용하는 데 있어 우리가 미처 생각하지 못했던 것으로 다음과 같은 두 가지가 있다.
  1. 표현식은 IEnumerable 인터페이스를 반드시 구현해야 한다
  2. 컬렉션에 있는 항목은 지정된 type으로 변환될 수 있어야 한다
데이터 바운드 컨트롤을 사용한 경험이 있다면 DataSource 속성을 잘 알고 있을 것이며, 이 속성에 대한 도움말을 찾아보면 이 속성(property)이 예상하는 값은 IEnumerable 인터페이스를 구현한 형식(Type)이라는 것을 알 수 있다.

다시 말해, 여러분이 만든 클래스를 DataSource로 사용하거나 foreach 루프를 사용해 반복하고 싶다면 기본 클래스들과 마찬가지로 IEnumerable 인터페이스를 구현하기만 하면된다. 닷넷 프레임워크에 있는 다양한 인터페이스를 사용하면 여러분이 만든 클래스를 닷넷 프레임워크와 간단하게 융화시킬 수 있다.
  • IComparable 인터페이스를 사용하여 클래스를 정렬할 수 있게 한다.
  • IConeable 인터페이스를 사용하여 클래스를 복제할 수 있게 한다. 복제는 단순히 클래스의 다른 인스턴스 즉, 원본의 복사본을 생성하는 것이다.
  • IFormattable은 클래스가 ToString() 메소드와 함께 사용될 때 문자열 변환 방식을 바꾸기 위해 사용된다.
  • IDisposable 인터페이스는 클래스에서 사용하는 COM 객체등의 비관리 리소스를 해제하는 기능을 제공하기 위해 사용된다.
클래스를 만들 때 이러한 인터페이스를 적절히 사용하면 클래스를 닷넷 프레임워크와 보다 밀접하게 통합할 수 있으며 다른 사용자가 클래스를 사용할 때 클래스의 동작을 예상하기가 쉬워진다. 이와 같이 미리 정의된 인터페이스를 구현하는 것 외에 디자인에 사용하기 위해 직접 인터페이스를 정의할 수도 있다.


.NET Framework Essentials, 3rd Edition

참고 도서

.NET Framework Essentials, 3rd Edition
Thuan L. Thai, Hoang Lam




인터페이스 설계

인터페이스 정의를 자세히 살펴보기 전에 왜 인터페이스를 정의해야 하는지부터 살펴보자. 가장 확실한 이유는 비즈니스 로직과 UI를 보다 명확하게 구분할 수 있다는 점, UI의 동작을 보다 잘 정의할 수 있다는 점 때문일 것이다. 예를 들어, 멤버 프로파일과 관련된 모든 필드의 속성을 정의하는 IMemberProfile 인터페이스가 있다고 하자. 이제 사용자가 자신의 프로파일을 조회하고 수정하는 웹 페이지는 System.Web.UI.Page 클래스를 상속할 뿐만 아니라 IMemberProfile 인터페이스를 구현해야 한다. IMemberProfile 인터페이스를 정의하는 것은 웹 페이지 외에 모바일 페이지, 윈도우 폼, 사용자 컨트롤, 사용자 지정 컨트롤 등이 될 수도 있다. IMemberProfile에서 정의된 메소드는 사용된 기본 클래스가 무엇인지는 상관하지 않는다. 인터페이스가 간여하는 것은 인터페이스를 구현하는데 필요한 모든 것을 클래스에서 구현했는가에 대한 것이다. 이러한 메소드들은 속성이 참조하는 것이 지역 변수, 드롭 다운 메뉴, 텍스트 박스, 데이터베이스, XML 스트림인지는 상관하지 않는다. 중요한 것은 ‘이러한 속성이 구현되었는가’하는 것이다.

모든 비즈니스 로직을 사용자 지정 인터페이스(custom interface)를 통해 메소드를 구현했다면 다양한 UI를 대상으로 하는 것이 쉬울 뿐만 아니라 데이터 요구 사항의 변화에도 각각의 UI를 보호할 수 있다. 새로운 데이터 값이 어떤 함수 호출을 지속적으로 요구하는 것처럼 최악의 경우에는 새 속성을 인터페이스 정의에 추가하고 각 UI는 새 속성을 구현해야만 한다. 각 UI는 항상 "this"나 VB의 경우에 "me"를 항상 전달하기 때문에 개별 함수 호출은 바뀌지 않는다.

IMemberProfile 인터페이스 정의

인터페이스를 정의하는 것은 구현만 하지 않는다는 것을 제외하면 클래스를 정의하는 것과 비슷하며 C/C++에서 헤더 파일을 정의하는 것과도 비슷하다.
public interface IMemberProfile
{
  string FirstName {get;set;}
  string LastName {get;set;}
  string Email {get;set;}
  System.Guid MemberID {get;set;}
}
예제에서는 FirstName, LastName, Email, MemberID와 같은 읽기/쓰기 속성을 가진 인터페이스를 정의하고 있으며, 이 인터페이스를 구현하는 클래스는 이들 속성을 정의해야 한다.

IMemberProfile 인터페이스 구현

인터페이스를 구현하려면 인터페이스를 정의할 클래스에 표기를 해야 하는데 C#에서는 상속과 비슷한 구문을 사용한다.
public class MemberProfile: System.Web.UI.Page, IMemberProfile
VB.NET에서는 조금 다른 구문을 사용한다.
Public Class MemberProfile
  Inherits System.Web.UI.Page
  Implements IMemberProfile
만약 인터페이스에서 요구하는 것이 클래스에 정의되어 있지 않다면 컴파일러가 경고를 할 것이다. 예를 들어, 클래스에는 FirstName, LastName, Email, MemeberID과 같은 속성들이 포함되어야 한다.
public string FirstName
{
 get{return txtFirstName.Text ;}
 set {txtFirstName.Text = value;}
}

public string LastName
{
 get{return txtLastName.Text ;}
 set {txtLastName.Text = value;}
}

public string Email
{
 get{return txtEmail.Text ;}
 set {txtEmail.Text = value;}
}

public System.Guid MemberID
{
  get
  {
    if (m_uniqueID.CompareTo (null) == 0)
    {
       m_uniqueID = System.Guid.NewGuid();
    }
    return m_uniqueID;
  }
  set 
  {
    m_uniqueID = value;
  }
}
인터페이스를 구현하는 것은 캡슐화에 대한 규칙을 제시하는 역할을 한다.

인터페이스 사용하기

인터페이스와 인터페이스를 구현하는 UI를 정의했으므로 이제 이것을 사용한 비즈니스 로직을 작성해보자. UI에 사용할 데이터를 가져오기 위한 메소드 두 개를 정의하고, UI에 현재 사용중인 데이터를 가져오는 메소드 하나를 정의할 것이다. 실제 예제에서는 이들 메소드가 정보를 가져오기 위해 데이터베이스를 사용하겠지만 여기서는 예제를 단순하게 하기 위해 지역 변수만 사용할 것이다. 이 예제에서 동작하는 것을 제대로 이해하기 위해 디버거를 사용하여 코드를 단계별로 수행하면서 접근하는 속성들을 살펴볼 것을 권한다.

현재 멤버 프로파일 정보를 가져오는 메소드는 다음과 같다.
public static void RetrieveMemberProfile (IMemberProfile Profile)
{
 Profile.FirstName = "John";
 Profile.LastName = "Doe";
 Profile.Email = "jdoe@email.com";
 Profile.MemberID = 
    new System.Guid  
    ("{65C38236-CA96-4FF1-9142-00873B8BD333}");
}
멤버 프로파일 정보를 업데이트하는 메소드는 다음과 같다.
public static void UpdateMemberProfile (IMemberProfile Profile)
{
  string _FirstName;
  string _LastName;
  string _Email;
  string _MemberID;
  
  _FirstName = Profile.FirstName;
  _LastName = Profile.LastName;
  _Email = Profile.Email;
  _MemberID = Profile.MemberID.ToString();
}
Page_Load 이벤트에서 RetrieveMemberProfile 메소드를 호출한다.
private void Page_Load(object sender, System.EventArgs e)
{
   // Put user code to initialize the page here
   if (!Page.IsPostBack)
   {
      BusinessRules.RetrieveMemberProfile (this);
   }
}
버튼에 대한 클릭 이벤트 핸들러는 다음과 같다.
private void btnUpdate_Click(object sender, System.EventArgs e)
{
  BusinessRules.UpdateMemberProfile (this);
}
결론

간단한 예제로 살펴본 것처럼 내장 인터페이스를 사용한 이점에 대해서 살펴보았으며, 인터페이스를 직접 정의하고 구현하는 방법이 얼마나 쉬운지 살펴보았다. 또한 예제에서 인터페이스를 사용하여 UI와 비즈니스 로직 간에 보다 강력한 분리를 할 수 있었다.
이 기사를 작성한 닉 해리슨(Nick Harrison)은 닷넷 옹호자로 전향한 UNIX 프로그래머이며, 현재 노스 캐롤라이나(NC)에 있는 Charlotte에서 일하고 있으며 금융권의 흥미로운 문제들을 해결하기 위해 닷넷을 사용하고 있다.
TAG :
댓글 입력
자료실

최근 본 상품0