Development/C#

[제프리 리처의 CLR via C#] 컴포넌트, 다형성, 버전 관리

오늘도 진이 2021. 7. 8. 16:13

2부. 타입 설계

6장. 타입과 멤버의 기본

컴포넌트, 다형성, 버전 관리

  • 객체 지향 프로그래밍(Object Oriented Programming, OOP)은 정말 많은 세월 동안 논의되어온 개념이다.
  • 1970년대 후반에서 1980년대 초반까지 객체 지향 프로그래밍을 처음 도입할 떄에는 응용프로그램의 크기는 지금보다 더 작았고 응용프로그램의 제작을 위하여 코드를 작성하는 개발자들은 모두 한 회사 소속이었다.
  • 오늘날 소프트웨어는 더욱 더 복잡해지고 있고 사용자들은 응용프로그램이 제공하는 그래픽 사용자 인터페이스(Graphic User Interface, GUI)와 메뉴 항목, 마우스 입력, 태블릿 입력, 프린터 출력, 네트워킹 등 수많은 기능들에 의존하고 있다. 이러한 이유로, 우리가 사용하는 운영체제와 개발 플랫폼들은 지난 수년간 꾸준히 발전해왔다. 더 나아가서 요즘은 응용프로그램 개발자가 사용자들이 원하는 모든 기능을 모두 구현할 수 없으며 비용 효율적인 방법이 존재하는 것도 아니다. 요즘의 응용프로그램들은 여러 다른 회사들이 생산한 코드로 구성되어 있다. 이 코드는 객체 지향 패러다임을 사용하여 긴밀하게 통합되어 있다.
  • 컴포넌트 소프트웨어 프로그래밍(Component SOftware Programming, CSP)은 OOP를 이 수준까지 끌어올렸다. 다음은 컴포넌트로서 가져야 할 성격들을 열거한 것이다.
    • 하나의 컴포넌트(.NET Framework에서는 하나의 어셈블리)는 "출발(Published)"된 것으로 간주한다.
    • 하나의 컴포넌트에는 식별할 수 있는 아이덴티티(이름, 버전, 문화권, 그리고 공개 키)가 존재한다.
    • 하나의 컴포넌트에는 영원히 유지되는 고유한 아이덴티티가 존재한다. 어셈블리 안에 들어 있는 코드는 다른 어셈블리에 정적으로 링크되는 일이 없으며, .NET 환경에서는 항상 동적 링크만 발생한다.
    • 하나의 컴포넌트는 의지하는 다른 컴포넌트들에 대해 정확한 정보를 표시한다. (참조 메타데이터 테이블)
    • 하나의 컴포넌트는 반드시 그 안에 들어있는 클래스와 멤버들에 대해 문서를 만들 수 있어야 한다. C#에서는 소스 코드 내부에 XML 주석을 추가하여 컴파일러의 /doc 명령 줄 스위치를 사용하여 이러한 목적을 성취할 수 있다.
    • 하나의 컴포넌트는 반드시 필요로 하는 권한에 대해 자세히 서술하고 있어야 한다. CLR의 코드 엑세스 보안(CAS) 기능을 통해 이를 달성할 수 있다.
    • 하나의 컴포넌트는 새로운 서비스를 제공하더라도 변하지 않는 인터페이스나 객체 모델을 제공해야 한다. 서비스 제공이라 함은 원래의 컴포넌트 버전과 호환성을 유지할 수 있는 새로운 버전의 컴포넌트를 제공하는 것이다. 일반적으로, 서비스 제공에는 버그 수정, 보안 패치, 그리고 약간의 기능 향상이 포함된다. 그러나 서비스 제공을 통해서는 새로운 종속성이나 추가적인 보안 권한을 요구할 수는 없다.
  • 컴포넌트 소프트웨어 프로그래밍에서의 가장 큰 부분은 버전 관리에 대한 것이다.
  • 컴포넌트는 지속적으로 변경될 것이고, 매번 다른 시간 스케줄에 따라 새롭게 배포될 것이다.
  • 버전 관리를 통하여 이전의 객체 지향 프로그래밍만으로는 달성할 수 없었던 완전히 새로운 차원의 컴포넌트 소프트웨어 프로그래밍을 단일 회사에 의하여 작성되고 테스트되고, 출시된 모든 개별 유닛에 적용할 수 있을 것이다.
  • .NET Framework에서는 버전 번호가 네 개의 파트로 구분되는데, 메이저 파트, 마이너 파트, 빌드 번호 파트, 리비전 번호 파트로 구성되어 있다
  • 메이저와 마이너 파트는 보통 어셈블리의 기능상 큰 특징을 설명하기 위한 목적으로 부여되는 것이고, 빌드 번호와 리비전 번호 파트는 어셈블리의 기능 집합 중 일부를 업데이트하는 서비스 제공을 목적으로 부여되는 것이다.
  • CLR이 가상 메서드, 속성, 이벤트를 호출하는 방법
    • 속성과 이벤트 역시도 내부적으로는 메서드로 구현되는 것이며, 이에 대한 내용은 각각 해당되는 장에서 다시 살펴보게 될 것이다.
    • 메서드는 특정한 타입에 대한 조작이나 연산을 수행하는 정적 메서드와 특정한 타입의 인스턴스에 대한 조작이나 연산을 수행하는 인스턴스 (비정적) 메서드로 구분된다.
    • 모든 메서드들은 이름과 원형, 그리고 void를 포함한 일정한 반환 타입을 가지고 있다. CLR은 한 타입 내에 같은 이름의 메서드 여러 개를 서로 다른 매개변수 또는 서로 다른 반환 타입을 사용하여 구분하는 것을 허용하고 있다.
    • 그래서 같은 이름에 같은 매개변수 목록을 가지지만 반환 타입만 다른 두 개의 메서드를 정의하는 것이 가능하다.
    • 타입을 설계할 때에는 반드시 정의하는 가상 메서드의 수를 가능한 최소화해야 한다.
      • 가상 메서드는 비가상 메서드보다 호출 속도가 느리다.
      • 가상 메서드는 JIT 컴파일러에 의하여 인라인으로 번역될 수 없어 성능에 영향을 줄 개연성이 크다.
      • 가상 메서드는 컴포넌트의 버전 관리를 힘들게 만든다.
      • 기본 타입을 선언할 떄에는 편의를 위한 오버로드 메서드들의 집합을 제공하는 것이 보편적이다.
    • 타입의 가시성과 멤버의 접근성을 지능적으로 활용하는 방법
      • .NET Framework에서 응용프로그램들은 여러 회사들이 만드는 다수의 어셈블리들 안에 정의되는 타입들의 조합으로 구성된다.
      • 봉인되지 않은 클래스보다는 봉인된 클래스가 더 나은 선택인 이유
        • 버전관리
        • 성능
        • 보안 향상과 예측의 용이성
      • 봉인된 클래스의 문제점은 타입을 사용하는 사용자들에게 매우 큰 불편함이 될 수 있다는 것이다.
      • 개발자들은 기존 타입으로부터 상속을 받아 새로운 클래스를 만들어 추가적인 필드나 상태 정보를 더하여 그들이 만드는 응용프로그램에 반영하기를 원한다.
    • 새로운 클래스를 정의할 떄 따르는 스스로의 가이드라인들
      • 클래스를 정의할 때에는 만드는 클래스에 확실히 상속을 이용하여 새로운 기능의 확장을 허용하도록 만들기 전까지는 명시적으로 sealed 키워드를 사용하여 상속이 불가능하도록 만든다.
      • 클래스 안에서는 항상 데이터 필드를 private 멤버로 선언하며 이를 어기는 일이 없다.
      • 필자는 클래스 안에서는 항상 메서드, 속성, 이벤트까지 모두 private으로 선언하고 비가상 멤버로 선언하는 것을 선호한다.
      • 어떤 알고리즘을 구현할 때 지나치게 복잡해지는 경향이 보인다면, 도우미 타입을 만들어서 각각의 기능들을 여러 조각으로 나누고 이를 캡슐화하려고 시도한다.
    • 타입에 대한 버전 관리와 가상 메서드 사이의 조율
        • 객체 지향 프로그래밍(Object Oriented Programming, OOP)은 정말 많은 세월 동안 논의되어온 개념이다.
        • 1970년대 후반에서 1980년대 초반까지 객체 지향 프로그래밍을 처음 도입할 떄에는 응용프로그램의 크기는 지금보다 더 작았고 응용프로그램의 제작을 위하여 코드를 작성하는 개발자들은 모두 한 회사 소속이었다.
        • 오늘날 소프트웨어는 더욱 더 복잡해지고 있고 사용자들은 응용프로그램이 제공하는 그래픽 사용자 인터페이스(Graphic User Interface, GUI)와 메뉴 항목, 마우스 입력, 태블릿 입력, 프린터 출력, 네트워킹 등 수많은 기능들에 의존하고 있다. 이러한 이유로, 우리가 사용하는 운영체제와 개발 플랫폼들은 지난 수년간 꾸준히 발전해왔다. 더 나아가서 요즘은 응용프로그램 개발자가 사용자들이 원하는 모든 기능을 모두 구현할 수 없으며 비용 효율적인 방법이 존재하는 것도 아니다. 요즘의 응용프로그램들은 여러 다른 회사들이 생산한 코드로 구성되어 있다. 이 코드는 객체 지향 패러다임을 사용하여 긴밀하게 통합되어 있다.
        • 컴포넌트 소프트웨어 프로그래밍(Component SOftware Programming, CSP)은 OOP를 이 수준까지 끌어올렸다. 다음은 컴포넌트로서 가져야 할 성격들을 열거한 것이다.
          • 하나의 컴포넌트(.NET Framework에서는 하나의 어셈블리)는 "출발(Published)"된 것으로 간주한다.
          • 하나의 컴포넌트에는 식별할 수 있는 아이덴티티(이름, 버전, 문화권, 그리고 공개 키)가 존재한다.
          • 하나의 컴포넌트에는 영원히 유지되는 고유한 아이덴티티가 존재한다. 어셈블리 안에 들어 있는 코드는 다른 어셈블리에 정적으로 링크되는 일이 없으며, .NET 환경에서는 항상 동적 링크만 발생한다.
          • 하나의 컴포넌트는 의지하는 다른 컴포넌트들에 대해 정확한 정보를 표시한다. (참조 메타데이터 테이블)
          • 하나의 컴포넌트는 반드시 그 안에 들어있는 클래스와 멤버들에 대해 문서를 만들 수 있어야 한다. C#에서는 소스 코드 내부에 XML 주석을 추가하여 컴파일러의 /doc 명령 줄 스위치를 사용하여 이러한 목적을 성취할 수 있다.
          • 하나의 컴포넌트는 반드시 필요로 하는 권한에 대해 자세히 서술하고 있어야 한다. CLR의 코드 엑세스 보안(CAS) 기능을 통해 이를 달성할 수 있다.
          • 하나의 컴포넌트는 새로운 서비스를 제공하더라도 변하지 않는 인터페이스나 객체 모델을 제공해야 한다. 서비스 제공이라 함은 원래의 컴포넌트 버전과 호환성을 유지할 수 있는 새로운 버전의 컴포넌트를 제공하는 것이다. 일반적으로, 서비스 제공에는 버그 수정, 보안 패치, 그리고 약간의 기능 향상이 포함된다. 그러나 서비스 제공을 통해서는 새로운 종속성이나 추가적인 보안 권한을 요구할 수는 없다.
        • 컴포넌트 소프트웨어 프로그래밍에서의 가장 큰 부분은 버전 관리에 대한 것이다.
        • 컴포넌트는 지속적으로 변경될 것이고, 매번 다른 시간 스케줄에 따라 새롭게 배포될 것이다.
        • 버전 관리를 통하여 이전의 객체 지향 프로그래밍만으로는 달성할 수 없었던 완전히 새로운 차원의 컴포넌트 소프트웨어 프로그래밍을 단일 회사에 의하여 작성되고 테스트되고, 출시된 모든 개별 유닛에 적용할 수 있을 것이다.
        • .NET Framework에서는 버전 번호가 네 개의 파트로 구분되는데, 메이저 파트, 마이너 파트, 빌드 번호 파트, 리비전 번호 파트로 구성되어 있다
        • 메이저와 마이너 파트는 보통 어셈블리의 기능상 큰 특징을 설명하기 위한 목적으로 부여되는 것이고, 빌드 번호와 리비전 번호 파트는 어셈블리의 기능 집합 중 일부를 업데이트하는 서비스 제공을 목적으로 부여되는 것이다.컴포넌트 소프트웨어 프로그래밍 환경에서 버전 관리는 매우 중요한 문제이다.
          •