LOGIN • JOININ

extreme Extensibility

1... 개방된 구조

일반적으로 엔진의 사용을 위해  API형태의 함수나 클래스를 제공합니다.

Engine_general.png


하지만 많은 엔진들은 닫힌 구조를 가지며 API 혹은 클래스들은 단지 엔진의 기능을 사용하는 용도로만 제공집니다.  즉 엔진 내부는 블랙박스란 의미입니다.
이런 형태의 엔진은 엔진 내부가 노출되지 않기 때문에 엔진 소스나 기술 보호에 용이하지만 태생적으로 확장성의 한계를 가질 수 밖에 없습니다. 

또 특정 구조 혹은 장르에 특화해 엔진을 제작하면 그범용성을 확보하기는 힘듭니다.
 특정 형태에 특화된 서버엔진인 경우 최근 개발되는 많은 서버들은 기능이 다양해 짐에 따라 매우 다양한 형태를 복합적으로 사용하는 경우가 많은데 엔진이 제공하는 형태에서 조금난 벗어나도 구현이 오히려 더 힘들고 복잡해지는 경우가 많습니다.
 반대로 특화하지 않고 공급할 경우 기본적인 기능만 제공해 주어 제작하는데 많은 노력이 듦은 물론 서버 개발의 구조나  설계의 노하우 혹은 방향 등에 대해서는 엔진으로부터 도움을 받지 못하기 때문에 사실상 생산성에는 큰 도움이 되지 못하는 경우가 많습니다.
즉, 범용성과 편의성 둘중 하나를 포기해야만 한다는 의미입니다.

하지만 CGCII는 개방적 구조를 가지고 있으며 최하층부부터 최상층까지 모든 구조를 추상화해  인터페이스를 제공해 줌으로써 확장성과 범용성 그리고 사용상의 편리함을 모두 제공해 줄 수 있도록 설계되었습니다.

이를 위해서는 최종 사용자 인터페이스 뿐만 아니라 내부구조까지 모두 체계적으로 설계되어야만 가능합니다.


Engine_CGCII.png


추상화 클래스들(Abstracted Classes)
서버의 주요 동작들을 추상화하여 정의된 추상화 클래스입니다.
이 레이어의 인터페이스 클래스들은 단순히 최종 사용만을 위한 것들이 아니라 서버 동작이나 구성요소를 재정의하거나 변경 혹은 교체할 수 있도록 설계되어 있습니다.
또 이 추상화된 클래스 수준에서 전체 서버의 구조를 설계할 수 있습니다.


컴포넌트 (Components)
추상화된 클래스를 상속받아 구현된 컴포넌트 클래스입니다.
필요에 따라 컴포넌트 클래스들을 조합해 구현 클래스들을 정의할 수 있습니다.


구현 클래스 (Implemented Classes) 
객체화할 수 있는 클래스들로 일반적으로 컴포넌트 클래스들의 조합으로 구현 됩니다. 
또 이 레이어는 사용자의 프로그래밍적인 편리성에 가장 중점이 되어 설계됩니다.
사용자가 정의한 클래스를 최대한 자유롭게 사용할수 있도록 하기 위해 템플릿(Template) 으로  제공되는 경우가 많습니다.


특화된 시스템 (Specialized System)
기반 엔진을 기반으로 구체인 상황이나 형태에 맞게 구현한 것입니다.
예를들어 로그인처리, 채팅방 등등과 같은 단순 기능적 시스템에서 하나의 채탕 서버와 같은 전체를 구성하는 시스템 등을 구현하여 제공하하는 것을 말합니다.
굳이 복잡하게 내부 시스템을 이해할 필요 없이 간단한 함수나 인터페이스 클래스를 사용 하는 것만으로 시스템을 구성하거나 붙일 수 있도록 해줍니다.

이러한 구조의 차이는 실제 개발 과정에서 엄청난 생산성과 구조의 간결성 그리고 유연성의 차이 그리고 개발 도중 발생하는 다양한 변화에 빠르게 적응할 수 있도록 해줍니다.
또 구현해야할 기능들이 많아져도 비교적 그 복잡함의 가중을 최소화할 수 있도록 해줍니다.
그리고 궁극적으로 안정적인 서버의 개발과 유지보수와  직결됩니다.
구조의 간결함이란 개발의 편의성의 문제 뿐만 아니라  코드의 명확성을 제공해  프로그래머의 오류를 최대한 줄여주는 중요한 요소이기도 합니다.


예제나 실제 구현을 통해 그 차이를 확인해 보시기 바랍니다.



2... 개방된 추상화 설계란? 

개방적 구조와 추상화된 설계가 어떻게 확장성을 가질 수 있는지 Socket의 추상화를 예로  들어 설명해 보도록 하겠습니다.

CGCII에서 TCP 소켓을 사용하려면  
앞에서 보여진 예제대로 아래와 같이 사용했습니다.


class CSocket : public CGNet::Socket::CTCP<>
{
        virtual void OnConnect() override;
        virtual void OnDisconnect(uint32_t _Reason) override;
        ...
};


즉 'CGNet::Socket::CTCP<>'를 상속받아 CSocket을 정의했고

OnConnect(), OnDisconnect() 등과 같은 필요한 '가로채기(Hook)함수'들을 재정의해 사용을 했습니다.

사용하기도 상당히 간단하고 직관적입니다.


근데 이런 클래스 상속 구조는 너무나 흔히 보이는 소켓 설계일 뿐이죠.

하지만 여기서  상속받은 'CGNet::Socket::CTCP<>'를 한번 더 열어 보면 CGCII가 말하는 확장성 있는 추상화 설계에 대해서 알 수 있을 것입니다. 


아래는 'CGNet::Socket::CTCP<>' 클래스의 정의입니다.

namespace CGNet
{
namespace Socket
{

template <class THEAD=CGNet::IO::Packetable::PRIMITIVE<uint32_t>>
class CTCP :
	public	CGNet::IO::Socket::NTCP,				// (@) Socket TCP
	public	CGNet::IO::Connectable::NTCP,			// (@) Connectable TCP
	public	CGNet::IO::Sendable::NTCP,				// (@) Sendable TCP
	public	CGNet::IO::SendRequest::NBase,			// (@) SendRequest Base
	public	CGNet::IO::Sender::NStreamGather,		// (@) Sender Stream (Gather Version)
	public	CGNet::IO::Receivable::NStream,			// (@) Receivable Stream
	public	CGNet::IO::Packetable::NStream<THEAD>	  // (@) Packetable Stream
	public	CGNet::IO::Messageable::NBase			// (@) Messageable
{
};

}
}

이렇게 정의되어 있습니다.

여러 개의 클래스를 상속받았을 뿐 클래스에는 아무런 내용도 존재하지 않습니다.

즉 'CGNet::Socket::CTCP<>'클래스는 여러 구현된 컴포넌트 클래스들을 다중 상속받아 조합한 클래스에 불과하다는 것을 볼수 있으실 것입니다.


CGCII의 소켓은 송신 3+1단계, 수신 3단계, 접속처리 1단계 그리고 소켓핸들 1개로 구성되어 있습니다.

CGCII_AbstractedSockt.png

수신과정은 IReceivable - IPacketable - Messageable의 3단계로 정의되어 있고

송신과정은 ISendable - ISendRequest - ISender의 3단계와 옵션인 'ISendComplete'로 정의되어 있습니다.

접속처리는 IConnectable 하나로 정의되어 있습니다. 


이 과정 중 수신과정을 좀더 자세히 설명해 보도록 하겠습니다.


CGCII_Socket_Receive_Process.png

'IReceivable 단계'는  실제 저수준 Socket I/O API를 사용해 메시지를 받아오는 처리를 해주는 레이어입니다.

이 레이어는 O/S나 플랫폼 그리고 I/O 방법에 따라 다양한 구현이 가능할 것입니다.

이 IReceivable이 데이터를 수신하면 그 수신한 데이터를 IPacketable로 전달해 줍니다.

'IPacketable 단계'는 전달되어진 데이터를 읽어 메시지 단위로 자르는 처리를 수행합니다. 
이 역시 다양한 형태의 메시지 처리 단위가 있기 때문에 다양한 구현이 있을 수 있습니다.

이렇게 IPacketable에서 나뉘어진 메시지는 ICGMessageable로 전달어집니다.

'IMessageable 단계'에서는 해당하는 메시지에 맞는 처리를 해주게 됩니다.


이렇게 3단계의 수신 단계마다 다양한 구현이 있을 수 있습니다. 

예를 들어 IReceivable의 경우 IOCP를 사용한 것도 있을 수 있고 EvnetSelect를 사용한 것도 있을 수 있으며 ePoll을 사용한 구현도 있을 수 있습니다.

IPacketable의 경우도 다양할 수 있습니다. 바이너리 메시지 헤더로 메시지를 구분해 내는 방법도 있을 수 있고 스트링 형태에 터미널 문자로 메시지를 구분해 내는 것도 있을 수 있습니다. 

메시지처리하는 부분인 ICGMessageble은 어플리케이션 마다 다 다르기 때문에 어플리케이션 마다 다르게 구현을 할 것입니다.

위의 'CGNet::Socket::CTCP<>' 클래스는 이렇게 많이 사용되는 소켓의 형태에 따라 미리 구현된 컴포넌트들을 조합해 놓은 클래스라고 할 수 있습니다. 

특정 레이어에 다른 기능을 원한다면 얼마든지 해당레이어만 다른 것으로 교체 상속해 원하는 소켓을 구성해 낼수 있습니다.

 또 독자적인 처리 방법이 필요하다면 해당 레이어의 인터페이스만 상속받아 구현한 후에 그것을 교체해서 상속함으로써 간단하게 기능을 교체할 수 있다는 것입니다.


CGCII의 많은 기능들은 이런 식으로 추상화되어 설계되었고 이 추상화된 클래스들을 제공해 줌으로써 언젠든 원하는 기능들로 변경하거나 재정의하거나 추가할 수 있습니다.

즉 편리함은 편리함대로 확장성은 확장성 대로 제공한다는 것입니다.