COM 이란 무엇인가
COM(Component Object Model) 이란?
COM 은 상호작용이 가능한 binary software component 를 만들기 위한 시스템이며 아래의 특징을 가진다.
- 플랫폼에 독립적인 시스템이다.
- 분산된 시스템이다.
- 객체 지향 시스템이다.
COM 은 마이크로소프트의 OLE(Object Linking and Embedding)1 와 ActiveX2 의 기반이 되는 기술이다.
COM 과 COM 기반 기술을 이해하기 위해서 COM 이 객체지향언어가 아닌 표준(Standard) 이라는 것을 이해하는 것이 중요하다. COM 은 어플리케이션의 구조가 어떤식으로 되어야한다 라고 지정하지 않는다. 언어, 구조, 구현에 대한 자세한 내용은 개발자에게 달려있다.
오히려 COM 은 COM objects(COM component 혹은 그냥 objects 라고도 부름)가 다른 objects 들과 상호작용할 수 있도록 object model 이나 프로그래밍적인 요구사항을 지정한다. 이 objects 들은 single process, other processes, 또는 remote computers 에도 있을 수 있다.
COM objects 들은 다른 프로그래밍 언어로 작성될 수도 있고, 구조적으로도 다른 부분이 있을수도 있다. 이런 점이 COM 이 binary standard(프로그램이 binary machine code 로 바뀐 뒤 적용되는 표준) 라고 불리는 이유이다.
COM 에 대해 프로그래밍 언어적으로 딱 하나 요구되는 것은 프로그래밍 언어 중 pointer 구조를 생성할 수 있고 pointer 를 통해 explicitly 혹은 implicitly 하게 function 을 호출할 수 있는 언어를 사용해 코드를 작성해야 한다는 것이다. C++ 나 Smalltalk 같은 객체지향 언어들은 COM objects 의 구현을 편하게 할 수 있도록 프로그래밍 메카니즘을 제공해준다. 반면 C, Java, VBScript 같은 언어들은 COM objects 를 만들거나 사용할 수 있다.
COM 은 COM objects 의 필수 특성을 정의한다. 보편적으로 software object 는 데이터들(set of data)과 그 데이터를 조작할 수 있는 function 들로 만들어진다. COM object 는 object 의 데이터에 접근하기위해 하나 이상의 관련 function 들을 통해야만 하는 부류이다. 이 관련 function 의 모음을 interfaces 라고 부르며, interface 의 function 들을 methods 라고 부른다. 또 COM 은 methods 에 접근할 때 interface 에 대한 pointer 를 통해서만 접근하게 하라고 요구한다.
COM 은 기본적인 binary object standard 를 지정하는 것 외에도 특정 기본 interfaces 를 정의해서 모든 COM 기반 기술에 공통적으로 사용되는 function 을 제공하고 있으며, 모든 component 에서 사용되는 몇가지 function 들을 제공한다. 또 COM 은 분산된 환경에서 objects 들이 어떻게 같이 동작할 수 있는지를 정의하며, 시스템과 component 의 무결성을 위한 보안 기능들도 추가되어있다.
COM Technical Overview
마이크로소프트의 COM 은 binary interoperability3 standard 을 정의하는데, 이는 런타임에 상호작용하는 reusable software libraries 를 만들기 위한 표준이다. 다시말해 COM libraries 들을 우리 어플리케이션에 컴파일할 필요없이 사용이 가능하다. COM은 Windows Media Player 와 Windows Server 같은 마이크로소프트 제품과 기술들의 기반이다.
COM 은 많은 OS 와 하드웨어 플랫폼들에 적용되는 binary standard 를 정의한다. 네트워크 컴퓨팅에서는 standard wire format 과 다른 하드웨어 플랫폼에서 동작하는 object 간의 상호작용 프로토콜을 정의한다. COM 은 구현에 사용된 언어에 독립적이다. 다시말해 C++ 나 .NET Framework 의 다양한 언어들을 사용해 COM libraries 를 개발할 수 있다.
COM specification(사양) 은 cross-platform software reuse 를 위한 기본 개념들을 아래와 같이 제공한다.
- component 간의 functions 호출을 위한 binary standard
- interface 내부 functions 의 strongly-typed groupings 에 대한 규정
- polymorphism, feature discovery, object lifetime tracking 을 제공하는 기본 인터페이스
- component 들과, component 들의 interfaces 를 고유 식별하는 메커니즘
- component loader - deployment 에서 component instance 들을 생성해준다
아래와 같이 COM 이 가지고있는 다양한 파츠들을 같이 사용해 reusable components 들로 이루어진 어플리케이션을 만들수 있다.
- COM specification 에 따른 런타임 환경을 제공하는 host system
- feature contracts4 를 정의하는 interface, interface 를 구현하는 component
- 시스템에 components 를 제공하는 servers, components 가 제공하는 기능을 사용하는 clients
- local host, remote host 에서 components 가 deploy 된 위치를 기록하는 registry
- local host, remote host 에서 components 의 위치를 찾아내고, servers 를 clients 에 연결하는 Service Control Manager
- host 의 파일 시스템 안 파일 내용을 탐색하는 방법을 정의하는 structured storage protocol5
다양한 hosts 와 platforms(소프트웨어가 실행되는 HW + OS 조합 - ex. Windows 플랫폼, Linux 플랫폼) 전반에 걸쳐 코드의 재사용을 가능하게 만드는 것이 COM 의 핵심이다. 재사용 가능한 interface 구현체를 component, component object, COM object 라고 부른다. 이 component 는 하나 이상의 interface 를 구현한다.
개발자는 자신의 library 가 구현하는 interface 를 디자인해서 custom COM library 를 만들 수 있다. 이렇게 만들어진 custom COM library 의 사용자는 library 의 deployment(SW, library 를 사용가능하게 시스템에 설치, 구성하는것) 나 자세한 구현에 대해 알 필요 없이 기능을 찾아서 사용할 수 있다.
Objects and Interfaces
COM object 의 기능은 interface(member function = method 의 모음) 를 통해서 드러난다. COM interface 는 component 의 예상 동작과 역할을 정의하는데, 이는 strongly-typed contract 에 맞게 작성된 관련된 작업(method)들의 모음으로서 작성한다.
잠깐 정리하기(COM interface, COM object, COM component)
위에서 설명한대로 COM interface 는 strongly-typed contract 를 준수하며 특정 역할에 관련된 메서드를 정의하고, COM object 는 이러한 interface 를 실제로 구현한다. COM component 는 필요한 여러 기능의 COM object 들을 포함하며, COM 의 기본 원칙을 따르고 interoperability(상호 운용성)을 가지기 때문에 다른 소프트웨어에서 사용할 수 있는 독립적인 모듈(보통 파일 단위) 또는 라이브러리(여러 모듈 포함가능)이다.
일반적으로 COM component 는 DLL, EXE 같은 형태로 존재하며, 다양한 프로그래밍 언어(C++, C#, VB 등)로 개발될 수 있다.이러한 구조의 간단한 예시를 들어보자. 만약 파일시스템에서 json 파일을 읽어온 후 데이터를 XML 형식으로 변환하여 다시 저장하는 COM component 를 만들고 싶다.
이러한 경우, 파일 조작에 관련된 interface 와 데이터 처리에 관련된 interface 를 각각 작성할 수 있다. 각 interface 는 object 로서 실제 기능이 구현되고, 구현된 object 들을 포함하는 component 가 만들어지는 것이다.
COM component 간의 모든 상호작용은 interface 를 통해 이루어지며, component 에서 제공하는 모든 기능들은 해당 기능이 정의된 interface 를 통해서 나온것이다. 사용자는 interface 의 member function(=method) 만 사용할 수 있으며, 더 자세한 내부 내용의 조작은 interface 에서 제공하지 않는 이상 불가능하다.
interface 는 strongly typed 되어있다. 모든 interface 는 각자 자신의 unique identifier(고유 식별자) 인 IID(Interface Identifier) 를 가지고있어서 사람들이 직접 이름을 지을 때 발생할 수 있는 충돌을 방지한다. IID 는 GUID(Globally Unique Identifier), UUID(Universally Unique Identifier)6 라고도 불린다. 실제로 우리가 새로운 interface 를 만들 때 새로운 identifier 도 같이 만들어야한다. 사용자가 interface 를 사용시에 반드시 unique identifier 를 사용해야 한다. 이런식으로 명시적인 식별자를 사용하면 runtime 에 발생할 수 있는 에러인 naming conflicts 를 방지하여 강건성(robustness)을 향상시킬 수 있다.
새로운 interface 를 만들 때, IDL(interface definition language) 을 사용하여 interface definition(interface 를 정의하는 코드) 을 작성할 수 있다. 작성된 interface definition 를 보고 Microsoft IDL compiler 가 header file(interface 를 사용하는 application 들을 위한 파일) 과 remote procedure calls7 를 다루기 위한 소스코드를 만들어낸다.
이 과정에 대한 간단한 예시를 ChatGPT 로 만들어보았다.
1. 새로운 인터페이스 정의 (IDL):
새로운 기능을 제공하는 인터페이스를 IDL로 정의한다.
1 2 3 interface MyInterface { HRESULT DoSomething([in] int param1, [in, out] BSTR param2, [out, retval] int* result); };2. IDL 컴파일러 실행:
IDL 파일을 Microsoft IDL 컴파일러에 제공하여 헤더 파일과 소스 코드를 생성한다.
1 midl MyInterface.idl3. 헤더 파일 및 소스 코드 사용 (C++ 응용 프로그램):
응용 프로그래머는 생성된 MyInterface.h 헤더 파일을 사용하여 새로운 인터페이스를 C++ 언어로 구현한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include "MyInterface.h" int main() { // 서버에 연결하는 코드 등을 작성 // MyInterface 인터페이스 사용 MyInterfacePtr pMyInterface; HRESULT hr = pMyInterface.CreateInstance(__uuidof(MyInterface)); if (SUCCEEDED(hr)) { // 원격 서버에 대한 호출 int result; BSTR param2 = L"Hello"; pMyInterface->DoSomething(42, ¶m2, &result); // 결과 처리 등을 작성 } // 서버 연결 해제 등을 작성 return 0; }응용 프로그램은 MyInterface 인터페이스를 사용하여 원격 서버의 DoSomething 함수를 호출하고, 결과를 처리한다. 일반적으로 RPC 에서 서버는 계속해서 구동되어 있고, 클라이언트는 필요할 때 원하는 RPC 호출을 수행한다.
마이크로소프트에서 제공하는 IDL 은 DCE IDL 의 간단한 확장형이고, 여기서 DCE IDL 은 RPC 기반 분산 컴퓨팅(DCE; Distributed Computing Environment) 의 산업 표준을 뜻한다. IDL 은 interface 를 간편하게 디자인하기위한 도구일 뿐, COM interoperability 에는 중요한 역할을 하지 않는다. IDL 을 사용하면 각각의 프로그래밍 환경(C, C++ 등. C# 에는 헤더 파일 개념이 없고 클래스, 인터페이스, 다른 코드들이 들어간 소스 코드 파일로 구성된다.)에 맞춰서 일일이 header files 를 만들어낼 필요없이 IDL 컴파일러로 생성이 가능하다. 자세한 내용은 도큐먼트(Defining COM Interfaces) 를 참조하자.
COM interfaces 에서도 드물게 상속(Inheritance)이 사용된다. COM 에서의 interface 상속은 오직 base interface 와 관련된 contract(interface 가 제공하는 method 의 집합) 를 재사용하기 위해서만 사용된다. COM 은 selective inheritance(선택적 상속)8 을 지원하지 않는다. 따라서 한 인터페이스가 다른 인터페이스를 상속받는다면, 다른 인터페이스에 정의된 모든 메서드들을 포함하게 된다. 또한 COM 에서 base interface 의 메서드들을 상속받기 위해서 오직 single inheritance 만 사용한다.(multiple inheritance 는 불가)
참조
- https://learn.microsoft.com/en-us/windows/win32/com/the-component-object-model
- https://learn.microsoft.com/en-us/windows/win32/com/com-technical-overview
마이크로소프트에서 개발한 윈도우의 각종 응용 프로그램 사이에서 서로 데이터를 공유할 수 있는 기능.
- 위키백과 ↩마이크로소프트에서 개발한 객체지향적 소프트웨어 구성요소 개발에 사용되는 기술이다. COM 과 OLE 를 결합한 기술로 WWW 에서 다운로드한 컨텐츠들을 이용하는데 사용된다.
- 위키백과 ↩binary interoperability(≒ compatibility). 어떤 컴퓨터 시스템(machine)에서 만든 소프트웨어의 executable code(대부분 CPU를 조작하는 machine code: 2진 컴퓨터가 지배적인 현대 시장에서 machine code 는 컴퓨터가 프로그램을 실제로 읽고 해석하기 위한 2진 표현법이다) 를 바꾸거나 recompile 하지 않고도 다른 시스템에서 실행할 수 있는 능력을 말한다. 예를들어 레노버의 Thinkpad 노트북과 삼성 데스크탑이 binary compatibility 를 가진다면, 서로 다른 브랜드의 processors, motherboards, memory, graphics hardware, disk drives 를 가진다 해도 똑같이 실행할 수 있다.
- 위키백과, computerworld.com ↩소프트웨어 개발에서 특정 기능이나 서비스에 대한 규격이나 인터페이스. 특정 기능이나 서비스가 어떻게 동작해야 하는지, 어떤 메서드나 속성을 제공해야 하는지 등을 정의한다. 예를 들어, 여러 component 가 데이터베이스에 접근하는데 사용되는 feature contract 로서 데이터 CRUD 메서드, 연결 설정 메서드 등이 포함될 수 있다.
- ChatGPT(틀릴수도있음) ↩파일 시스템이나 데이터 저장 공간에 구조적으로 데이터를 저장하고 접근하기 위한 규약이나 프로토콜. 데이터를 계층 구조 또는 특정 형식으로 저장하여 효율적으로 관리하고 검색할 수 있게 하는 방법이다. 일반적으로 파일의 메타데이터, 구조, 및 데이터 간의 상호 작용을 정의하며, 복잡한 데이터 구조를 효과적으로 저장하고 검색할 수 있도록 지원한다. 여러 응용 프로그램이 동일한 데이터에 접근하고 조작할 수 있도록 하며, 특히 데이터베이스나 문서 저장소와 같은 시스템에서 활용될 수 있다.
- ChatGPT(틀릴수도있음) ↩Open Software Foundation (OSF) Distributed Computing Environment (DCE) 에서 정의함 ↩
RPC 라고 불리며, 별도의 원격 제어를 위한 코딩 없이 컴퓨터 네트워크를 통해 다른 주소 공간(다른 컴퓨터)에서 프로시저(함수, 메서드 등)를 실행할 수 있게하는 프로세스 간 통신 기술이다. 보통 rpc 요청을 받는 서버가 띄워져있고, 클라이언트로부터 call 을 받으면 프로시저를 실행한다. ↩
특정 interface 를 상속받을 때 원하는 method 만 선택적으로 상속할 수 있는 기능. TypeScript 에서도 사용 가능하다. 간단하게 계산기 인터페이스의 선택적 상속을 구현한다고 하면
interface IAdvancedCalculator extends Partial<ICalculator>
이런식으로 코드를 작성한다. ↩